From 3dd0a7d6eba173e5169b66fd4417025916aa0f3e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 25 Oct 2020 20:41:28 +0900 Subject: [PATCH 01/46] Do not call `unwrap` with `signatures` option enabled --- compiler/rustc_save_analysis/src/sig.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 747e198cd9324..446a0723d8c35 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -21,7 +21,7 @@ // references. // // Signatures do not include visibility info. I'm not sure if this is a feature -// or an ommission (FIXME). +// or an omission (FIXME). // // FIXME where clauses need implementing, defs/refs in generics are mostly missing. @@ -677,7 +677,7 @@ impl<'hir> Sig for hir::Variant<'hir> { let mut text = self.ident.to_string(); match self.data { hir::VariantData::Struct(fields, r) => { - let id = parent_id.unwrap(); + let id = parent_id.ok_or("Missing id for Variant's parent")?; let name_def = SigElement { id: id_from_hir_id(id, scx), start: offset, From 459dae94a1c5ea658e78dbe99d88108d9cacc51d Mon Sep 17 00:00:00 2001 From: mark Date: Sat, 7 Nov 2020 15:22:52 -0600 Subject: [PATCH 02/46] fix #72680 by explicitly checking for or-pattern before test --- .../rustc_mir_build/src/build/matches/test.rs | 10 +++ src/test/ui/match/issue-72680.rs | 65 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/test/ui/match/issue-72680.rs diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 7bea8220ada74..e4bc4c0743243 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -671,6 +671,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (&TestKind::Range { .. }, _) => None, (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { + // We do call `test()` below to see what kind of test `match_pair` would require. + // If it is the same test as `test`, then we can just use `test`. + // + // However, `test()` assumes that there won't be any or-patterns, so we need to + // specially handle that here and return `None` (since the `test` clearly doesn't + // apply to an or-pattern). + if let PatKind::Or { .. } = &*match_pair.pattern.kind { + return None; + } + // These are all binary tests. // // FIXME(#29623) we can be more clever here diff --git a/src/test/ui/match/issue-72680.rs b/src/test/ui/match/issue-72680.rs new file mode 100644 index 0000000000000..3e8febb63add3 --- /dev/null +++ b/src/test/ui/match/issue-72680.rs @@ -0,0 +1,65 @@ +// run-pass + +#![feature(or_patterns)] + +fn main() { + assert_eq!(f("", 0), true); + assert_eq!(f("a", 1), true); + assert_eq!(f("b", 1), true); + + assert_eq!(f("", 1), false); + assert_eq!(f("a", 0), false); + assert_eq!(f("b", 0), false); + + assert_eq!(f("asdf", 032), false); + + //// + + assert_eq!(g(true, true, true), false); + assert_eq!(g(false, true, true), false); + assert_eq!(g(true, false, true), false); + assert_eq!(g(false, false, true), false); + assert_eq!(g(true, true, false), false); + + assert_eq!(g(false, true, false), true); + assert_eq!(g(true, false, false), true); + assert_eq!(g(false, false, false), true); + + //// + + assert_eq!(h(true, true, true), false); + assert_eq!(h(false, true, true), false); + assert_eq!(h(true, false, true), false); + assert_eq!(h(false, false, true), false); + assert_eq!(h(true, true, false), false); + + assert_eq!(h(false, true, false), true); + assert_eq!(h(true, false, false), true); + assert_eq!(h(false, false, false), true); +} + +fn f(s: &str, num: usize) -> bool { + match (s, num) { + ("", 0) | ("a" | "b", 1) => true, + + _ => false, + } +} + +fn g(x: bool, y: bool, z: bool) -> bool { + match (x, y, x, z) { + (true | false, false, true, false) => true, + (false, true | false, true | false, false) => true, + (true | false, true | false, true | false, true) => false, + (true, true | false, true | false, false) => false, + } +} + +fn h(x: bool, y: bool, z: bool) -> bool { + match (x, (y, (x, (z,)))) { + (true | false, (false, (true, (false,)))) => true, + (false, (true | false, (true | false, (false,)))) => true, + (true | false, (true | false, (true | false, (true,)))) => false, + (true, (true | false, (true | false, (false,)))) => false, + } +} From 43e4783ce3d833af82d76f6be12f3f4863b0638c Mon Sep 17 00:00:00 2001 From: mark Date: Mon, 9 Nov 2020 12:19:34 -0600 Subject: [PATCH 03/46] address reviewer comments --- .../rustc_mir_build/src/build/matches/test.rs | 11 +++-- src/test/ui/match/issue-72680.rs | 46 +++++++++---------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index e4bc4c0743243..07173f41cd6db 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -671,12 +671,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (&TestKind::Range { .. }, _) => None, (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => { - // We do call `test()` below to see what kind of test `match_pair` would require. - // If it is the same test as `test`, then we can just use `test`. + // The call to `self.test(&match_pair)` below is not actually used to generate any + // MIR. Instead, we just want to compare with `test` (the parameter of the method) + // to see if it is the same. // - // However, `test()` assumes that there won't be any or-patterns, so we need to - // specially handle that here and return `None` (since the `test` clearly doesn't - // apply to an or-pattern). + // However, at this point we can still encounter or-patterns that were extracted + // from previous calls to `sort_candidate`, so we need to manually address that + // case to avoid panicking in `self.test()`. if let PatKind::Or { .. } = &*match_pair.pattern.kind { return None; } diff --git a/src/test/ui/match/issue-72680.rs b/src/test/ui/match/issue-72680.rs index 3e8febb63add3..e6a723a6609f8 100644 --- a/src/test/ui/match/issue-72680.rs +++ b/src/test/ui/match/issue-72680.rs @@ -3,39 +3,39 @@ #![feature(or_patterns)] fn main() { - assert_eq!(f("", 0), true); - assert_eq!(f("a", 1), true); - assert_eq!(f("b", 1), true); + assert!(f("", 0)); + assert!(f("a", 1)); + assert!(f("b", 1)); - assert_eq!(f("", 1), false); - assert_eq!(f("a", 0), false); - assert_eq!(f("b", 0), false); + assert!(!f("", 1)); + assert!(!f("a", 0)); + assert!(!f("b", 0)); - assert_eq!(f("asdf", 032), false); + assert!(!f("asdf", 032)); //// - assert_eq!(g(true, true, true), false); - assert_eq!(g(false, true, true), false); - assert_eq!(g(true, false, true), false); - assert_eq!(g(false, false, true), false); - assert_eq!(g(true, true, false), false); + assert!(!g(true, true, true)); + assert!(!g(false, true, true)); + assert!(!g(true, false, true)); + assert!(!g(false, false, true)); + assert!(!g(true, true, false)); - assert_eq!(g(false, true, false), true); - assert_eq!(g(true, false, false), true); - assert_eq!(g(false, false, false), true); + assert!(g(false, true, false)); + assert!(g(true, false, false)); + assert!(g(false, false, false)); //// - assert_eq!(h(true, true, true), false); - assert_eq!(h(false, true, true), false); - assert_eq!(h(true, false, true), false); - assert_eq!(h(false, false, true), false); - assert_eq!(h(true, true, false), false); + assert!(!h(true, true, true)); + assert!(!h(false, true, true)); + assert!(!h(true, false, true)); + assert!(!h(false, false, true)); + assert!(!h(true, true, false)); - assert_eq!(h(false, true, false), true); - assert_eq!(h(true, false, false), true); - assert_eq!(h(false, false, false), true); + assert!(h(false, true, false)); + assert!(h(true, false, false)); + assert!(h(false, false, false)); } fn f(s: &str, num: usize) -> bool { From 659aa778d3305b2f20d4e0d2bbead4071e7f02b7 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Wed, 11 Nov 2020 23:40:11 +0800 Subject: [PATCH 04/46] test: add `()=()=()=...` to weird-exprs.rs --- src/test/ui/weird-exprs.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/ui/weird-exprs.rs b/src/test/ui/weird-exprs.rs index 916cabbfb8c94..9c69c834296e5 100644 --- a/src/test/ui/weird-exprs.rs +++ b/src/test/ui/weird-exprs.rs @@ -1,6 +1,7 @@ // run-pass #![feature(generators)] +#![feature(destructuring_assignment)] #![allow(non_camel_case_types)] #![allow(dead_code)] @@ -159,6 +160,11 @@ fn match_nested_if() { assert!(val); } +fn empty_tuple_assignment() { + let val = ()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=(); + assert_eq!(val, ()); +} + pub fn main() { strange(); funny(); @@ -177,4 +183,5 @@ pub fn main() { r#match(); i_yield(); match_nested_if(); + empty_tuple_assignment(); } From f5e67b5ee1d442ddfc580224c69f260a07b8840b Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Thu, 12 Nov 2020 01:05:27 +0100 Subject: [PATCH 05/46] Add a test for r# identifiers --- .../rustdoc/raw-ident-eliminate-r-hashtag.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs new file mode 100644 index 0000000000000..2221b5d47acf9 --- /dev/null +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -0,0 +1,17 @@ +pub mod internal { + pub struct r#mod; + + /// See [name], [other name] + /// + /// [name]: mod + /// [other name]: ../internal/struct.mod.html + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + pub struct B; +} + +/// See [name]. +/// +/// [name]: internal::mod +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +struct A; From 9c7069645cf919cb0a848175a208d6eb4d92802e Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Thu, 12 Nov 2020 01:39:06 +0100 Subject: [PATCH 06/46] Ignore tidy linelength --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 2221b5d47acf9..d758d9936132f 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + pub mod internal { pub struct r#mod; From bd0eb07af2deaff2c9f1e7553ea97341787779cd Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Mon, 2 Nov 2020 21:32:48 -0800 Subject: [PATCH 07/46] Added some unit tests as requested As discussed in PR #78267, for example: * https://github.com/rust-lang/rust/pull/78267#discussion_r515404722 * https://github.com/rust-lang/rust/pull/78267#discussion_r515405958 --- Cargo.lock | 8 + compiler/rustc_mir/Cargo.toml | 3 + .../src/transform/coverage/counters.rs | 4 +- .../rustc_mir/src/transform/coverage/debug.rs | 16 +- .../rustc_mir/src/transform/coverage/graph.rs | 16 +- .../rustc_mir/src/transform/coverage/mod.rs | 5 +- .../rustc_mir/src/transform/coverage/spans.rs | 6 +- .../transform/coverage/test_macros/Cargo.toml | 12 + .../transform/coverage/test_macros/src/lib.rs | 8 + .../rustc_mir/src/transform/coverage/tests.rs | 631 ++++++++++++++++++ 10 files changed, 687 insertions(+), 22 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml create mode 100644 compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 45a19fd79634e..4ab46a9cc871b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -721,6 +721,13 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" +[[package]] +name = "coverage_test_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", +] + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -3922,6 +3929,7 @@ dependencies = [ name = "rustc_mir" version = "0.0.0" dependencies = [ + "coverage_test_macros", "either", "itertools 0.9.0", "polonius-engine", diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index 487668cfa1109..9bfd1da039120 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -31,3 +31,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } rustc_apfloat = { path = "../rustc_apfloat" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } + +[dev-dependencies] +coverage_test_macros = { path = "src/transform/coverage/test_macros" } diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index d6c2f7f7aaf1d..7c4b30ca9e790 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::coverage::*; /// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR /// `Coverage` statements. -pub(crate) struct CoverageCounters { +pub(super) struct CoverageCounters { function_source_hash: u64, next_counter_id: u32, num_expressions: u32, @@ -37,7 +37,7 @@ impl CoverageCounters { self.debug_counters.enable(); } - /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlock`s directly or /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s /// representing intermediate values. pub fn make_bcb_counters( diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs index ffa795134e257..7f1dc3844b21d 100644 --- a/compiler/rustc_mir/src/transform/coverage/debug.rs +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -127,7 +127,7 @@ pub const NESTED_INDENT: &str = " "; const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; -pub(crate) fn debug_options<'a>() -> &'a DebugOptions { +pub(super) fn debug_options<'a>() -> &'a DebugOptions { static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) @@ -136,7 +136,7 @@ pub(crate) fn debug_options<'a>() -> &'a DebugOptions { /// Parses and maintains coverage-specific debug options captured from the environment variable /// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. #[derive(Debug, Clone)] -pub(crate) struct DebugOptions { +pub(super) struct DebugOptions { pub allow_unused_expressions: bool, counter_format: ExpressionFormat, } @@ -250,7 +250,7 @@ impl Default for ExpressionFormat { /// /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. -pub(crate) struct DebugCounters { +pub(super) struct DebugCounters { some_counters: Option>, } @@ -386,7 +386,7 @@ impl DebugCounter { /// If enabled, this data structure captures additional debugging information used when generating /// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. -pub(crate) struct GraphvizData { +pub(super) struct GraphvizData { some_bcb_to_coverage_spans_with_counters: Option>>, some_bcb_to_dependency_counters: Option>>, @@ -496,7 +496,7 @@ impl GraphvizData { /// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// and/or a `CoverageGraph` graphviz output). -pub(crate) struct UsedExpressions { +pub(super) struct UsedExpressions { some_used_expression_operands: Option>>, some_unused_expressions: @@ -626,7 +626,7 @@ impl UsedExpressions { } /// Generates the MIR pass `CoverageSpan`-specific spanview dump file. -pub(crate) fn dump_coverage_spanview( +pub(super) fn dump_coverage_spanview( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, basic_coverage_blocks: &CoverageGraph, @@ -666,7 +666,7 @@ fn span_viewables( } /// Generates the MIR pass coverage-specific graphviz dump file. -pub(crate) fn dump_coverage_graphviz( +pub(super) fn dump_coverage_graphviz( tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>, pass_name: &str, @@ -815,7 +815,7 @@ fn bcb_to_string_sections( /// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any /// values it might hold. -pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { +pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { match kind { TerminatorKind::Goto { .. } => "Goto", TerminatorKind::SwitchInt { .. } => "SwitchInt", diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs index c2ed2cbb10002..9d375633dcf51 100644 --- a/compiler/rustc_mir/src/transform/coverage/graph.rs +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -17,7 +17,8 @@ const ID_SEPARATOR: &str = ","; /// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional /// set of additional counters--if needed--to count incoming edges, if there are more than one. /// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) -pub(crate) struct CoverageGraph { +#[derive(Debug)] +pub(super) struct CoverageGraph { bcbs: IndexVec, bb_to_bcb: IndexVec>, pub successors: IndexVec>, @@ -275,7 +276,7 @@ impl graph::WithPredecessors for CoverageGraph { rustc_index::newtype_index! { /// A node in the [control-flow graph][CFG] of CoverageGraph. - pub(crate) struct BasicCoverageBlock { + pub(super) struct BasicCoverageBlock { DEBUG_FORMAT = "bcb{}", } } @@ -305,7 +306,7 @@ rustc_index::newtype_index! { /// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) /// significance. #[derive(Debug, Clone)] -pub(crate) struct BasicCoverageBlockData { +pub(super) struct BasicCoverageBlockData { pub basic_blocks: Vec, pub counter_kind: Option, edge_from_bcbs: Option>, @@ -431,7 +432,7 @@ impl BasicCoverageBlockData { /// the specific branching BCB, representing the edge between the two. The latter case /// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. #[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) struct BcbBranch { +pub(super) struct BcbBranch { pub edge_from_bcb: Option, pub target_bcb: BasicCoverageBlock, } @@ -498,9 +499,8 @@ fn bcb_filtered_successors<'a, 'tcx>( /// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the /// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that /// ensures a loop is completely traversed before processing Blocks after the end of the loop. -// FIXME(richkadel): Add unit tests for TraversalContext. #[derive(Debug)] -pub(crate) struct TraversalContext { +pub(super) struct TraversalContext { /// From one or more backedges returning to a loop header. pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, @@ -510,7 +510,7 @@ pub(crate) struct TraversalContext { pub worklist: Vec, } -pub(crate) struct TraverseCoverageGraphWithLoops { +pub(super) struct TraverseCoverageGraphWithLoops { pub backedges: IndexVec>, pub context_stack: Vec, visited: BitSet, @@ -642,7 +642,7 @@ impl TraverseCoverageGraphWithLoops { } } -fn find_loop_backedges( +pub(super) fn find_loop_backedges( basic_coverage_blocks: &CoverageGraph, ) -> IndexVec> { let num_bcbs = basic_coverage_blocks.num_nodes(); diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index c55349239b034..de37a67a1742f 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -5,6 +5,9 @@ mod debug; mod graph; mod spans; +#[cfg(test)] +mod tests; + use counters::CoverageCounters; use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; use spans::{CoverageSpan, CoverageSpans}; @@ -31,7 +34,7 @@ use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. #[derive(Debug)] -pub(crate) struct Error { +pub(self) struct Error { message: String, } diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index cda4fc125442f..f880d69bd64f6 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -17,7 +17,7 @@ use rustc_span::{BytePos, Span, SyntaxContext}; use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] -pub(crate) enum CoverageStatement { +pub(super) enum CoverageStatement { Statement(BasicBlock, Span, usize), Terminator(BasicBlock, Span), } @@ -66,7 +66,7 @@ impl CoverageStatement { /// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` /// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] -pub(crate) struct CoverageSpan { +pub(super) struct CoverageSpan { pub span: Span, pub bcb: BasicCoverageBlock, pub coverage_statements: Vec, @@ -214,7 +214,7 @@ pub struct CoverageSpans<'a, 'tcx> { } impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { - pub(crate) fn generate_coverage_spans( + pub(super) fn generate_coverage_spans( mir_body: &'a mir::Body<'tcx>, body_span: Span, basic_coverage_blocks: &'a CoverageGraph, diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml new file mode 100644 index 0000000000000..a9d6f0c803d2e --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["The Rust Project Developers"] +name = "coverage_test_macros" +version = "0.0.0" +edition = "2018" + +[lib] +proc-macro = true +doctest = false + +[dependencies] +proc-macro2 = "1" diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs new file mode 100644 index 0000000000000..ea551c7745556 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs @@ -0,0 +1,8 @@ +use proc_macro::TokenStream; + +#[proc_macro] +pub fn let_bcb(item: TokenStream) -> TokenStream { + format!("let bcb{} = graph::BasicCoverageBlock::from_usize({}); let _ = {};", item, item, item) + .parse() + .unwrap() +} diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs new file mode 100644 index 0000000000000..2231fe6427fa2 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -0,0 +1,631 @@ +use super::debug; +use super::graph; + +use coverage_test_macros::let_bcb; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::graph::WithSuccessors; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, TyS}; +use rustc_span::DUMMY_SP; + +use std::lazy::SyncOnceCell; + +fn dummy_ty<'tcx>() -> &'static TyS<'tcx> { + static DUMMY_TYS: SyncOnceCell> = SyncOnceCell::new(); + + &DUMMY_TYS.get_or_init(|| { + let fake_type_bytes = vec![0 as u8; std::mem::size_of::>()]; + unsafe { std::ptr::read_unaligned::>(fake_type_bytes.as_ptr() as *const TyS<'_>) } + }) +} + +struct MockBlocks<'tcx> { + blocks: IndexVec>, + source_info: SourceInfo, + dummy_place: Place<'tcx>, + next_local: usize, +} + +impl<'tcx> MockBlocks<'tcx> { + fn new() -> Self { + Self { + blocks: IndexVec::new(), + source_info: SourceInfo::outermost(DUMMY_SP), + dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() }, + next_local: 0, + } + } + + fn new_temp(&mut self) -> Local { + let index = self.next_local; + self.next_local += 1; + Local::new(index) + } + + fn push(&mut self, num_nops: usize, kind: TerminatorKind<'tcx>) -> BasicBlock { + let nop = Statement { source_info: self.source_info, kind: StatementKind::Nop }; + + self.blocks.push(BasicBlockData { + statements: std::iter::repeat(&nop).cloned().take(num_nops).collect(), + terminator: Some(Terminator { source_info: self.source_info, kind }), + is_cleanup: false, + }) + } + + fn link(&mut self, from_block: BasicBlock, to_block: BasicBlock) { + match self.blocks[from_block].terminator_mut().kind { + TerminatorKind::Assert { ref mut target, .. } + | TerminatorKind::Call { destination: Some((_, ref mut target)), .. } + | TerminatorKind::Drop { ref mut target, .. } + | TerminatorKind::DropAndReplace { ref mut target, .. } + | TerminatorKind::FalseEdge { real_target: ref mut target, .. } + | TerminatorKind::FalseUnwind { real_target: ref mut target, .. } + | TerminatorKind::Goto { ref mut target } + | TerminatorKind::InlineAsm { destination: Some(ref mut target), .. } + | TerminatorKind::Yield { resume: ref mut target, .. } => *target = to_block, + ref invalid => bug!("Invalid from_block: {:?}", invalid), + } + } + + fn add_block_from( + &mut self, + some_from_block: Option, + to_kind: TerminatorKind<'tcx>, + ) -> BasicBlock { + let new_block = self.push(1, to_kind); + if let Some(from_block) = some_from_block { + self.link(from_block, new_block); + } + new_block + } + + fn set_branch(&mut self, switchint: BasicBlock, branch_index: usize, to_block: BasicBlock) { + match self.blocks[switchint].terminator_mut().kind { + TerminatorKind::SwitchInt { ref mut targets, .. } => { + let mut branches = targets.iter().collect::>(); + let otherwise = if branch_index == branches.len() { + to_block + } else { + let old_otherwise = targets.otherwise(); + if branch_index > branches.len() { + branches.push((branches.len() as u128, old_otherwise)); + while branches.len() < branch_index { + branches.push((branches.len() as u128, START_BLOCK)); + } + to_block + } else { + branches[branch_index] = (branch_index as u128, to_block); + old_otherwise + } + }; + *targets = SwitchTargets::new(branches.into_iter(), otherwise); + } + ref invalid => bug!("Invalid BasicBlock kind or no to_block: {:?}", invalid), + } + } + + fn call(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from( + some_from_block, + TerminatorKind::Call { + func: Operand::Copy(self.dummy_place.clone()), + args: vec![], + destination: Some((self.dummy_place.clone(), START_BLOCK)), + cleanup: None, + from_hir_call: false, + fn_span: DUMMY_SP, + }, + ) + } + + fn goto(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Goto { target: START_BLOCK }) + } + + fn switchint(&mut self, some_from_block: Option) -> BasicBlock { + let move_ = |place: Place<'tcx>| Operand::Move(place); + let discriminant = Place::from(self.new_temp()); + let switchint_kind = TerminatorKind::SwitchInt { + discr: move_(discriminant), + switch_ty: dummy_ty(), + targets: SwitchTargets::static_if(0, START_BLOCK, START_BLOCK), + }; + self.add_block_from(some_from_block, switchint_kind) + } + + fn return_(&mut self, some_from_block: Option) -> BasicBlock { + self.add_block_from(some_from_block, TerminatorKind::Return) + } + + fn to_body(self) -> Body<'tcx> { + Body::new_cfg_only(self.blocks) + } +} + +fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { + format!( + "{:?}", + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + let kind = &data.terminator().kind; + match kind { + TerminatorKind::Assert { target, .. } + | TerminatorKind::Call { destination: Some((_, target)), .. } + | TerminatorKind::Drop { target, .. } + | TerminatorKind::DropAndReplace { target, .. } + | TerminatorKind::FalseEdge { real_target: target, .. } + | TerminatorKind::FalseUnwind { real_target: target, .. } + | TerminatorKind::Goto { target } + | TerminatorKind::InlineAsm { destination: Some(target), .. } + | TerminatorKind::Yield { resume: target, .. } => { + format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), target) + } + TerminatorKind::SwitchInt { targets, .. } => { + format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), targets) + } + _ => format!("{:?}:{}", bb, debug::term_type(kind)), + } + }) + .collect::>() + ) +} + +static PRINT_GRAPHS: bool = false; + +fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + mir_body + .basic_blocks() + .iter_enumerated() + .map(|(bb, data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bb, + bb, + debug::term_type(&data.terminator().kind), + mir_body + .successors(bb) + .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +fn print_coverage_graphviz( + name: &str, + mir_body: &Body<'_>, + basic_coverage_blocks: &graph::CoverageGraph, +) { + if PRINT_GRAPHS { + println!( + "digraph {} {{\n{}\n}}", + name, + basic_coverage_blocks + .iter_enumerated() + .map(|(bcb, bcb_data)| { + format!( + " {:?} [label=\"{:?}: {}\"];\n{}", + bcb, + bcb, + debug::term_type(&bcb_data.terminator(mir_body).kind), + basic_coverage_blocks + .successors(bcb) + .map(|successor| { format!(" {:?} -> {:?};", bcb, successor) }) + .collect::>() + .join("\n") + ) + }) + .collect::>() + .join("\n") + ); + } +} + +/// Create a mock `Body` with a simple flow. +fn mir_goto_switchint() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let goto = blocks.goto(Some(start)); + let switchint = blocks.switchint(Some(goto)); + let then_call = blocks.call(None); + let else_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + blocks.set_branch(switchint, 1, else_call); + blocks.return_(Some(then_call)); + blocks.return_(Some(else_call)); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_goto_switchint", &mir_body); + /* Graphviz character plots created using: `graph-easy --as=boxart`: + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb1: Goto │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Call │ ◀── │ bb2: SwitchInt │ + └─────────────┘ └────────────────┘ + │ │ + │ │ + ▼ ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb6: Return │ │ bb3: Call │ + └─────────────┘ └────────────────┘ + │ + │ + ▼ + ┌────────────────┐ + │ bb5: Return │ + └────────────────┘ + */ + mir_body +} + +fn covgraph_goto_switchint() -> graph::CoverageGraph { + let mir_body = mir_goto_switchint(); + if false { + println!("basic_blocks = {}", debug_basic_blocks(&mir_body)); + } + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &covgraph); + /* + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ + └──────────────┘ └─────────────────┘ + │ + │ + ▼ + ┌─────────────────┐ + │ bcb1: Return │ + └─────────────────┘ + */ + covgraph +} + +/// Create a mock `Body` with a loop. +fn mir_switchint_then_loop_else_return() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let backedge_goto = blocks.goto(Some(then_call)); + blocks.link(backedge_goto, switchint); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_then_loop_else_return", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb4: Return │ ◀── │ bb1: SwitchInt │ ◀┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb3: Goto │ ─┘ + └────────────────┘ + */ + mir_body +} + +fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { + let mir_body = mir_switchint_then_loop_else_return(); + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_switchint_then_loop_else_return", &mir_body, &covgraph); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌────────────┐ ┌─────────────────┐ + │ bcb3: Goto │ ◀── │ bcb1: SwitchInt │ ◀┐ + └────────────┘ └─────────────────┘ │ + │ │ │ + │ │ │ + │ ▼ │ + │ ┌─────────────────┐ │ + │ │ bcb2: Return │ │ + │ └─────────────────┘ │ + │ │ + └─────────────────────────────────────┘ + */ + covgraph +} + +/// Create a mock `Body` with nested loops. +fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { + let mut blocks = MockBlocks::new(); + let start = blocks.call(None); + let switchint = blocks.switchint(Some(start)); + let then_call = blocks.call(None); + blocks.set_branch(switchint, 0, then_call); + let else_return = blocks.return_(None); + blocks.set_branch(switchint, 1, else_return); + + let inner_start = blocks.call(Some(then_call)); + let inner_switchint = blocks.switchint(Some(inner_start)); + let inner_then_call = blocks.call(None); + blocks.set_branch(inner_switchint, 0, inner_then_call); + let inner_backedge_goto = blocks.goto(Some(inner_then_call)); + blocks.link(inner_backedge_goto, inner_switchint); + let inner_else_break_goto = blocks.goto(None); + blocks.set_branch(inner_switchint, 1, inner_else_break_goto); + + let backedge_goto = blocks.goto(Some(inner_else_break_goto)); + blocks.link(backedge_goto, switchint); + + let mir_body = blocks.to_body(); + print_mir_graphviz("mir_switchint_loop_then_inner_loop_else_break", &mir_body); + /* + ┌────────────────┐ + │ bb0: Call │ + └────────────────┘ + │ + │ + ▼ + ┌─────────────┐ ┌────────────────┐ + │ bb3: Return │ ◀── │ bb1: SwitchInt │ ◀─────┐ + └─────────────┘ └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb2: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌────────────────┐ │ + │ bb4: Call │ │ + └────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────┐ ┌────────────────┐ │ + │ bb8: Goto │ ◀── │ bb5: SwitchInt │ ◀┐ │ + └─────────────┘ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + ▼ ▼ │ │ + ┌─────────────┐ ┌────────────────┐ │ │ + │ bb9: Goto │ ─┐ │ bb6: Call │ │ │ + └─────────────┘ │ └────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────────────┐ │ │ + │ │ bb7: Goto │ ─┘ │ + │ └────────────────┘ │ + │ │ + └───────────────────────────┘ + */ + mir_body +} + +fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph { + let mir_body = mir_switchint_loop_then_inner_loop_else_break(); + let covgraph = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_loop_then_inner_loop_else_break", + &mir_body, + &covgraph, + ); + /* + ┌─────────────────┐ + │ bcb0: Call │ + └─────────────────┘ + │ + │ + ▼ + ┌──────────────┐ ┌─────────────────┐ + │ bcb2: Return │ ◀── │ bcb1: SwitchInt │ ◀┐ + └──────────────┘ └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌─────────────────┐ │ + │ bcb3: Call │ │ + └─────────────────┘ │ + │ │ + │ │ + ▼ │ + ┌──────────────┐ ┌─────────────────┐ │ + │ bcb6: Goto │ ◀── │ bcb4: SwitchInt │ ◀┼────┐ + └──────────────┘ └─────────────────┘ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌─────────────────┐ │ │ + │ │ bcb5: Goto │ ─┘ │ + │ └─────────────────┘ │ + │ │ + └────────────────────────────────────────────┘ + */ + covgraph +} + +macro_rules! assert_successors { + ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { + let mut successors = $basic_coverage_blocks.successors[$i].clone(); + successors.sort_unstable(); + assert_eq!(successors, vec![$($successor),*]); + } +} + +#[test] +fn test_covgraph_goto_switchint() { + let basic_coverage_blocks = covgraph_goto_switchint(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 3, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); + assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb2, []); +} + +#[test] +fn test_find_loop_backedges_none() { + let basic_coverage_blocks = covgraph_goto_switchint(); + if false { + println!( + "basic_coverage_blocks = {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + println!("successors = {:?}", basic_coverage_blocks.successors); + } + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 0, + "backedges: {:?}", + backedges + ); +} + +#[test] +fn test_covgraph_switchint_then_loop_else_return() { + let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 4, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); +} + +#[test] +fn test_find_loop_backedges_one() { + let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 1, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(3); + + assert_eq!(backedges[bcb1], vec![bcb3]); +} + +#[test] +fn test_covgraph_switchint_loop_then_inner_loop_else_break() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + assert_eq!( + basic_coverage_blocks.num_nodes(), + 7, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); + assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); + assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); +} + +#[test] +fn test_find_loop_backedges_two() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let backedges = graph::find_loop_backedges(&basic_coverage_blocks); + assert_eq!( + backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), + 2, + "backedges: {:?}", + backedges + ); + + let_bcb!(1); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); + + assert_eq!(backedges[bcb1], vec![bcb5]); + assert_eq!(backedges[bcb4], vec![bcb6]); +} + +#[test] +fn test_traverse_coverage_with_loops() { + let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mut traversed_in_order = Vec::new(); + let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); + while let Some(bcb) = traversal.next(&basic_coverage_blocks) { + traversed_in_order.push(bcb); + } + + let_bcb!(6); + + // bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except* + // bcb6 are inside the first loop. + assert_eq!( + *traversed_in_order.last().expect("should have elements"), + bcb6, + "bcb6 should not be visited until all nodes inside the first loop have been visited" + ); +} From ecfeac58aae332b5f88388624d9656ec6750e011 Mon Sep 17 00:00:00 2001 From: Poliorcetics Date: Thu, 12 Nov 2020 01:55:28 +0100 Subject: [PATCH 08/46] Use intradoc-links for the whole test, add a @has check Co-authored-by: Joshua Nelson --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index d758d9936132f..0c047e6249a53 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,12 +1,13 @@ // ignore-tidy-linelength pub mod internal { + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.mod.html' pub struct r#mod; /// See [name], [other name] /// /// [name]: mod - /// [other name]: ../internal/struct.mod.html + /// [other name]: crate::internal::mod // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' pub struct B; From 562d50eb7b67109ca420eab8705673b138863e6a Mon Sep 17 00:00:00 2001 From: Zachary Catlin Date: Wed, 11 Nov 2020 21:02:21 -0500 Subject: [PATCH 09/46] Include llvm-as in llvm-tools-preview component Including llvm-as adds the ability to include assembly language fragments that can be inlined using LTO. --- src/bootstrap/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3d111839dc725..ad8b9c7253c68 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -177,6 +177,7 @@ const LLVM_TOOLS: &[&str] = &[ "llvm-size", // used to prints the size of the linker sections of a program "llvm-strip", // used to discard symbols from binary files to reduce their size "llvm-ar", // used for creating and modifying archive files + "llvm-as", // used to convert LLVM assembly to LLVM bitcode "llvm-dis", // used to disassemble LLVM bitcode "llc", // used to compile LLVM bytecode "opt", // used to optimize LLVM bytecode From 38fa66aac3cb1790bde8f76014db6ccd62f70244 Mon Sep 17 00:00:00 2001 From: Slanterns Date: Thu, 12 Nov 2020 10:38:03 +0800 Subject: [PATCH 10/46] Rename empty_tuple_assignment to monkey_barrel --- src/test/ui/weird-exprs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/weird-exprs.rs b/src/test/ui/weird-exprs.rs index 9c69c834296e5..2d7ebbf1d5b04 100644 --- a/src/test/ui/weird-exprs.rs +++ b/src/test/ui/weird-exprs.rs @@ -160,7 +160,7 @@ fn match_nested_if() { assert!(val); } -fn empty_tuple_assignment() { +fn monkey_barrel() { let val = ()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=()=(); assert_eq!(val, ()); } @@ -183,5 +183,5 @@ pub fn main() { r#match(); i_yield(); match_nested_if(); - empty_tuple_assignment(); + monkey_barrel(); } From eb9f2bb3b0a3f7e712efa28743acf6134d49de5c Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Wed, 11 Nov 2020 20:01:10 -0800 Subject: [PATCH 11/46] Overcome Sync issues with non-parallel compiler Per Mark's recommendation at: https://github.com/rust-lang/rust/pull/78963#issuecomment-725790071 --- compiler/rustc_middle/src/ty/mod.rs | 12 ++++++++++++ .../rustc_mir/src/transform/coverage/tests.rs | 19 ++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0042b4a3a4279..d4f299b3f37b9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -611,6 +611,18 @@ pub struct TyS<'tcx> { outer_exclusive_binder: ty::DebruijnIndex, } +impl<'tcx> TyS<'tcx> { + /// A constructor used only for internal testing. + #[allow(rustc::usage_of_ty_tykind)] + pub fn make_for_test( + kind: TyKind<'tcx>, + flags: TypeFlags, + outer_exclusive_binder: ty::DebruijnIndex, + ) -> TyS<'tcx> { + TyS { kind, flags, outer_exclusive_binder } + } +} + // `TyS` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] static_assert_size!(TyS<'_>, 32); diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 2231fe6427fa2..3e305a58c6bb2 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -7,18 +7,19 @@ use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyS}; +use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; use rustc_span::DUMMY_SP; -use std::lazy::SyncOnceCell; - -fn dummy_ty<'tcx>() -> &'static TyS<'tcx> { - static DUMMY_TYS: SyncOnceCell> = SyncOnceCell::new(); +fn dummy_ty() -> &'static TyS<'static> { + thread_local! { + static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test( + ty::Bool, + TypeFlags::empty(), + DebruijnIndex::from_usize(0), + )); + } - &DUMMY_TYS.get_or_init(|| { - let fake_type_bytes = vec![0 as u8; std::mem::size_of::>()]; - unsafe { std::ptr::read_unaligned::>(fake_type_bytes.as_ptr() as *const TyS<'_>) } - }) + &DUMMY_TYS.with(|tys| *tys) } struct MockBlocks<'tcx> { From 775f1e5acdeae38eae7de41b22bdf6ab71e34efd Mon Sep 17 00:00:00 2001 From: thiolliere Date: Thu, 12 Nov 2020 12:41:19 +0100 Subject: [PATCH 12/46] fix pretty print for qpath --- compiler/rustc_ast_pretty/src/pprust/state.rs | 11 ++++++----- src/test/pretty/qpath-associated-type-bound.rs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 src/test/pretty/qpath-associated-type-bound.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a64014f5acbb8..62a9452c9f0aa 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2328,11 +2328,12 @@ impl<'a> State<'a> { self.print_path(path, false, depth); } self.s.word(">"); - self.s.word("::"); - let item_segment = path.segments.last().unwrap(); - self.print_ident(item_segment.ident); - if let Some(ref args) = item_segment.args { - self.print_generic_args(args, colons_before_params) + for item_segment in &path.segments[qself.position..] { + self.s.word("::"); + self.print_ident(item_segment.ident); + if let Some(ref args) = item_segment.args { + self.print_generic_args(args, colons_before_params) + } } } diff --git a/src/test/pretty/qpath-associated-type-bound.rs b/src/test/pretty/qpath-associated-type-bound.rs new file mode 100644 index 0000000000000..e06885e03882b --- /dev/null +++ b/src/test/pretty/qpath-associated-type-bound.rs @@ -0,0 +1,16 @@ +// pp-exact + + +mod m { + pub trait Tr { + type Ts: super::Tu; + } +} + +trait Tu { + fn dummy() { } +} + +fn foo() { ::Ts::dummy(); } + +fn main() { } From f8a32e9a4efcbbbf79cbaeec9c3f4346e5348d76 Mon Sep 17 00:00:00 2001 From: DevJPM Date: Sat, 7 Nov 2020 20:49:41 +0100 Subject: [PATCH 13/46] Bumped minimal tested LLVM version to 9 This bumps the minimal tested llvm version to 9. This should enable supporting newer LLVM features (and CPU extensions). --- .github/workflows/ci.yml | 4 ++-- .../{x86_64-gnu-llvm-8 => x86_64-gnu-llvm-9}/Dockerfile | 4 ++-- src/ci/github-actions/ci.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/ci/docker/host-x86_64/{x86_64-gnu-llvm-8 => x86_64-gnu-llvm-9}/Dockerfile (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84e39a4189eb4..f10b6ca7ea94d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: - name: mingw-check os: ubuntu-latest-xl env: {} - - name: x86_64-gnu-llvm-8 + - name: x86_64-gnu-llvm-9 os: ubuntu-latest-xl env: {} - name: x86_64-gnu-tools @@ -265,7 +265,7 @@ jobs: - name: x86_64-gnu-distcheck os: ubuntu-latest-xl env: {} - - name: x86_64-gnu-llvm-8 + - name: x86_64-gnu-llvm-9 env: RUST_BACKTRACE: 1 os: ubuntu-latest-xl diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile similarity index 97% rename from src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile rename to src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile index bd046f802c8d0..36c9238195ce1 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile @@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-8-tools \ + llvm-9-tools \ libedit-dev \ libssl-dev \ pkg-config \ @@ -27,7 +27,7 @@ RUN sh /scripts/sccache.sh # using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-8 \ + --llvm-root=/usr/lib/llvm-9 \ --enable-llvm-link-shared \ --set rust.thin-lto-import-instr-limit=10 diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 031000d147cdf..9eea6243dfa57 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -280,7 +280,7 @@ jobs: - name: mingw-check <<: *job-linux-xl - - name: x86_64-gnu-llvm-8 + - name: x86_64-gnu-llvm-9 <<: *job-linux-xl - name: x86_64-gnu-tools @@ -412,7 +412,7 @@ jobs: - name: x86_64-gnu-distcheck <<: *job-linux-xl - - name: x86_64-gnu-llvm-8 + - name: x86_64-gnu-llvm-9 env: RUST_BACKTRACE: 1 <<: *job-linux-xl From 63235651e85f9c87b0b43301e6721abbf4f6f89a Mon Sep 17 00:00:00 2001 From: DevJPM Date: Sat, 7 Nov 2020 21:25:42 +0100 Subject: [PATCH 14/46] explicitly add llvm-9-dev in dockerfile apparently llvm-8-tools already had llvm-8-dev as a dependency which was removed in llvm-9-tools, so we need to explicitly pull llvm-9-dev to make a build --- src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile index 36c9238195ce1..0ab1f727a29a0 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-9/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ sudo \ gdb \ llvm-9-tools \ + llvm-9-dev \ libedit-dev \ libssl-dev \ pkg-config \ From 6830f1c6e25bb5a49782d71698878ed9ed97523c Mon Sep 17 00:00:00 2001 From: DevJPM Date: Sat, 7 Nov 2020 22:53:44 +0100 Subject: [PATCH 15/46] Bump the minimal supported LLVM version in the bootstrapping code to 9.0 --- src/bootstrap/native.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 6dc83c7d70af6..d716b23af6004 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -348,11 +348,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = output(cmd.arg("--version")); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 8 { + if major >= 9 { return; } } - panic!("\n\nbad LLVM version: {}, need >=8.0\n\n", version) + panic!("\n\nbad LLVM version: {}, need >=9.0\n\n", version) } fn configure_cmake( From b51bcc72d9fce469ccdcd75676d8bd35f41e5614 Mon Sep 17 00:00:00 2001 From: DevJPM Date: Sat, 7 Nov 2020 23:25:45 +0100 Subject: [PATCH 16/46] fully exploited the dropped support of LLVM 8 This commit grepped for LLVM_VERSION_GE, LLVM_VERSION_LT, get_major_version and min-llvm-version and statically evaluated every expression possible (and sensible) assuming that the LLVM version is >=9 now --- compiler/rustc_codegen_llvm/src/attributes.rs | 14 ++------ compiler/rustc_codegen_llvm/src/back/write.rs | 5 --- compiler/rustc_codegen_llvm/src/context.rs | 3 -- compiler/rustc_codegen_llvm/src/llvm_util.rs | 8 ++--- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 32 ++----------------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 16 ---------- src/test/codegen/abi-efiapi.rs | 1 - src/test/codegen/force-unwind-tables.rs | 1 - .../new-llvm-pass-manager-thin-lto.rs | 1 - 9 files changed, 8 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 87bcce07b3498..3bc60c0b4d75a 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -147,17 +147,9 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { fn translate_obsolete_target_features(feature: &str) -> &str { const LLVM9_FEATURE_CHANGES: &[(&str, &str)] = &[("+fp-only-sp", "-fp64"), ("-fp-only-sp", "+fp64"), ("+d16", "-d32"), ("-d16", "+d32")]; - if llvm_util::get_major_version() >= 9 { - for &(old, new) in LLVM9_FEATURE_CHANGES { - if feature == old { - return new; - } - } - } else { - for &(old, new) in LLVM9_FEATURE_CHANGES { - if feature == new { - return old; - } + for &(old, new) in LLVM9_FEATURE_CHANGES { + if feature == old { + return new; } } feature diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index e6acb6860be9e..0116b81951eb3 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -377,11 +377,6 @@ fn get_pgo_use_path(config: &ModuleConfig) -> Option { } pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool { - // We only support the new pass manager starting with LLVM 9. - if llvm_util::get_major_version() < 9 { - return false; - } - // The new pass manager is disabled by default. config.new_llvm_pass_manager } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b6e922ca5456b..881b6bcb68421 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -119,9 +119,6 @@ pub unsafe fn create_module( let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); let mut target_data_layout = sess.target.data_layout.clone(); - if llvm_util::get_major_version() < 9 { - target_data_layout = strip_function_ptr_alignment(target_data_layout); - } if llvm_util::get_major_version() < 10 && (sess.target.arch == "x86" || sess.target.arch == "x86_64") { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index ab70f72dc613f..cc71b6289faf1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -104,7 +104,7 @@ unsafe fn configure_llvm(sess: &Session) { } } - if sess.opts.debugging_opts.llvm_time_trace && get_major_version() >= 9 { + if sess.opts.debugging_opts.llvm_time_trace { // time-trace is not thread safe and running it in parallel will cause seg faults. if !sess.opts.debugging_opts.no_parallel_llvm { bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm") @@ -122,10 +122,8 @@ unsafe fn configure_llvm(sess: &Session) { pub fn time_trace_profiler_finish(file_name: &str) { unsafe { - if get_major_version() >= 9 { - let file_name = CString::new(file_name).unwrap(); - llvm::LLVMTimeTraceProfilerFinish(file_name.as_ptr()); - } + let file_name = CString::new(file_name).unwrap(); + llvm::LLVMTimeTraceProfilerFinish(file_name.as_ptr()); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 71ca4f23bbb17..01d76bb3e94f2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -16,9 +16,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Object/IRObjectFile.h" #include "llvm/Passes/PassBuilder.h" -#if LLVM_VERSION_GE(9, 0) #include "llvm/Passes/StandardInstrumentations.h" -#endif #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -31,15 +29,11 @@ #include "llvm-c/Transforms/PassManagerBuilder.h" #include "llvm/Transforms/Instrumentation.h" -#if LLVM_VERSION_GE(9, 0) #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Support/TimeProfiler.h" -#endif #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" -#if LLVM_VERSION_GE(9, 0) #include "llvm/Transforms/Utils/CanonicalizeAliases.h" -#endif #include "llvm/Transforms/Utils/NameAnonGlobals.h" using namespace llvm; @@ -73,20 +67,18 @@ extern "C" void LLVMTimeTraceProfilerInitialize() { timeTraceProfilerInitialize( /* TimeTraceGranularity */ 0, /* ProcName */ "rustc"); -#elif LLVM_VERSION_GE(9, 0) +#else timeTraceProfilerInitialize(); #endif } extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) { -#if LLVM_VERSION_GE(9, 0) StringRef FN(FileName); std::error_code EC; raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways); timeTraceProfilerWrite(OS); timeTraceProfilerCleanup(); -#endif } enum class LLVMRustPassKind { @@ -127,22 +119,14 @@ extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) { const bool CompileKernel = false; -#if LLVM_VERSION_GE(9, 0) return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover)); -#else - return wrap(createAddressSanitizerModulePass(CompileKernel, Recover)); -#endif } extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) { -#if LLVM_VERSION_GE(9, 0) const bool CompileKernel = false; return wrap(createMemorySanitizerLegacyPassPass( MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel})); -#else - return wrap(createMemorySanitizerLegacyPassPass(TrackOrigins, Recover)); -#endif } extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() { @@ -657,8 +641,6 @@ extern "C" typedef void (*LLVMRustSelfProfileBeforePassCallback)(void*, // LlvmS const char*); // IR name extern "C" typedef void (*LLVMRustSelfProfileAfterPassCallback)(void*); // LlvmSelfProfiler -#if LLVM_VERSION_GE(9, 0) - std::string LLVMRustwrappedIrGetName(const llvm::Any &WrappedIr) { if (any_isa(WrappedIr)) return any_cast(WrappedIr)->getName().str(); @@ -706,7 +688,6 @@ void LLVMSelfProfileInitializeCallbacks( AfterPassCallback(LlvmSelfProfiler); }); } -#endif enum class LLVMRustOptStage { PreLinkNoLTO, @@ -739,7 +720,6 @@ LLVMRustOptimizeWithNewPassManager( void* LlvmSelfProfiler, LLVMRustSelfProfileBeforePassCallback BeforePassCallback, LLVMRustSelfProfileAfterPassCallback AfterPassCallback) { -#if LLVM_VERSION_GE(9, 0) Module *TheModule = unwrap(ModuleRef); TargetMachine *TM = unwrap(TMRef); PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); @@ -970,11 +950,6 @@ LLVMRustOptimizeWithNewPassManager( UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove MPM.run(*TheModule, MAM); -#else - // The new pass manager has been available for a long time, - // but we don't bother supporting it on old LLVM versions. - report_fatal_error("New pass manager only supported since LLVM 9"); -#endif } // Callback to demangle function name @@ -1325,12 +1300,9 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, GlobalValue::LinkageTypes NewLinkage) { Ret->ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; }; -#if LLVM_VERSION_GE(9, 0) + thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, Ret->GUIDPreservedSymbols); -#else - thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage); -#endif // Here we calculate an `ExportedGUIDs` set for use in the `isExported` // callback below. This callback below will dictate the linkage for all diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 938eb19faef6b..9b0c176b69203 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -124,9 +124,7 @@ extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, return wrap(unwrap(M) ->getOrInsertFunction(StringRef(Name, NameLen), unwrap(FunctionTy)) -#if LLVM_VERSION_GE(9, 0) .getCallee() -#endif ); } @@ -251,11 +249,7 @@ extern "C" void LLVMRustAddDereferenceableOrNullCallSiteAttr(LLVMValueRef Instr, extern "C" void LLVMRustAddByValCallSiteAttr(LLVMValueRef Instr, unsigned Index, LLVMTypeRef Ty) { CallBase *Call = unwrap(Instr); -#if LLVM_VERSION_GE(9, 0) Attribute Attr = Attribute::getWithByValType(Call->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(Call->getContext(), Attribute::ByVal); -#endif Call->addAttribute(Index, Attr); } @@ -296,11 +290,7 @@ extern "C" void LLVMRustAddDereferenceableOrNullAttr(LLVMValueRef Fn, extern "C" void LLVMRustAddByValAttr(LLVMValueRef Fn, unsigned Index, LLVMTypeRef Ty) { Function *F = unwrap(Fn); -#if LLVM_VERSION_GE(9, 0) Attribute Attr = Attribute::getWithByValType(F->getContext(), unwrap(Ty)); -#else - Attribute Attr = Attribute::get(F->getContext(), Attribute::ByVal); -#endif F->addAttribute(Index, Attr); } @@ -616,11 +606,9 @@ static DISubprogram::DISPFlags fromRust(LLVMRustDISPFlags SPFlags) { if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagOptimized)) { Result |= DISubprogram::DISPFlags::SPFlagOptimized; } -#if LLVM_VERSION_GE(9, 0) if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) { Result |= DISubprogram::DISPFlags::SPFlagMainSubprogram; } -#endif return Result; } @@ -744,10 +732,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( DITemplateParameterArray(unwrap(TParam)); DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); DINode::DIFlags llvmFlags = fromRust(Flags); -#if LLVM_VERSION_LT(9, 0) - if (isSet(SPFlags & LLVMRustDISPFlags::SPFlagMainSubprogram)) - llvmFlags |= DINode::DIFlags::FlagMainSubprogram; -#endif DISubprogram *Sub = Builder->createFunction( unwrapDI(Scope), StringRef(Name, NameLen), diff --git a/src/test/codegen/abi-efiapi.rs b/src/test/codegen/abi-efiapi.rs index 1c0b77ad9c727..6cb2728359b95 100644 --- a/src/test/codegen/abi-efiapi.rs +++ b/src/test/codegen/abi-efiapi.rs @@ -1,7 +1,6 @@ // Checks if the correct annotation for the efiapi ABI is passed to llvm. // revisions:x86_64 i686 aarch64 arm riscv -// min-llvm-version: 9.0 // needs-llvm-components: aarch64 arm riscv //[x86_64] compile-flags: --target x86_64-unknown-uefi diff --git a/src/test/codegen/force-unwind-tables.rs b/src/test/codegen/force-unwind-tables.rs index eba4a7469f930..4c0a5602c6dad 100644 --- a/src/test/codegen/force-unwind-tables.rs +++ b/src/test/codegen/force-unwind-tables.rs @@ -1,4 +1,3 @@ -// min-llvm-version: 8.0 // compile-flags: -C no-prepopulate-passes -C force-unwind-tables=y #![crate_type="lib"] diff --git a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs index 9439df266d59b..1542c7f311848 100644 --- a/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs +++ b/src/test/ui/sanitize/new-llvm-pass-manager-thin-lto.rs @@ -2,7 +2,6 @@ // being run when compiling with new LLVM pass manager and ThinLTO. // Note: The issue occurred only on non-zero opt-level. // -// min-llvm-version: 9.0 // needs-sanitizer-support // needs-sanitizer-address // From 8236830209ca4a8cdf3a538f2cce40162ff31fc5 Mon Sep 17 00:00:00 2001 From: DevJPM Date: Sat, 7 Nov 2020 23:36:00 +0100 Subject: [PATCH 17/46] Removed an unused function now that LLVM 9 is the minimal supported version The function was only used in LLVM 8 compatibility code and was found and flagged by dead code detection and now removed. --- compiler/rustc_codegen_llvm/src/context.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 881b6bcb68421..8dd40308075ed 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -100,11 +100,6 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { } } -fn strip_function_ptr_alignment(data_layout: String) -> String { - // FIXME: Make this more general. - data_layout.replace("-Fi8-", "-") -} - fn strip_x86_address_spaces(data_layout: String) -> String { data_layout.replace("-p270:32:32-p271:32:32-p272:64:64-", "-") } From 7e443c4282df6daa9c92be7d1008c974cc5ede00 Mon Sep 17 00:00:00 2001 From: DevJPM Date: Tue, 10 Nov 2020 20:07:18 +0100 Subject: [PATCH 18/46] Dropped Support for Bidirectional Custom Target Definition Emulation as requested in the review and argued that this is only consistent with later LLVM upgrades --- compiler/rustc_codegen_llvm/src/attributes.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 3bc60c0b4d75a..456fa6fc19fba 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -144,17 +144,6 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { ); } -fn translate_obsolete_target_features(feature: &str) -> &str { - const LLVM9_FEATURE_CHANGES: &[(&str, &str)] = - &[("+fp-only-sp", "-fp64"), ("-fp-only-sp", "+fp64"), ("+d16", "-d32"), ("-d16", "+d32")]; - for &(old, new) in LLVM9_FEATURE_CHANGES { - if feature == old { - return new; - } - } - feature -} - pub fn llvm_target_features(sess: &Session) -> impl Iterator { const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; @@ -169,7 +158,6 @@ pub fn llvm_target_features(sess: &Session) -> impl Iterator { .split(',') .chain(cmdline) .filter(|l| !l.is_empty()) - .map(translate_obsolete_target_features) } pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { From 86193ca91c2fa472ef8fbebe05037331d803f0fd Mon Sep 17 00:00:00 2001 From: DevJPM Date: Tue, 10 Nov 2020 23:22:38 +0100 Subject: [PATCH 19/46] fixed a re-format due to removed chain call --- compiler/rustc_codegen_llvm/src/attributes.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 456fa6fc19fba..e06e2d45665b1 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -153,11 +153,7 @@ pub fn llvm_target_features(sess: &Session) -> impl Iterator { .target_feature .split(',') .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); - sess.target - .features - .split(',') - .chain(cmdline) - .filter(|l| !l.is_empty()) + sess.target.features.split(',').chain(cmdline).filter(|l| !l.is_empty()) } pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { From 3747df71fa280557f85d847dcb460c0cbe0d16ce Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 12 Nov 2020 09:49:45 -0500 Subject: [PATCH 20/46] Avoid installing external LLVM dylibs If the LLVM was externally provided, then we don't currently copy artifacts into the sysroot. This is not necessarily the right choice (in particular, it will require the LLVM dylib to be in the linker's load path at runtime), but the common use case for external LLVMs is distribution provided LLVMs, and in that case they're usually in the standard search path (e.g., /usr/lib) and copying them here is going to cause problems as we may end up with the wrong files and isn't what distributions want. This behavior may be revisited in the future though. --- src/bootstrap/dist.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index bdab12db43502..9506a8294082d 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -2335,6 +2335,22 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir return; } + if let Some(config) = builder.config.target_config.get(&target) { + if config.llvm_config.is_some() { + // If the LLVM was externally provided, then we don't currently copy + // artifacts into the sysroot. This is not necessarily the right + // choice (in particular, it will require the LLVM dylib to be in + // the linker's load path at runtime), but the common use case for + // external LLVMs is distribution provided LLVMs, and in that case + // they're usually in the standard search path (e.g., /usr/lib) and + // copying them here is going to cause problems as we may end up + // with the wrong files and isn't what distributions want. + // + // This behavior may be revisited in the future though. + return; + } + } + // On macOS, rustc (and LLVM tools) link to an unversioned libLLVM.dylib // instead of libLLVM-11-rust-....dylib, as on linux. It's not entirely // clear why this is the case, though. llvm-config will emit the versioned From 010265a4393176bdc62fc8f3e06ef4bf5b42a6da Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Nov 2020 07:23:00 -0800 Subject: [PATCH 21/46] Fix an intrinsic invocation on threaded wasm This looks like it was forgotten to get updated in #74482 and wasm with threads isn't built on CI so we didn't catch this by accident. --- library/std/src/sys/wasm/mutex_atomics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/wasm/mutex_atomics.rs b/library/std/src/sys/wasm/mutex_atomics.rs index 479182ffa44d5..5ff0ec052b6f4 100644 --- a/library/std/src/sys/wasm/mutex_atomics.rs +++ b/library/std/src/sys/wasm/mutex_atomics.rs @@ -138,7 +138,7 @@ impl ReentrantMutex { self.owner.swap(0, SeqCst); // SAFETY: the caller must gurantee that `self.ptr()` is valid i32. unsafe { - wasm32::atomic_notify(self.ptr() as *mut i32, 1); + wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1); } // wake up one waiter, if any } ref mut n => *n -= 1, From dd682cb48c8b667859dded98a4bbfbd891a1eca4 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 12 Nov 2020 19:16:59 +0300 Subject: [PATCH 22/46] rustc_target: Fix dash vs underscore mismatches in option names --- compiler/rustc_target/src/spec/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f949bf95a502a..f837114ee743b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1428,8 +1428,8 @@ impl Target { } key!(is_builtin, bool); - key!(endian = "target_endian"); - key!(c_int_width = "target_c_int_width"); + key!(endian = "target-endian"); + key!(c_int_width = "target-c-int-width"); key!(os); key!(env); key!(vendor); @@ -1466,7 +1466,7 @@ impl Target { key!(exe_suffix); key!(staticlib_prefix); key!(staticlib_suffix); - key!(os_family = "target_family", optional); + key!(os_family = "target-family", optional); key!(abi_return_struct_as_int, bool); key!(is_like_osx, bool); key!(is_like_solaris, bool); @@ -1511,7 +1511,7 @@ impl Target { key!(limit_rdylib_exports, bool); key!(override_export_symbols, opt_list); key!(merge_functions, MergeFunctions)?; - key!(mcount = "target_mcount"); + key!(mcount = "target-mcount"); key!(llvm_abiname); key!(relax_elf_relocations, bool); key!(llvm_args, list); @@ -1663,8 +1663,8 @@ impl ToJson for Target { target_val!(data_layout); target_option_val!(is_builtin); - target_option_val!(endian, "target_endian"); - target_option_val!(c_int_width, "target_c_int_width"); + target_option_val!(endian, "target-endian"); + target_option_val!(c_int_width, "target-c-int-width"); target_option_val!(os); target_option_val!(env); target_option_val!(vendor); @@ -1701,7 +1701,7 @@ impl ToJson for Target { target_option_val!(exe_suffix); target_option_val!(staticlib_prefix); target_option_val!(staticlib_suffix); - target_option_val!(os_family, "target_family"); + target_option_val!(os_family, "target-family"); target_option_val!(abi_return_struct_as_int); target_option_val!(is_like_osx); target_option_val!(is_like_solaris); @@ -1746,7 +1746,7 @@ impl ToJson for Target { target_option_val!(limit_rdylib_exports); target_option_val!(override_export_symbols); target_option_val!(merge_functions); - target_option_val!(mcount, "target_mcount"); + target_option_val!(mcount, "target-mcount"); target_option_val!(llvm_abiname); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); From 0b4af1614d91152564b852572afa63fab71162a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 23/46] Never inline when `no_sanitize` attributes differ The inliner looks if a sanitizer is enabled before considering `no_sanitize` attribute as possible source of incompatibility. The MIR inlining could happen in a crate with sanitizer disabled, but code generation in a crate with sanitizer enabled, thus the attribute would be incorrectly ignored. To avoid the issue never inline functions with different `no_sanitize` attributes. --- compiler/rustc_mir/src/transform/inline.rs | 6 +----- src/test/mir-opt/inline/inline-compatibility.rs | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 7737672dbde66..7332d4194944e 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -218,11 +218,7 @@ impl Inliner<'tcx> { return false; } - let self_no_sanitize = - self.codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - let callee_no_sanitize = - codegen_fn_attrs.no_sanitize & self.tcx.sess.opts.debugging_opts.sanitizer; - if self_no_sanitize != callee_no_sanitize { + if self.codegen_fn_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { debug!("`callee has incompatible no_sanitize attribute - not inlining"); return false; } diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs index ff9049edb4f2c..2e9edf5260f53 100644 --- a/src/test/mir-opt/inline/inline-compatibility.rs +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -1,8 +1,6 @@ // Checks that only functions with compatible attributes are inlined. // // only-x86_64 -// needs-sanitizer-address -// compile-flags: -Zsanitizer=address #![crate_type = "lib"] #![feature(no_sanitize)] @@ -35,5 +33,5 @@ pub unsafe fn not_inlined_no_sanitize() { pub unsafe fn target_feature() {} #[inline] -#[no_sanitize(address, memory)] +#[no_sanitize(address)] pub unsafe fn no_sanitize() {} From ae4332643de6a672ab3aefd62a3063c9af21166c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 24/46] Never inline cold functions The information about cold attribute is lost during inlining, Avoid the issue by never inlining cold functions. --- compiler/rustc_mir/src/transform/inline.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 7332d4194944e..5023e49df3abb 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -252,9 +252,9 @@ impl Inliner<'tcx> { self.tcx.sess.opts.debugging_opts.inline_mir_threshold }; - // Significantly lower the threshold for inlining cold functions if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { - threshold /= 5; + debug!("#[cold] present - not inlining"); + return false; } // Give a bonus functions with a small number of blocks, From 9bb3d6b7d472e2116312ea45db07a5338af205fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 25/46] Remove check for impossible condition The callee body is already transformed; the condition is always false. --- compiler/rustc_mir/src/transform/inline.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 5023e49df3abb..0d6d9e397ac9f 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -203,12 +203,6 @@ impl Inliner<'tcx> { debug!("should_inline({:?})", callsite); let tcx = self.tcx; - // Cannot inline generators which haven't been transformed yet - if callee_body.yield_ty.is_some() { - debug!(" yield ty present - not inlining"); - return false; - } - let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); let self_features = &self.codegen_fn_attrs.target_features; From 66cadec1763ac645337c1ac58f06ea48b9b72a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 26/46] Fix generator inlining by checking for rust-call abi and spread arg --- compiler/rustc_mir/src/transform/inline.rs | 26 +++++++++++---------- src/test/mir-opt/inline/inline-generator.rs | 16 +++++++++++++ 2 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/test/mir-opt/inline/inline-generator.rs diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 0d6d9e397ac9f..2ccb9b3709f2f 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -7,6 +7,7 @@ use rustc_index::vec::Idx; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_span::{hygiene::ExpnKind, ExpnData, Span}; use rustc_target::spec::abi::Abi; @@ -28,6 +29,7 @@ pub struct Inline; #[derive(Copy, Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, block: BasicBlock, target: Option, source_info: SourceInfo, @@ -173,22 +175,23 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func: ref op, ref destination, .. } = terminator.kind { - if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { - // To resolve an instance its substs have to be fully normalized, so - // we do this here. - let normalized_substs = self.tcx.normalize_erasing_regions(self.param_env, substs); + if let TerminatorKind::Call { ref func, ref destination, .. } = terminator.kind { + let func_ty = func.ty(caller_body, self.tcx); + if let ty::FnDef(def_id, substs) = *func_ty.kind() { + // To resolve an instance its substs have to be fully normalized. + let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); let callee = - Instance::resolve(self.tcx, self.param_env, callee_def_id, normalized_substs) - .ok() - .flatten()?; + Instance::resolve(self.tcx, self.param_env, def_id, substs).ok().flatten()?; if let InstanceDef::Virtual(..) | InstanceDef::Intrinsic(_) = callee.def { return None; } + let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs); + return Some(CallSite { callee, + fn_sig, block: bb, target: destination.map(|(_, target)| target), source_info: terminator.source_info, @@ -437,7 +440,7 @@ impl Inliner<'tcx> { }; // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body); + let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body); let mut integrator = Integrator { args: &args, @@ -518,6 +521,7 @@ impl Inliner<'tcx> { args: Vec>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, + callee_body: &Body<'tcx>, ) -> Vec { let tcx = self.tcx; @@ -544,9 +548,7 @@ impl Inliner<'tcx> { // tmp2 = tuple_tmp.2 // // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - // FIXME(eddyb) make this check for `"rust-call"` ABI combined with - // `callee_body.spread_arg == None`, instead of special-casing closures. - if tcx.is_closure(callsite.callee.def_id()) { + if callsite.fn_sig.abi() == Abi::RustCall && callee_body.spread_arg.is_none() { let mut args = args.into_iter(); let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); diff --git a/src/test/mir-opt/inline/inline-generator.rs b/src/test/mir-opt/inline/inline-generator.rs new file mode 100644 index 0000000000000..d11b3e548f721 --- /dev/null +++ b/src/test/mir-opt/inline/inline-generator.rs @@ -0,0 +1,16 @@ +// ignore-wasm32-bare compiled with panic=abort by default +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::pin::Pin; + +// EMIT_MIR inline_generator.main.Inline.diff +fn main() { + let _r = Pin::new(&mut g()).resume(false); +} + +#[inline(always)] +pub fn g() -> impl Generator { + #[inline(always)] + |a| { yield if a { 7 } else { 13 } } +} From 79d853eccebf6d9704adeea45f20f11bae0d7396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 27/46] Never inline C variadic functions --- compiler/rustc_mir/src/transform/inline.rs | 5 +++++ .../mir-opt/inline/inline-compatibility.rs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 2ccb9b3709f2f..aae98f5b6d8d6 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -206,6 +206,11 @@ impl Inliner<'tcx> { debug!("should_inline({:?})", callsite); let tcx = self.tcx; + if callsite.fn_sig.c_variadic() { + debug!("callee is variadic - not inlining"); + return false; + } + let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); let self_features = &self.codegen_fn_attrs.target_features; diff --git a/src/test/mir-opt/inline/inline-compatibility.rs b/src/test/mir-opt/inline/inline-compatibility.rs index 2e9edf5260f53..30aff0a64efb9 100644 --- a/src/test/mir-opt/inline/inline-compatibility.rs +++ b/src/test/mir-opt/inline/inline-compatibility.rs @@ -5,6 +5,7 @@ #![crate_type = "lib"] #![feature(no_sanitize)] #![feature(target_feature_11)] +#![feature(c_variadic)] // EMIT_MIR inline_compatibility.inlined_target_feature.Inline.diff #[target_feature(enable = "sse2")] @@ -35,3 +36,20 @@ pub unsafe fn target_feature() {} #[inline] #[no_sanitize(address)] pub unsafe fn no_sanitize() {} + +// EMIT_MIR inline_compatibility.not_inlined_c_variadic.Inline.diff +pub unsafe fn not_inlined_c_variadic() { + let s = sum(4u32, 4u32, 30u32, 200u32, 1000u32); +} + +#[no_mangle] +#[inline(always)] +unsafe extern "C" fn sum(n: u32, mut vs: ...) -> u32 { + let mut s = 0; + let mut i = 0; + while i != n { + s += vs.arg::(); + i += 1; + } + s +} From 2a010dd3404256ea774e5338ad1952d09ab2cf03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Wed, 11 Nov 2020 00:00:00 +0000 Subject: [PATCH 28/46] ./x.py test --bless --- ...patibility.inlined_no_sanitize.Inline.diff | 20 +-- ...ibility.inlined_target_feature.Inline.diff | 20 +-- ...ibility.not_inlined_c_variadic.Inline.diff | 25 ++++ ...bility.not_inlined_no_sanitize.Inline.diff | 16 +-- ...ity.not_inlined_target_feature.Inline.diff | 16 +-- .../inline/inline_generator.main.Inline.diff | 123 ++++++++++++++++++ 6 files changed, 184 insertions(+), 36 deletions(-) create mode 100644 src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_generator.main.Inline.diff diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff index 451ec39422fc4..c95cf47695785 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_no_sanitize` after Inline fn inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:24:37: 24:37 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -+ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:25:5: 25:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:23:37: 23:37 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 ++ scope 1 (inlined no_sanitize) { // at $DIR/inline-compatibility.rs:24:5: 24:18 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 -- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:25:5: 25:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 +- _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:24:5: 24:18 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:25:5: 25:16 +- // + span: $DIR/inline-compatibility.rs:24:5: 24:16 - // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:25:5: 25:18 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:25:18: 25:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:24:37: 26:2 - return; // scope 0 at $DIR/inline-compatibility.rs:26:2: 26:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:24:5: 24:18 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:24:18: 24:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:23:37: 25:2 + return; // scope 0 at $DIR/inline-compatibility.rs:25:2: 25:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff index a59ddd344cb26..2bb928343229f 100644 --- a/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff @@ -2,24 +2,24 @@ + // MIR for `inlined_target_feature` after Inline fn inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:13:40: 13:40 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -+ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:14:5: 14:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:12:40: 12:40 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 ++ scope 1 (inlined target_feature) { // at $DIR/inline-compatibility.rs:13:5: 13:21 + } bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 -- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:14:5: 14:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 +- _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:13:5: 13:21 - // mir::Constant -- // + span: $DIR/inline-compatibility.rs:14:5: 14:19 +- // + span: $DIR/inline-compatibility.rs:13:5: 13:19 - // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } - } - - bb1: { -+ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:14:5: 14:21 - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:14:21: 14:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:13:40: 15:2 - return; // scope 0 at $DIR/inline-compatibility.rs:15:2: 15:2 ++ _1 = const (); // scope 1 at $DIR/inline-compatibility.rs:13:5: 13:21 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:13:21: 13:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:12:40: 14:2 + return; // scope 0 at $DIR/inline-compatibility.rs:14:2: 14:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff new file mode 100644 index 0000000000000..09bca903c80e8 --- /dev/null +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_c_variadic.Inline.diff @@ -0,0 +1,25 @@ +- // MIR for `not_inlined_c_variadic` before Inline ++ // MIR for `not_inlined_c_variadic` after Inline + + fn not_inlined_c_variadic() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:41:40: 41:40 + let _1: u32; // in scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + scope 1 { + debug s => _1; // in scope 1 at $DIR/inline-compatibility.rs:42:9: 42:10 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:42:9: 42:10 + _1 = sum(const 4_u32, const 4_u32, const 30_u32, const 200_u32, const 1000_u32) -> bb1; // scope 0 at $DIR/inline-compatibility.rs:42:13: 42:52 + // mir::Constant + // + span: $DIR/inline-compatibility.rs:42:13: 42:16 + // + literal: Const { ty: unsafe extern "C" fn(u32, ...) -> u32 {sum}, val: Value(Scalar()) } + } + + bb1: { + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:41:40: 43:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:43:1: 43:2 + return; // scope 0 at $DIR/inline-compatibility.rs:43:2: 43:2 + } + } + diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff index 651eadc1e849c..5af3946f2e501 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_no_sanitize.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_no_sanitize` after Inline fn not_inlined_no_sanitize() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:29:41: 29:41 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:28:41: 28:41 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 - _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:30:5: 30:18 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 + _1 = no_sanitize() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:29:5: 29:18 // mir::Constant - // + span: $DIR/inline-compatibility.rs:30:5: 30:16 + // + span: $DIR/inline-compatibility.rs:29:5: 29:16 // + literal: Const { ty: unsafe fn() {no_sanitize}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:30:18: 30:19 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:29:41: 31:2 - return; // scope 0 at $DIR/inline-compatibility.rs:31:2: 31:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:29:18: 29:19 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:28:41: 30:2 + return; // scope 0 at $DIR/inline-compatibility.rs:30:2: 30:2 } } diff --git a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff index 55b9edf3adc1f..8c9fa573ce218 100644 --- a/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff +++ b/src/test/mir-opt/inline/inline_compatibility.not_inlined_target_feature.Inline.diff @@ -2,21 +2,21 @@ + // MIR for `not_inlined_target_feature` after Inline fn not_inlined_target_feature() -> () { - let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:18:44: 18:44 - let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + let mut _0: (); // return place in scope 0 at $DIR/inline-compatibility.rs:17:44: 17:44 + let _1: (); // in scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 bb0: { - StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 - _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:19:5: 19:21 + StorageLive(_1); // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 + _1 = target_feature() -> bb1; // scope 0 at $DIR/inline-compatibility.rs:18:5: 18:21 // mir::Constant - // + span: $DIR/inline-compatibility.rs:19:5: 19:19 + // + span: $DIR/inline-compatibility.rs:18:5: 18:19 // + literal: Const { ty: unsafe fn() {target_feature}, val: Value(Scalar()) } } bb1: { - StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:19:21: 19:22 - _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:18:44: 20:2 - return; // scope 0 at $DIR/inline-compatibility.rs:20:2: 20:2 + StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:18:21: 18:22 + _0 = const (); // scope 0 at $DIR/inline-compatibility.rs:17:44: 19:2 + return; // scope 0 at $DIR/inline-compatibility.rs:19:2: 19:2 } } diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff new file mode 100644 index 0000000000000..aa32daa82dd51 --- /dev/null +++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff @@ -0,0 +1,123 @@ +- // MIR for `main` before Inline ++ // MIR for `main` after Inline + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/inline-generator.rs:8:11: 8:11 + let _1: std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return>; // in scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + let mut _2: std::pin::Pin<&mut impl std::ops::Generator>; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + let mut _3: &mut impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + let mut _4: impl std::ops::Generator; // in scope 0 at $DIR/inline-generator.rs:9:28: 9:31 ++ let mut _7: bool; // in scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + scope 1 { + debug _r => _1; // in scope 1 at $DIR/inline-generator.rs:9:9: 9:11 + } ++ scope 2 (inlined g) { // at $DIR/inline-generator.rs:9:28: 9:31 ++ } ++ scope 3 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _3; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _5: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 3 at $DIR/inline-generator.rs:9:14: 9:32 ++ scope 4 { ++ scope 5 (inlined Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]>::new_unchecked) { // at $DIR/inline-generator.rs:9:14: 9:32 ++ debug pointer => _5; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ let mut _6: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]; // in scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ } ++ } ++ } ++ scope 6 (inlined g::{closure#0}) { // at $DIR/inline-generator.rs:9:14: 9:46 ++ debug a => _8; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _8: bool; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ let mut _9: u32; // in scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/inline-generator.rs:9:9: 9:11 + StorageLive(_2); // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 + StorageLive(_3); // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 + StorageLive(_4); // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- _4 = g() -> bb1; // scope 0 at $DIR/inline-generator.rs:9:28: 9:31 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:28: 9:29 +- // + literal: Const { ty: fn() -> impl std::ops::Generator {g}, val: Value(Scalar()) } +- } +- +- bb1: { ++ discriminant(_4) = 0; // scope 2 at $DIR/inline-generator.rs:9:28: 9:31 + _3 = &mut _4; // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 +- _2 = Pin::<&mut impl Generator>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:14: 9:22 +- // + user_ty: UserType(0) +- // + literal: Const { ty: fn(&mut impl std::ops::Generator) -> std::pin::Pin<&mut impl std::ops::Generator> {std::pin::Pin::<&mut impl std::ops::Generator>::new}, val: Value(Scalar()) } +- } +- +- bb2: { ++ StorageLive(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ _5 = move _3; // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageLive(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ _6 = move _5; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ (_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]) = move _6; // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_6); // scope 5 at $DIR/inline-generator.rs:9:14: 9:32 ++ StorageDead(_5); // scope 4 at $DIR/inline-generator.rs:9:14: 9:32 + StorageDead(_3); // scope 0 at $DIR/inline-generator.rs:9:31: 9:32 +- _1 = as Generator>::resume(move _2, const false) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 +- // mir::Constant +- // + span: $DIR/inline-generator.rs:9:33: 9:39 +- // + literal: Const { ty: for<'r> fn(std::pin::Pin<&'r mut impl std::ops::Generator>, bool) -> std::ops::GeneratorState< as std::ops::Generator>::Yield, as std::ops::Generator>::Return> { as std::ops::Generator>::resume}, val: Value(Scalar()) } ++ StorageLive(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _7 = const false; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 ++ _9 = discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))); // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(move _9) -> [0_u32: bb3, 1_u32: bb8, 3_u32: bb7, otherwise: bb9]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + +- bb3: { ++ bb1: { ++ StorageDead(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + StorageDead(_2); // scope 0 at $DIR/inline-generator.rs:9:45: 9:46 + StorageDead(_4); // scope 0 at $DIR/inline-generator.rs:9:46: 9:47 + _0 = const (); // scope 0 at $DIR/inline-generator.rs:8:11: 10:2 + StorageDead(_1); // scope 0 at $DIR/inline-generator.rs:10:1: 10:2 + return; // scope 0 at $DIR/inline-generator.rs:10:2: 10:2 + } + +- bb4 (cleanup): { ++ bb2 (cleanup): { + resume; // scope 0 at $DIR/inline-generator.rs:8:1: 10:2 ++ } ++ ++ bb3: { ++ _8 = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ switchInt(_8) -> [false: bb4, otherwise: bb5]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb4: { ++ ((_1 as Yielded).0: i32) = const 13_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb5: { ++ ((_1 as Yielded).0: i32) = const 7_i32; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb6; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb6: { ++ discriminant(_1) = 0; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 3; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:11: 15:39 ++ } ++ ++ bb7: { ++ ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant(_1) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ discriminant((*(_2.0: &mut [generator@$DIR/inline-generator.rs:15:5: 15:41 {bool, i32}]))) = 1; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ goto -> bb1; // scope 0 at $DIR/inline-generator.rs:15:41: 15:41 ++ } ++ ++ bb8: { ++ assert(const false, "generator resumed after completion") -> [success: bb8, unwind: bb2]; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 ++ } ++ ++ bb9: { ++ unreachable; // scope 6 at $DIR/inline-generator.rs:9:14: 9:46 + } + } + From d486bfcbff107e8a6769e00c59d02b13c664b6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 12 Nov 2020 00:00:00 +0000 Subject: [PATCH 29/46] Normalize function type during validation During inlining, the callee body is normalized and has types revealed, but some of locals corresponding to the arguments might come from the caller body which is not. As a result the caller body does not pass validation without additional normalization. --- compiler/rustc_mir/src/transform/validate.rs | 2 ++ .../auxiliary/lib.rs | 4 ---- .../ui/mir/mir-inlining/ice-issue-77306-1.rs | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index e1e6e71acb5a8..2fbfe13082f89 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -357,7 +357,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Call { func, args, destination, cleanup, .. } => { + let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let func_ty = func.ty(&self.body.local_decls, self.tcx); + let func_ty = self.tcx.normalize_erasing_regions(param_env, func_ty); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( diff --git a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs index fb4bf2b8b44e7..f3a51b415faca 100644 --- a/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs +++ b/src/test/ui/issues/issue-50865-private-impl-trait/auxiliary/lib.rs @@ -1,7 +1,3 @@ -// revisions: default miropt -//[miropt]compile-flags: -Z mir-opt-level=2 -// ~^ This flag is for #77668, it used to be ICE. - #![crate_type = "lib"] pub fn bar

( // Error won't happen if "bar" is not generic diff --git a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs index 4d083bf232155..ccb279f7fa212 100644 --- a/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs +++ b/src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs @@ -1,17 +1,27 @@ -// run-pass +// Regression test for various issues related to normalization & inlining. +// * #68347, #77306, #77668 - missed normalization during inlining. +// * #78442 - missed normalization in validator after inlining. +// +// build-pass // compile-flags:-Zmir-opt-level=2 -// Previously ICEd because we did not normalize during inlining, -// see https://github.com/rust-lang/rust/pull/77306 for more discussion. - pub fn write() { create()() } +pub fn write_generic(_t: T) { + hide()(); +} + pub fn create() -> impl FnOnce() { || () } +pub fn hide() -> impl Fn() { + write +} + fn main() { write(); + write_generic(()); } From 99be78d135e73197e04221c139a219ea6436e72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 12 Nov 2020 00:00:00 +0000 Subject: [PATCH 30/46] Always use param_env_reveal_all_normalized in validator --- compiler/rustc_mir/src/transform/validate.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index 2fbfe13082f89..876ecee80c6a1 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -38,7 +38,9 @@ pub struct Validator { impl<'tcx> MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let param_env = tcx.param_env(def_id); + // We need to param_env_reveal_all_normalized, as some optimizations + // change types in ways that require unfolding opaque types. + let param_env = tcx.param_env_reveal_all_normalized(def_id); let mir_phase = self.mir_phase; let always_live_locals = AlwaysLiveLocals::new(body); @@ -79,7 +81,6 @@ pub fn equal_up_to_regions( } // Normalize lifetimes away on both sides, then compare. - let param_env = param_env.with_reveal_all_normalized(tcx); let normalize = |ty: Ty<'tcx>| { tcx.normalize_erasing_regions( param_env, @@ -167,17 +168,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } // Normalize projections and things like that. - // FIXME: We need to reveal_all, as some optimizations change types in ways - // that require unfolding opaque types. - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); - let src = self.tcx.normalize_erasing_regions(param_env, src); - let dest = self.tcx.normalize_erasing_regions(param_env, dest); + let src = self.tcx.normalize_erasing_regions(self.param_env, src); + let dest = self.tcx.normalize_erasing_regions(self.param_env, dest); // Type-changing assignments can happen when subtyping is used. While // all normal lifetimes are erased, higher-ranked types with their // late-bound lifetimes are still around and can lead to type // differences. So we compare ignoring lifetimes. - equal_up_to_regions(self.tcx, param_env, src, dest) + equal_up_to_regions(self.tcx, self.param_env, src, dest) } } @@ -357,9 +355,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Call { func, args, destination, cleanup, .. } => { - let param_env = self.param_env.with_reveal_all_normalized(self.tcx); let func_ty = func.ty(&self.body.local_decls, self.tcx); - let func_ty = self.tcx.normalize_erasing_regions(param_env, func_ty); + let func_ty = self.tcx.normalize_erasing_regions(self.param_env, func_ty); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( From cc6baf7b99d97cb289e5f2fd531bef3cc5e87e3f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 12 Nov 2020 23:42:42 +0300 Subject: [PATCH 31/46] rustc_expand: Mark inner `#![test]` attributes as soft-unstable --- compiler/rustc_expand/src/expand.rs | 31 ++++++++++++------- library/std/src/num/tests.rs | 6 ++-- .../issue-43106-gating-of-test.rs | 1 + .../issue-43106-gating-of-test.stderr | 2 +- src/test/ui/issues/issue-28134.rs | 1 + src/test/ui/issues/issue-28134.stderr | 2 +- src/test/ui/proc-macro/proc-macro-gates.rs | 5 +++ .../ui/proc-macro/proc-macro-gates.stderr | 12 ++++++- 8 files changed, 42 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 1b31bd6a30510..27d6344702f39 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -22,7 +22,7 @@ use rustc_errors::{struct_span_err, Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::{AttemptLocalParseRecovery, Parser}; use rustc_parse::validate_attr; -use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; +use rustc_session::lint::builtin::{SOFT_UNSTABLE, UNUSED_DOC_COMMENTS}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::Limit; @@ -1064,17 +1064,24 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }) .map(|i| attrs.remove(i)); if let Some(attr) = &attr { - if !self.cx.ecfg.custom_inner_attributes() - && attr.style == ast::AttrStyle::Inner - && !attr.has_name(sym::test) - { - feature_err( - &self.cx.sess.parse_sess, - sym::custom_inner_attributes, - attr.span, - "non-builtin inner attributes are unstable", - ) - .emit(); + if attr.style == ast::AttrStyle::Inner && !self.cx.ecfg.custom_inner_attributes() { + let msg = "non-builtin inner attributes are unstable"; + if attr.has_name(sym::test) { + self.cx.sess.parse_sess.buffer_lint( + SOFT_UNSTABLE, + attr.span, + ast::CRATE_NODE_ID, + msg, + ); + } else { + feature_err( + &self.cx.sess.parse_sess, + sym::custom_inner_attributes, + attr.span, + msg, + ) + .emit(); + } } } attr diff --git a/library/std/src/num/tests.rs b/library/std/src/num/tests.rs index 2f50b73f4907f..df0df3f23f756 100644 --- a/library/std/src/num/tests.rs +++ b/library/std/src/num/tests.rs @@ -75,8 +75,8 @@ fn test_checked_mul() { macro_rules! test_is_power_of_two { ($test_name:ident, $T:ident) => { + #[test] fn $test_name() { - #![test] assert_eq!((0 as $T).is_power_of_two(), false); assert_eq!((1 as $T).is_power_of_two(), true); assert_eq!((2 as $T).is_power_of_two(), true); @@ -96,8 +96,8 @@ test_is_power_of_two! { test_is_power_of_two_uint, usize } macro_rules! test_next_power_of_two { ($test_name:ident, $T:ident) => { + #[test] fn $test_name() { - #![test] assert_eq!((0 as $T).next_power_of_two(), 1); let mut next_power = 1; for i in 1 as $T..40 { @@ -118,8 +118,8 @@ test_next_power_of_two! { test_next_power_of_two_uint, usize } macro_rules! test_checked_next_power_of_two { ($test_name:ident, $T:ident) => { + #[test] fn $test_name() { - #![test] assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); let smax = $T::MAX >> 1; assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1)); diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-test.rs b/src/test/ui/feature-gate/issue-43106-gating-of-test.rs index d343746955f39..ee3fe712e36e5 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-test.rs +++ b/src/test/ui/feature-gate/issue-43106-gating-of-test.rs @@ -1,5 +1,6 @@ // The non-crate level cases are in issue-43106-gating-of-builtin-attrs.rs. +#![allow(soft_unstable)] #![test = "4200"] //~^ ERROR cannot determine resolution for the attribute macro `test` diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr index a7d3a1e16840e..335af5e7905f2 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr @@ -1,5 +1,5 @@ error: cannot determine resolution for the attribute macro `test` - --> $DIR/issue-43106-gating-of-test.rs:3:4 + --> $DIR/issue-43106-gating-of-test.rs:4:4 | LL | #![test = "4200"] | ^^^^ diff --git a/src/test/ui/issues/issue-28134.rs b/src/test/ui/issues/issue-28134.rs index fa692db4bf66c..1ed2d330b5123 100644 --- a/src/test/ui/issues/issue-28134.rs +++ b/src/test/ui/issues/issue-28134.rs @@ -1,3 +1,4 @@ // compile-flags: --test +#![allow(soft_unstable)] #![test] //~ ERROR cannot determine resolution for the attribute macro `test` diff --git a/src/test/ui/issues/issue-28134.stderr b/src/test/ui/issues/issue-28134.stderr index 5f8d27dd043b1..8ed4d015f3216 100644 --- a/src/test/ui/issues/issue-28134.stderr +++ b/src/test/ui/issues/issue-28134.stderr @@ -1,5 +1,5 @@ error: cannot determine resolution for the attribute macro `test` - --> $DIR/issue-28134.rs:3:4 + --> $DIR/issue-28134.rs:4:4 | LL | #![test] | ^^^^ diff --git a/src/test/ui/proc-macro/proc-macro-gates.rs b/src/test/ui/proc-macro/proc-macro-gates.rs index b3b677fa7ffed..c15b6b5953569 100644 --- a/src/test/ui/proc-macro/proc-macro-gates.rs +++ b/src/test/ui/proc-macro/proc-macro-gates.rs @@ -45,4 +45,9 @@ fn attrs() { //~^ ERROR: custom attributes cannot be applied to expressions } +fn test_case() { + #![test] //~ ERROR non-builtin inner attributes are unstable + //~| WARN this was previously accepted +} + fn main() {} diff --git a/src/test/ui/proc-macro/proc-macro-gates.stderr b/src/test/ui/proc-macro/proc-macro-gates.stderr index c034349553177..65ab917fb4828 100644 --- a/src/test/ui/proc-macro/proc-macro-gates.stderr +++ b/src/test/ui/proc-macro/proc-macro-gates.stderr @@ -76,6 +76,16 @@ LL | let _x = #[identity_attr] println!(); = note: see issue #54727 for more information = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable -error: aborting due to 9 previous errors +error: non-builtin inner attributes are unstable + --> $DIR/proc-macro-gates.rs:49:5 + | +LL | #![test] + | ^^^^^^^^ + | + = note: `#[deny(soft_unstable)]` on by default + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #64266 + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0658`. From 016146d1782f8e41a4870b120dcbfac5fa9c3cb0 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 12 Nov 2020 21:46:54 +0100 Subject: [PATCH 32/46] Add column number support to Backtrace Backtrace frames might include column numbers. Print them if they are included. --- library/std/src/backtrace.rs | 7 +++++-- library/std/src/backtrace/tests.rs | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index a9d8a4e2a81c1..bd0439a7adcdf 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -161,6 +161,7 @@ struct BacktraceSymbol { name: Option>, filename: Option, lineno: Option, + colno: Option, } enum BytesOrWide { @@ -209,7 +210,7 @@ impl fmt::Debug for BacktraceSymbol { write!(fmt, ", file: \"{:?}\"", fname)?; } - if let Some(line) = self.lineno.as_ref() { + if let Some(line) = self.lineno { write!(fmt, ", line: {:?}", line)?; } @@ -381,7 +382,7 @@ impl fmt::Display for Backtrace { f.print_raw(frame.frame.ip(), None, None, None)?; } else { for symbol in frame.symbols.iter() { - f.print_raw( + f.print_raw_with_column( frame.frame.ip(), symbol.name.as_ref().map(|b| backtrace_rs::SymbolName::new(b)), symbol.filename.as_ref().map(|b| match b { @@ -389,6 +390,7 @@ impl fmt::Display for Backtrace { BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), }), symbol.lineno, + symbol.colno, )?; } } @@ -427,6 +429,7 @@ impl Capture { BytesOrWideString::Wide(b) => BytesOrWide::Wide(b.to_owned()), }), lineno: symbol.lineno(), + colno: symbol.colno(), }); }); } diff --git a/library/std/src/backtrace/tests.rs b/library/std/src/backtrace/tests.rs index 287359cd545e3..f5f74d1eb9ae6 100644 --- a/library/std/src/backtrace/tests.rs +++ b/library/std/src/backtrace/tests.rs @@ -13,6 +13,7 @@ fn test_debug() { name: Some(b"std::backtrace::Backtrace::create".to_vec()), filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())), lineno: Some(100), + colno: None, }], }, BacktraceFrame { @@ -21,6 +22,7 @@ fn test_debug() { name: Some(b"__rust_maybe_catch_panic".to_vec()), filename: None, lineno: None, + colno: None, }], }, BacktraceFrame { @@ -30,11 +32,13 @@ fn test_debug() { name: Some(b"std::rt::lang_start_internal".to_vec()), filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), lineno: Some(300), + colno: Some(5), }, BacktraceSymbol { name: Some(b"std::rt::lang_start".to_vec()), filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())), lineno: Some(400), + colno: None, }, ], }, From 31741aad3946511893dbaa2fda1d74918f6a8050 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 12 Nov 2020 16:23:35 -0500 Subject: [PATCH 33/46] Add `--color` support to bootstrap This allows using bootstrap with https://github.com/Canop/bacon. --- src/bootstrap/builder.rs | 12 +++++++++++- src/bootstrap/config.rs | 4 +++- src/bootstrap/flags.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 3d724c1484227..508d785834fce 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -19,7 +19,7 @@ use crate::compile; use crate::config::TargetSelection; use crate::dist; use crate::doc; -use crate::flags::Subcommand; +use crate::flags::{Color, Subcommand}; use crate::install; use crate::native; use crate::run; @@ -811,6 +811,16 @@ impl<'a> Builder<'a> { cargo.env("REAL_LIBRARY_PATH", e); } + match self.build.config.color { + Color::Always => { + cargo.arg("--color=always"); + } + Color::Never => { + cargo.arg("--color=never"); + } + Color::Auto => {} // nothing to do + } + if cmd != "install" { cargo.arg("--target").arg(target.rustc_target_arg()); } else { diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index c0753d88504ae..94319a6d1e9e2 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -13,8 +13,8 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use crate::cache::{Interned, INTERNER}; -use crate::flags::Flags; pub use crate::flags::Subcommand; +use crate::flags::{Color, Flags}; use crate::util::exe; use build_helper::t; use merge::Merge; @@ -67,6 +67,7 @@ pub struct Config { pub json_output: bool, pub test_compare_mode: bool, pub llvm_libunwind: Option, + pub color: Color, pub on_fail: Option, pub stage: u32, @@ -577,6 +578,7 @@ impl Config { config.keep_stage = flags.keep_stage; config.keep_stage_std = flags.keep_stage_std; config.bindir = "bin".into(); // default + config.color = flags.color; if let Some(value) = flags.deny_warnings { config.deny_warnings = value; } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index dbfcf4df9b4be..5a8096674c6da 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -15,6 +15,31 @@ use crate::config::{Config, TargetSelection}; use crate::setup::Profile; use crate::{Build, DocTests}; +pub enum Color { + Always, + Never, + Auto, +} + +impl Default for Color { + fn default() -> Self { + Self::Auto + } +} + +impl std::str::FromStr for Color { + type Err = (); + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "always" => Ok(Self::Always), + "never" => Ok(Self::Never), + "auto" => Ok(Self::Auto), + _ => Err(()), + } + } +} + /// Deserialized version of all flags for this compile. pub struct Flags { pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo @@ -34,6 +59,7 @@ pub struct Flags { pub rustc_error_format: Option, pub json_output: bool, pub dry_run: bool, + pub color: Color, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. @@ -184,6 +210,7 @@ To learn more about a subcommand, run `./x.py -h`", ); opts.optopt("", "error-format", "rustc error format", "FORMAT"); opts.optflag("", "json-output", "use message-format=json"); + opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE"); opts.optopt( "", "llvm-skip-rebuild", @@ -644,6 +671,9 @@ Arguments: llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map( |s| s.parse::().expect("`llvm-skip-rebuild` should be either true or false"), ), + color: matches + .opt_get_default("color", Color::Auto) + .expect("`color` should be `always`, `never`, or `auto`"), } } } From 8766c0452c9ae19a041a926820ae400864be6a9b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 13 Nov 2020 00:35:46 +0300 Subject: [PATCH 34/46] cleanup: Remove `ParseSess::injected_crate_name` --- compiler/rustc_ast_pretty/src/pprust/state.rs | 3 +-- .../rustc_builtin_macros/src/standard_library_imports.rs | 6 +++--- compiler/rustc_driver/src/pretty.rs | 2 -- compiler/rustc_interface/src/passes.rs | 8 ++------ compiler/rustc_session/src/parse.rs | 4 +--- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a64014f5acbb8..d3de2537ae776 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -109,7 +109,6 @@ pub fn print_crate<'a>( ann: &'a dyn PpAnn, is_expanded: bool, edition: Edition, - has_injected_crate: bool, ) -> String { let mut s = State { s: pp::mk_printer(), @@ -119,7 +118,7 @@ pub fn print_crate<'a>( insert_extra_parens: true, }; - if is_expanded && has_injected_crate { + if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) { // We need to print `#![no_std]` (and its feature gate) so that // compiling pretty-printed source won't inject libstd again. // However, we don't want these attributes in the AST because diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index e801b5c7b0c62..91566ec1ef245 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -13,12 +13,12 @@ pub fn inject( resolver: &mut dyn ResolverExpand, sess: &Session, alt_std_name: Option, -) -> (ast::Crate, Option) { +) -> ast::Crate { let rust_2018 = sess.parse_sess.edition >= Edition::Edition2018; // the first name in this list is the crate name of the crate with the prelude let names: &[Symbol] = if sess.contains_name(&krate.attrs, sym::no_core) { - return (krate, None); + return krate; } else if sess.contains_name(&krate.attrs, sym::no_std) { if sess.contains_name(&krate.attrs, sym::compiler_builtins) { &[sym::core] @@ -81,5 +81,5 @@ pub fn inject( krate.module.items.insert(0, use_item); - (krate, Some(name)) + krate } diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index b0fbf1e03f5af..5b35754a95148 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -407,7 +407,6 @@ pub fn print_after_parsing( annotation.pp_ann(), false, parse.edition, - parse.injected_crate_name.get().is_some(), ) }) } else { @@ -449,7 +448,6 @@ pub fn print_after_hir_lowering<'tcx>( annotation.pp_ann(), true, parse.edition, - parse.injected_crate_name.get().is_some(), ) }) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 548b6c03daa7e..f6af4b7d8a44a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -239,16 +239,12 @@ fn configure_and_expand_inner<'a>( krate = sess.time("crate_injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s)); - let (krate, name) = rustc_builtin_macros::standard_library_imports::inject( + rustc_builtin_macros::standard_library_imports::inject( krate, &mut resolver, &sess, alt_std_name, - ); - if let Some(name) = name { - sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized"); - } - krate + ) }); util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer()); diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 6f10d0c4b89ea..5b2a9a341a00b 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -4,7 +4,7 @@ use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId}; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{Lock, Lrc, OnceCell}; +use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; use rustc_errors::{error_code, Applicability, DiagnosticBuilder}; use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures}; @@ -129,7 +129,6 @@ pub struct ParseSess { /// operation token that followed it, but that the parser cannot identify without further /// analysis. pub ambiguous_block_expr_parse: Lock>, - pub injected_crate_name: OnceCell, pub gated_spans: GatedSpans, pub symbol_gallery: SymbolGallery, /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors. @@ -158,7 +157,6 @@ impl ParseSess { source_map, buffered_lints: Lock::new(vec![]), ambiguous_block_expr_parse: Lock::new(FxHashMap::default()), - injected_crate_name: OnceCell::new(), gated_spans: GatedSpans::default(), symbol_gallery: SymbolGallery::default(), reached_eof: Lock::new(false), From c131063988ff8ac86bbf53a3bf998145c6b99d6f Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Thu, 12 Nov 2020 16:08:42 -0800 Subject: [PATCH 35/46] Added a unit test for BcbCounters Restructured the code a little, to allow getting both the mir::Body and coverage graph. --- .../src/transform/coverage/counters.rs | 1 - .../rustc_mir/src/transform/coverage/spans.rs | 10 +- .../rustc_mir/src/transform/coverage/tests.rs | 279 +++++++++++------- 3 files changed, 188 insertions(+), 102 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs index 7c4b30ca9e790..20f6a16e0f757 100644 --- a/compiler/rustc_mir/src/transform/coverage/counters.rs +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -120,7 +120,6 @@ struct BcbCounters<'a> { basic_coverage_blocks: &'a mut CoverageGraph, } -// FIXME(richkadel): Add unit tests for `BcbCounters` functions/algorithms. impl<'a> BcbCounters<'a> { fn new( coverage_counters: &'a mut CoverageCounters, diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs index f880d69bd64f6..95c49922262f6 100644 --- a/compiler/rustc_mir/src/transform/coverage/spans.rs +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -645,7 +645,10 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { } } -fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_statement_span( + statement: &'a Statement<'tcx>, + body_span: Span, +) -> Option { match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. @@ -686,7 +689,10 @@ fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> O } } -fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { +pub(super) fn filtered_terminator_span( + terminator: &'a Terminator<'tcx>, + body_span: Span, +) -> Option { match terminator.kind { // These terminators have spans that don't positively contribute to computing a reasonable // span of actually executed source code. (For example, SwitchInt terminators extracted from diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 3e305a58c6bb2..7de965be53bde 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -1,14 +1,39 @@ +//! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR +//! pass. +//! +//! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage` +//! functions and algorithms. Mocked objects include instances of `mir::Body`; including +//! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on +//! real, runtime versions of these mocked-up objects have constraints (such as cross-thread +//! limitations) and deep dependencies on other elements of the full Rust compiler (which is +//! *not* constructed or mocked for these tests). +//! +//! Of particular note, attempting to simply print elements of the `mir::Body` with default +//! `Debug` formatting can fail because some `Debug` format implementations require the +//! `TyCtxt`, obtained via a static global variable that is *not* set for these tests. +//! Initializing the global type context is prohibitively complex for the scope and scale of these +//! tests (essentially requiring initializing the entire compiler). +//! +//! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which +//! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some +//! basic, coverage-specific features would be impossible to test, but thankfully initializing these +//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument +//! to: `rustc_span::with_default_session_globals(|| { test_here(); })`. + +use super::counters; use super::debug; use super::graph; +use super::spans; use coverage_test_macros::let_bcb; use rustc_data_structures::graph::WithNumNodes; use rustc_data_structures::graph::WithSuccessors; use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::*; use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; -use rustc_span::DUMMY_SP; +use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; fn dummy_ty() -> &'static TyS<'static> { thread_local! { @@ -24,7 +49,6 @@ fn dummy_ty() -> &'static TyS<'static> { struct MockBlocks<'tcx> { blocks: IndexVec>, - source_info: SourceInfo, dummy_place: Place<'tcx>, next_local: usize, } @@ -33,7 +57,6 @@ impl<'tcx> MockBlocks<'tcx> { fn new() -> Self { Self { blocks: IndexVec::new(), - source_info: SourceInfo::outermost(DUMMY_SP), dummy_place: Place { local: RETURN_PLACE, projection: ty::List::empty() }, next_local: 0, } @@ -45,12 +68,19 @@ impl<'tcx> MockBlocks<'tcx> { Local::new(index) } - fn push(&mut self, num_nops: usize, kind: TerminatorKind<'tcx>) -> BasicBlock { - let nop = Statement { source_info: self.source_info, kind: StatementKind::Nop }; - + fn push(&mut self, kind: TerminatorKind<'tcx>) -> BasicBlock { + let next_lo = if let Some(last) = self.blocks.last() { + self.blocks[last].terminator().source_info.span.hi() + } else { + BytePos(1) + }; + let next_hi = next_lo + BytePos(1); self.blocks.push(BasicBlockData { - statements: std::iter::repeat(&nop).cloned().take(num_nops).collect(), - terminator: Some(Terminator { source_info: self.source_info, kind }), + statements: vec![], + terminator: Some(Terminator { + source_info: SourceInfo::outermost(Span::with_root_ctxt(next_lo, next_hi)), + kind, + }), is_cleanup: false, }) } @@ -75,7 +105,7 @@ impl<'tcx> MockBlocks<'tcx> { some_from_block: Option, to_kind: TerminatorKind<'tcx>, ) -> BasicBlock { - let new_block = self.push(1, to_kind); + let new_block = self.push(to_kind); if let Some(from_block) = some_from_block { self.link(from_block, new_block); } @@ -152,7 +182,10 @@ fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { .basic_blocks() .iter_enumerated() .map(|(bb, data)| { - let kind = &data.terminator().kind; + let term = &data.terminator(); + let kind = &term.kind; + let span = term.source_info.span; + let sp = format!("(span:{},{})", span.lo().to_u32(), span.hi().to_u32()); match kind { TerminatorKind::Assert { target, .. } | TerminatorKind::Call { destination: Some((_, target)), .. } @@ -163,12 +196,12 @@ fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String { | TerminatorKind::Goto { target } | TerminatorKind::InlineAsm { destination: Some(target), .. } | TerminatorKind::Yield { resume: target, .. } => { - format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), target) + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), target) } TerminatorKind::SwitchInt { targets, .. } => { - format!("{:?}:{} -> {:?}", bb, debug::term_type(kind), targets) + format!("{}{:?}:{} -> {:?}", sp, bb, debug::term_type(kind), targets) } - _ => format!("{:?}:{}", bb, debug::term_type(kind)), + _ => format!("{}{:?}:{}", sp, bb, debug::term_type(kind)), } }) .collect::>() @@ -235,7 +268,7 @@ fn print_coverage_graphviz( } /// Create a mock `Body` with a simple flow. -fn mir_goto_switchint() -> Body<'a> { +fn goto_switchint() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let goto = blocks.goto(Some(start)); @@ -281,13 +314,22 @@ fn mir_goto_switchint() -> Body<'a> { mir_body } -fn covgraph_goto_switchint() -> graph::CoverageGraph { - let mir_body = mir_goto_switchint(); +macro_rules! assert_successors { + ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { + let mut successors = $basic_coverage_blocks.successors[$i].clone(); + successors.sort_unstable(); + assert_eq!(successors, vec![$($successor),*]); + } +} + +#[test] +fn test_covgraph_goto_switchint() { + let mir_body = goto_switchint(); if false { println!("basic_blocks = {}", debug_basic_blocks(&mir_body)); } - let covgraph = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &covgraph); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz("covgraph_goto_switchint ", &mir_body, &basic_coverage_blocks); /* ┌──────────────┐ ┌─────────────────┐ │ bcb2: Return │ ◀── │ bcb0: SwitchInt │ @@ -299,11 +341,24 @@ fn covgraph_goto_switchint() -> graph::CoverageGraph { │ bcb1: Return │ └─────────────────┘ */ - covgraph + assert_eq!( + basic_coverage_blocks.num_nodes(), + 3, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); + assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb2, []); } /// Create a mock `Body` with a loop. -fn mir_switchint_then_loop_else_return() -> Body<'a> { +fn switchint_then_loop_else_return() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let switchint = blocks.switchint(Some(start)); @@ -342,10 +397,15 @@ fn mir_switchint_then_loop_else_return() -> Body<'a> { mir_body } -fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { - let mir_body = mir_switchint_then_loop_else_return(); - let covgraph = graph::CoverageGraph::from_mir(&mir_body); - print_coverage_graphviz("covgraph_switchint_then_loop_else_return", &mir_body, &covgraph); +#[test] +fn test_covgraph_switchint_then_loop_else_return() { + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + print_coverage_graphviz( + "covgraph_switchint_then_loop_else_return", + &mir_body, + &basic_coverage_blocks, + ); /* ┌─────────────────┐ │ bcb0: Call │ @@ -365,11 +425,26 @@ fn covgraph_switchint_then_loop_else_return() -> graph::CoverageGraph { │ │ └─────────────────────────────────────┘ */ - covgraph + assert_eq!( + basic_coverage_blocks.num_nodes(), + 4, + "basic_coverage_blocks: {:?}", + basic_coverage_blocks.iter_enumerated().collect::>() + ); + + let_bcb!(0); + let_bcb!(1); + let_bcb!(2); + let_bcb!(3); + + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); + assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); } /// Create a mock `Body` with nested loops. -fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { +fn switchint_loop_then_inner_loop_else_break() -> Body<'a> { let mut blocks = MockBlocks::new(); let start = blocks.call(None); let switchint = blocks.switchint(Some(start)); @@ -438,13 +513,14 @@ fn mir_switchint_loop_then_inner_loop_else_break() -> Body<'a> { mir_body } -fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph { - let mir_body = mir_switchint_loop_then_inner_loop_else_break(); - let covgraph = graph::CoverageGraph::from_mir(&mir_body); +#[test] +fn test_covgraph_switchint_loop_then_inner_loop_else_break() { + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); print_coverage_graphviz( "covgraph_switchint_loop_then_inner_loop_else_break", &mir_body, - &covgraph, + &basic_coverage_blocks, ); /* ┌─────────────────┐ @@ -477,23 +553,9 @@ fn covgraph_switchint_loop_then_inner_loop_else_break() -> graph::CoverageGraph │ │ └────────────────────────────────────────────┘ */ - covgraph -} - -macro_rules! assert_successors { - ($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => { - let mut successors = $basic_coverage_blocks.successors[$i].clone(); - successors.sort_unstable(); - assert_eq!(successors, vec![$($successor),*]); - } -} - -#[test] -fn test_covgraph_goto_switchint() { - let basic_coverage_blocks = covgraph_goto_switchint(); assert_eq!( basic_coverage_blocks.num_nodes(), - 3, + 7, "basic_coverage_blocks: {:?}", basic_coverage_blocks.iter_enumerated().collect::>() ); @@ -501,15 +563,24 @@ fn test_covgraph_goto_switchint() { let_bcb!(0); let_bcb!(1); let_bcb!(2); + let_bcb!(3); + let_bcb!(4); + let_bcb!(5); + let_bcb!(6); - assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]); - assert_successors!(basic_coverage_blocks, bcb1, []); + assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); assert_successors!(basic_coverage_blocks, bcb2, []); + assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); + assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); + assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); + assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); } #[test] fn test_find_loop_backedges_none() { - let basic_coverage_blocks = covgraph_goto_switchint(); + let mir_body = goto_switchint(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); if false { println!( "basic_coverage_blocks = {:?}", @@ -526,30 +597,10 @@ fn test_find_loop_backedges_none() { ); } -#[test] -fn test_covgraph_switchint_then_loop_else_return() { - let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); - assert_eq!( - basic_coverage_blocks.num_nodes(), - 4, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::>() - ); - - let_bcb!(0); - let_bcb!(1); - let_bcb!(2); - let_bcb!(3); - - assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); - assert_successors!(basic_coverage_blocks, bcb2, []); - assert_successors!(basic_coverage_blocks, bcb3, [bcb1]); -} - #[test] fn test_find_loop_backedges_one() { - let basic_coverage_blocks = covgraph_switchint_then_loop_else_return(); + let mir_body = switchint_then_loop_else_return(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let backedges = graph::find_loop_backedges(&basic_coverage_blocks); assert_eq!( backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), @@ -564,36 +615,10 @@ fn test_find_loop_backedges_one() { assert_eq!(backedges[bcb1], vec![bcb3]); } -#[test] -fn test_covgraph_switchint_loop_then_inner_loop_else_break() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); - assert_eq!( - basic_coverage_blocks.num_nodes(), - 7, - "basic_coverage_blocks: {:?}", - basic_coverage_blocks.iter_enumerated().collect::>() - ); - - let_bcb!(0); - let_bcb!(1); - let_bcb!(2); - let_bcb!(3); - let_bcb!(4); - let_bcb!(5); - let_bcb!(6); - - assert_successors!(basic_coverage_blocks, bcb0, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]); - assert_successors!(basic_coverage_blocks, bcb2, []); - assert_successors!(basic_coverage_blocks, bcb3, [bcb4]); - assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]); - assert_successors!(basic_coverage_blocks, bcb5, [bcb1]); - assert_successors!(basic_coverage_blocks, bcb6, [bcb4]); -} - #[test] fn test_find_loop_backedges_two() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let backedges = graph::find_loop_backedges(&basic_coverage_blocks); assert_eq!( backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::(), @@ -613,7 +638,8 @@ fn test_find_loop_backedges_two() { #[test] fn test_traverse_coverage_with_loops() { - let basic_coverage_blocks = covgraph_switchint_loop_then_inner_loop_else_break(); + let mir_body = switchint_loop_then_inner_loop_else_break(); + let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); let mut traversed_in_order = Vec::new(); let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks); while let Some(bcb) = traversal.next(&basic_coverage_blocks) { @@ -630,3 +656,58 @@ fn test_traverse_coverage_with_loops() { "bcb6 should not be visited until all nodes inside the first loop have been visited" ); } + +fn synthesize_body_span_from_terminators(mir_body: &Body<'_>) -> Span { + let mut some_span: Option = None; + for (_, data) in mir_body.basic_blocks().iter_enumerated() { + let term_span = data.terminator().source_info.span; + if let Some(span) = some_span.as_mut() { + *span = span.to(term_span); + } else { + some_span = Some(term_span) + } + } + some_span.expect("body must have at least one BasicBlock") +} + +#[test] +fn test_make_bcb_counters() { + rustc_span::with_default_session_globals(|| { + let mir_body = goto_switchint(); + let body_span = synthesize_body_span_from_terminators(&mir_body); + let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body); + let mut coverage_spans = Vec::new(); + for (bcb, data) in basic_coverage_blocks.iter_enumerated() { + if let Some(span) = + spans::filtered_terminator_span(data.terminator(&mir_body), body_span) + { + coverage_spans.push(spans::CoverageSpan::for_terminator(span, bcb, data.last_bb())); + } + } + let mut coverage_counters = counters::CoverageCounters::new(0); + let intermediate_expressions = coverage_counters + .make_bcb_counters(&mut basic_coverage_blocks, &coverage_spans) + .expect("should be Ok"); + assert_eq!(intermediate_expressions.len(), 0); + + let_bcb!(1); + assert_eq!( + 1, // coincidentally, bcb1 has a `Counter` with id = 1 + match basic_coverage_blocks[bcb1].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + + let_bcb!(2); + assert_eq!( + 2, // coincidentally, bcb2 has a `Counter` with id = 2 + match basic_coverage_blocks[bcb2].counter().expect("should have a counter") { + CoverageKind::Counter { id, .. } => id, + _ => panic!("expected a Counter"), + } + .as_u32() + ); + }); +} From 309d863e372227f9a3eed89e46cba0ff3b1ea8c8 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Fri, 13 Nov 2020 14:58:21 +0100 Subject: [PATCH 36/46] Fix wrong XPath --- src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs index 0c047e6249a53..f895a4c2104ba 100644 --- a/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs +++ b/src/test/rustdoc/raw-ident-eliminate-r-hashtag.rs @@ -1,5 +1,7 @@ // ignore-tidy-linelength +#![crate_type="lib"] + pub mod internal { // @has 'raw_ident_eliminate_r_hashtag/internal/struct.mod.html' pub struct r#mod; @@ -8,13 +10,13 @@ pub mod internal { /// /// [name]: mod /// [other name]: crate::internal::mod - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' - // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' + // @has 'raw_ident_eliminate_r_hashtag/internal/struct.B.html' '//*a[@href="../../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'other name' pub struct B; } /// See [name]. /// /// [name]: internal::mod -// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' -struct A; +// @has 'raw_ident_eliminate_r_hashtag/struct.A.html' '//*a[@href="../raw_ident_eliminate_r_hashtag/internal/struct.mod.html"]' 'name' +pub struct A; From 894b1f7d3d508bb644e01c62dfb983a394380fd3 Mon Sep 17 00:00:00 2001 From: Bastian Kauschke Date: Fri, 13 Nov 2020 15:49:17 +0100 Subject: [PATCH 37/46] extract closures into a separate trait --- compiler/rustc_typeck/src/astconv/generics.rs | 25 +- compiler/rustc_typeck/src/astconv/mod.rs | 224 +++++++++++------- .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 147 +++++++----- .../rustc_typeck/src/check/method/confirm.rs | 74 ++++-- 4 files changed, 292 insertions(+), 178 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 3bfb2d3f1b0f9..301ede66006de 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -1,12 +1,13 @@ use crate::astconv::{ - AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, GenericArgPosition, }; use crate::errors::AssocTypeBindingNotAllowed; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{GenericArg, GenericArgs}; +use rustc_hir::GenericArg; use rustc_middle::ty::{ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, }; @@ -90,20 +91,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// instantiate a `GenericArg`. /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then /// creates a suitable inference variable. - pub fn create_substs_for_generic_args<'b>( + pub fn create_substs_for_generic_args<'a>( tcx: TyCtxt<'tcx>, def_id: DefId, parent_substs: &[subst::GenericArg<'tcx>], has_self: bool, self_ty: Option>, arg_count: GenericArgCountResult, - args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), - mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, - mut inferred_kind: impl FnMut( - Option<&[subst::GenericArg<'tcx>]>, - &GenericParamDef, - bool, - ) -> subst::GenericArg<'tcx>, + ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, ) -> SubstsRef<'tcx> { // Collect the segments of the path; we need to substitute arguments // for parameters throughout the entire path (wherever there are @@ -142,7 +137,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { substs.push( self_ty .map(|ty| ty.into()) - .unwrap_or_else(|| inferred_kind(None, param, true)), + .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), ); params.next(); } @@ -151,7 +146,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } // Check whether this segment takes generic arguments and the user has provided any. - let (generic_args, infer_args) = args_for_def_id(def_id); + let (generic_args, infer_args) = ctx.args_for_def_id(def_id); let mut args = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); @@ -173,7 +168,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _) | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { - substs.push(provided_kind(param, arg)); + substs.push(ctx.provided_kind(param, arg)); args.next(); params.next(); } @@ -184,7 +179,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ) => { // We expected a lifetime argument, but got a type or const // argument. That means we're inferring the lifetimes. - substs.push(inferred_kind(None, param, infer_args)); + substs.push(ctx.inferred_kind(None, param, infer_args)); force_infer_lt = Some(arg); params.next(); } @@ -302,7 +297,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (None, Some(¶m)) => { // If there are fewer arguments than parameters, it means // we're inferring the remaining arguments. - substs.push(inferred_kind(Some(&substs), param, infer_args)); + substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); params.next(); } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 07e523af3ebf5..89c5adfa14c67 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -165,6 +165,23 @@ pub struct GenericArgCountResult { pub correct: Result<(), GenericArgCountMismatch>, } +pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> { + fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool); + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx>; + + fn inferred_kind( + &mut self, + substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> subst::GenericArg<'tcx>; +} + impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { pub fn ast_region_to_region( &self, @@ -321,81 +338,102 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self); - let default_needs_object_self = |param: &ty::GenericParamDef| { - if let GenericParamDefKind::Type { has_default, .. } = param.kind { - if is_object && has_default { - let default_ty = tcx.at(span).type_of(param.def_id); - let self_param = tcx.types.self_param; - if default_ty.walk().any(|arg| arg == self_param.into()) { - // There is no suitable inference default for a type parameter - // that references self, in an object type. - return true; + + struct SubstsForAstPathCtxt<'a, 'tcx> { + astconv: &'a (dyn AstConv<'tcx> + 'a), + def_id: DefId, + generic_args: &'a GenericArgs<'a>, + span: Span, + missing_type_params: Vec, + inferred_params: Vec, + infer_args: bool, + is_object: bool, + } + + impl<'tcx, 'a> SubstsForAstPathCtxt<'tcx, 'a> { + fn default_needs_object_self(&mut self, param: &ty::GenericParamDef) -> bool { + let tcx = self.astconv.tcx(); + if let GenericParamDefKind::Type { has_default, .. } = param.kind { + if self.is_object && has_default { + let default_ty = tcx.at(self.span).type_of(param.def_id); + let self_param = tcx.types.self_param; + if default_ty.walk().any(|arg| arg == self_param.into()) { + // There is no suitable inference default for a type parameter + // that references self, in an object type. + return true; + } } } - } - false - }; + false + } + } - let mut missing_type_params = vec![]; - let mut inferred_params = vec![]; - let substs = Self::create_substs_for_generic_args( - tcx, - def_id, - parent_substs, - self_ty.is_some(), - self_ty, - arg_count.clone(), - // Provide the generic args, and whether types should be inferred. - |did| { - if did == def_id { - (Some(generic_args), infer_args) + impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> { + fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) { + if did == self.def_id { + (Some(self.generic_args), self.infer_args) } else { // The last component of this tuple is unimportant. (None, false) } - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.ast_region_to_region(<, Some(param)).into() - } - (GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { - if *has_default { - tcx.check_optional_stability( - param.def_id, - Some(arg.id()), - arg.span(), - |_, _| { - // Default generic parameters may not be marked - // with stability attributes, i.e. when the - // default parameter was defined at the same time - // as the rest of the type. As such, we ignore missing - // stability attributes. + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx> { + let tcx = self.astconv.tcx(); + match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + self.astconv.ast_region_to_region(<, Some(param)).into() + } + (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { + if has_default { + tcx.check_optional_stability( + param.def_id, + Some(arg.id()), + arg.span(), + |_, _| { + // Default generic parameters may not be marked + // with stability attributes, i.e. when the + // default parameter was defined at the same time + // as the rest of the type. As such, we ignore missing + // stability attributes. + }, + ) + } + if let (hir::TyKind::Infer, false) = + (&ty.kind, self.astconv.allow_ty_infer()) + { + self.inferred_params.push(ty.span); + tcx.ty_error().into() + } else { + self.astconv.ast_ty_to_ty(&ty).into() + } + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + ty::Const::from_opt_const_arg_anon_const( + tcx, + ty::WithOptConstParam { + did: tcx.hir().local_def_id(ct.value.hir_id), + const_param_did: Some(param.def_id), }, ) + .into() } - if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) { - inferred_params.push(ty.span); - tcx.ty_error().into() - } else { - self.ast_ty_to_ty(&ty).into() - } - } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - ty::Const::from_opt_const_arg_anon_const( - tcx, - ty::WithOptConstParam { - did: tcx.hir().local_def_id(ct.value.hir_id), - const_param_did: Some(param.def_id), - }, - ) - .into() + _ => unreachable!(), } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { + } + + fn inferred_kind( + &mut self, + substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> subst::GenericArg<'tcx> { + let tcx = self.astconv.tcx(); match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), GenericParamDefKind::Type { has_default, .. } => { @@ -407,48 +445,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // other type parameters may reference `Self` in their // defaults. This will lead to an ICE if we are not // careful! - if default_needs_object_self(param) { - missing_type_params.push(param.name.to_string()); + if self.default_needs_object_self(param) { + self.missing_type_params.push(param.name.to_string()); tcx.ty_error().into() } else { // This is a default type parameter. - self.normalize_ty( - span, - tcx.at(span).type_of(param.def_id).subst_spanned( - tcx, - substs.unwrap(), - Some(span), - ), - ) - .into() + self.astconv + .normalize_ty( + self.span, + tcx.at(self.span).type_of(param.def_id).subst_spanned( + tcx, + substs.unwrap(), + Some(self.span), + ), + ) + .into() } } else if infer_args { // No type parameters were provided, we can infer all. - let param = - if !default_needs_object_self(param) { Some(param) } else { None }; - self.ty_infer(param, span).into() + let param = if !self.default_needs_object_self(param) { + Some(param) + } else { + None + }; + self.astconv.ty_infer(param, self.span).into() } else { // We've already errored above about the mismatch. tcx.ty_error().into() } } GenericParamDefKind::Const => { - let ty = tcx.at(span).type_of(param.def_id); + let ty = tcx.at(self.span).type_of(param.def_id); // FIXME(const_generics:defaults) if infer_args { // No const parameters were provided, we can infer all. - self.ct_infer(ty, Some(param), span).into() + self.astconv.ct_infer(ty, Some(param), self.span).into() } else { // We've already errored above about the mismatch. tcx.const_error(ty).into() } } } - }, + } + } + + let mut substs_ctx = SubstsForAstPathCtxt { + astconv: self, + def_id, + span, + generic_args, + missing_type_params: vec![], + inferred_params: vec![], + infer_args, + is_object, + }; + let substs = Self::create_substs_for_generic_args( + tcx, + def_id, + parent_substs, + self_ty.is_some(), + self_ty, + arg_count.clone(), + &mut substs_ctx, ); self.complain_about_missing_type_params( - missing_type_params, + substs_ctx.missing_type_params, def_id, span, generic_args.args.is_empty(), diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 0bb7b464f167b..300ee8f85f8dd 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1,5 +1,6 @@ use crate::astconv::{ - AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, PathSeg, }; use crate::check::callee::{self, DeferredCallResolution}; use crate::check::method::{self, MethodCallee, SelfSource}; @@ -1298,76 +1299,108 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; - let substs = self_ctor_substs.unwrap_or_else(|| { - AstConv::create_substs_for_generic_args( - tcx, - def_id, - &[][..], - has_self, - self_ty, - arg_count, - // Provide the generic args, and whether types should be inferred. - |def_id| { - if let Some(&PathSeg(_, index)) = - path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) - { - // If we've encountered an `impl Trait`-related error, we're just - // going to infer the arguments for better error messages. - if !infer_args_for_err.contains(&index) { - // Check whether the user has provided generic arguments. - if let Some(ref data) = segments[index].args { - return (Some(data), segments[index].infer_args); - } + struct CreateCtorSubstsContext<'a, 'tcx> { + fcx: &'a FnCtxt<'a, 'tcx>, + span: Span, + path_segs: &'a [PathSeg], + infer_args_for_err: &'a FxHashSet, + segments: &'a [hir::PathSegment<'a>], + } + impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> { + fn args_for_def_id( + &mut self, + def_id: DefId, + ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { + if let Some(&PathSeg(_, index)) = + self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + { + // If we've encountered an `impl Trait`-related error, we're just + // going to infer the arguments for better error messages. + if !self.infer_args_for_err.contains(&index) { + // Check whether the user has provided generic arguments. + if let Some(ref data) = self.segments[index].args { + return (Some(data), self.segments[index].infer_args); } - return (None, segments[index].infer_args); } + return (None, self.segments[index].infer_args); + } - (None, true) - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { + (None, true) + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx> { + match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self, lt, Some(param)).into() + AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.to_ty(ty).into() + self.fcx.to_ty(ty).into() } (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.const_arg_to_const(&ct.value, param.def_id).into() + self.fcx.const_arg_to_const(&ct.value, param.def_id).into() } _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { - match param.kind { - GenericParamDefKind::Lifetime => { - self.re_infer(Some(param), span).unwrap().into() - } - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - let default = tcx.type_of(param.def_id); - self.normalize_ty( - span, - default.subst_spanned(tcx, substs.unwrap(), Some(span)), + } + } + + fn inferred_kind( + &mut self, + substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + infer_args: bool, + ) -> subst::GenericArg<'tcx> { + let tcx = self.fcx.tcx(); + match param.kind { + GenericParamDefKind::Lifetime => { + self.fcx.re_infer(Some(param), self.span).unwrap().into() + } + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // If we have a default, then we it doesn't matter that we're not + // inferring the type arguments: we provide the default where any + // is missing. + let default = tcx.type_of(param.def_id); + self.fcx + .normalize_ty( + self.span, + default.subst_spanned(tcx, substs.unwrap(), Some(self.span)), ) .into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.var_for_def(span, param) - } - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - // No const parameters were provided, we have to infer them. - self.var_for_def(span, param) + } else { + // If no type arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g. + // a lifetime argument being given instead of a type parameter. + // Using inference instead of `Error` gives better error messages. + self.fcx.var_for_def(self.span, param) } } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + // No const parameters were provided, we have to infer them. + self.fcx.var_for_def(self.span, param) + } + } + } + } + + let substs = self_ctor_substs.unwrap_or_else(|| { + AstConv::create_substs_for_generic_args( + tcx, + def_id, + &[][..], + has_self, + self_ty, + arg_count, + &mut CreateCtorSubstsContext { + fcx: self, + span, + path_segs: &path_segs, + infer_args_for_err: &infer_args_for_err, + segments, }, ) }); diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index fd2700b85e279..fb048abc0e9cd 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -1,6 +1,6 @@ use super::{probe, MethodCallee}; -use crate::astconv::AstConv; +use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt}; use crate::check::{callee, FnCtxt}; use crate::hir::def_id::DefId; use crate::hir::GenericArg; @@ -10,7 +10,7 @@ use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, Subst, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; @@ -307,6 +307,52 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // parameters from the type and those from the method. assert_eq!(generics.parent_count, parent_substs.len()); + struct MethodSubstsCtxt<'a, 'tcx> { + cfcx: &'a ConfirmContext<'a, 'tcx>, + pick: &'a probe::Pick<'tcx>, + seg: &'a hir::PathSegment<'a>, + } + impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> { + fn args_for_def_id( + &mut self, + def_id: DefId, + ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { + if def_id == self.pick.item.def_id { + if let Some(ref data) = self.seg.args { + return (Some(data), false); + } + } + (None, false) + } + + fn provided_kind( + &mut self, + param: &ty::GenericParamDef, + arg: &GenericArg<'_>, + ) -> subst::GenericArg<'tcx> { + match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + AstConv::ast_region_to_region(self.cfcx.fcx, lt, Some(param)).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + self.cfcx.to_ty(ty).into() + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() + } + _ => unreachable!(), + } + } + + fn inferred_kind( + &mut self, + _substs: Option<&[subst::GenericArg<'tcx>]>, + param: &ty::GenericParamDef, + _infer_args: bool, + ) -> subst::GenericArg<'tcx> { + self.cfcx.var_for_def(self.cfcx.span, param) + } + } AstConv::create_substs_for_generic_args( self.tcx, pick.item.def_id, @@ -314,29 +360,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { false, None, arg_count_correct, - // Provide the generic args, and whether types should be inferred. - |def_id| { - // The last component of the returned tuple here is unimportant. - if def_id == pick.item.def_id { - if let Some(ref data) = seg.args { - return (Some(data), false); - } - } - (None, false) - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => self.to_ty(ty).into(), - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.const_arg_to_const(&ct.value, param.def_id).into() - } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |_, param, _| self.var_for_def(self.span, param), + &mut MethodSubstsCtxt { cfcx: self, pick, seg }, ) } From b4b0ef3e4b6ebf1571b2c38b952c940c8b94e91e Mon Sep 17 00:00:00 2001 From: Rich Kadel Date: Fri, 13 Nov 2020 09:02:25 -0800 Subject: [PATCH 38/46] Addressed feedback --- compiler/rustc_mir/src/transform/coverage/mod.rs | 2 +- .../src/transform/coverage/test_macros/src/lib.rs | 4 +--- .../rustc_mir/src/transform/coverage/tests.rs | 15 ++++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs index de37a67a1742f..192bb6680e420 100644 --- a/compiler/rustc_mir/src/transform/coverage/mod.rs +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -34,7 +34,7 @@ use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; /// A simple error message wrapper for `coverage::Error`s. #[derive(Debug)] -pub(self) struct Error { +struct Error { message: String, } diff --git a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs index ea551c7745556..3d6095d2738cb 100644 --- a/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs +++ b/compiler/rustc_mir/src/transform/coverage/test_macros/src/lib.rs @@ -2,7 +2,5 @@ use proc_macro::TokenStream; #[proc_macro] pub fn let_bcb(item: TokenStream) -> TokenStream { - format!("let bcb{} = graph::BasicCoverageBlock::from_usize({}); let _ = {};", item, item, item) - .parse() - .unwrap() + format!("let bcb{} = graph::BasicCoverageBlock::from_usize({});", item, item).parse().unwrap() } diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs index 7de965be53bde..d36f1b8e5f670 100644 --- a/compiler/rustc_mir/src/transform/coverage/tests.rs +++ b/compiler/rustc_mir/src/transform/coverage/tests.rs @@ -35,6 +35,9 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags}; use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP}; +// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`. +const TEMP_BLOCK: BasicBlock = BasicBlock::MAX; + fn dummy_ty() -> &'static TyS<'static> { thread_local! { static DUMMY_TYS: &'static TyS<'static> = Box::leak(box TyS::make_for_test( @@ -123,7 +126,7 @@ impl<'tcx> MockBlocks<'tcx> { if branch_index > branches.len() { branches.push((branches.len() as u128, old_otherwise)); while branches.len() < branch_index { - branches.push((branches.len() as u128, START_BLOCK)); + branches.push((branches.len() as u128, TEMP_BLOCK)); } to_block } else { @@ -143,7 +146,7 @@ impl<'tcx> MockBlocks<'tcx> { TerminatorKind::Call { func: Operand::Copy(self.dummy_place.clone()), args: vec![], - destination: Some((self.dummy_place.clone(), START_BLOCK)), + destination: Some((self.dummy_place.clone(), TEMP_BLOCK)), cleanup: None, from_hir_call: false, fn_span: DUMMY_SP, @@ -152,16 +155,14 @@ impl<'tcx> MockBlocks<'tcx> { } fn goto(&mut self, some_from_block: Option) -> BasicBlock { - self.add_block_from(some_from_block, TerminatorKind::Goto { target: START_BLOCK }) + self.add_block_from(some_from_block, TerminatorKind::Goto { target: TEMP_BLOCK }) } fn switchint(&mut self, some_from_block: Option) -> BasicBlock { - let move_ = |place: Place<'tcx>| Operand::Move(place); - let discriminant = Place::from(self.new_temp()); let switchint_kind = TerminatorKind::SwitchInt { - discr: move_(discriminant), + discr: Operand::Move(Place::from(self.new_temp())), switch_ty: dummy_ty(), - targets: SwitchTargets::static_if(0, START_BLOCK, START_BLOCK), + targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK), }; self.add_block_from(some_from_block, switchint_kind) } From 75dfc711da035ab8cc29a2dc32644f47e9d16ed8 Mon Sep 17 00:00:00 2001 From: C Date: Fri, 13 Nov 2020 17:56:39 +0000 Subject: [PATCH 39/46] refactor: vec_deque ignore-tidy-filelength commit c547d5fabcd756515afa7263ee5304965bb4c497 Author: C Date: Sat Oct 31 11:22:23 2020 +0000 test: updating ui/hygiene/panic-location.rs expected commit 2af03769c4ffdbbbad75197a1ad0df8c599186be Author: C Date: Sat Oct 31 10:43:30 2020 +0000 fix: documentation unresolved link commit c4b0df361ce27d7392d8016229f2e0265af32086 Author: C Date: Sat Oct 31 02:58:31 2020 +0000 style: compiling with Rust's style guidelines commit bdd2de5f3c09b49a18e3293f2457fcab25557c96 Author: C Date: Sat Oct 31 02:56:31 2020 +0000 refactor: removing ignore-tidy-filelength commit fcc4b3bc41f57244c65ebb8e4efe4cbc9460b5a9 Author: C Date: Sat Oct 31 02:51:35 2020 +0000 refactor: moving trait RingSlices to ring_slices.rs commit 2f0cc539c06d8841baf7f675168f68ca7c21e68e Author: C Date: Sat Oct 31 02:46:09 2020 +0000 refactor: moving struct PairSlices to pair_slices.rs commit a55d3ef1dab4c3d85962b3a601ff8d1f7497faf2 Author: C Date: Sat Oct 31 02:31:45 2020 +0000 refactor: moving struct Iter to iter.rs commit 76ab33a12442a03726f36f606b4e0fe70f8f246b Author: C Date: Sat Oct 31 02:24:32 2020 +0000 refactor: moving struct IntoIter into into_iter.rs commit abe0d9eea2933881858c3b1bc09df67cedc5ada5 Author: C Date: Sat Oct 31 02:19:07 2020 +0000 refactor: moving struct IterMut into iter_mut.rs commit 70ebd6420335e1895e2afa2763a0148897963e24 Author: C Date: Sat Oct 31 01:49:15 2020 +0000 refactor: moved macros into macros.rs commit b08dd2add994b04ae851aa065800bd8bd6326134 Author: C Date: Sat Oct 31 01:05:36 2020 +0000 refactor: moving vec_deque.rs to vec_deque/mod.rs --- .../src/collections/vec_deque/into_iter.rs | 57 ++ .../alloc/src/collections/vec_deque/iter.rs | 159 ++++++ .../src/collections/vec_deque/iter_mut.rs | 128 +++++ .../alloc/src/collections/vec_deque/macros.rs | 19 + .../{vec_deque.rs => vec_deque/mod.rs} | 501 +----------------- .../src/collections/vec_deque/pair_slices.rs | 66 +++ .../src/collections/vec_deque/ring_slices.rs | 56 ++ src/test/ui/hygiene/panic-location.run.stderr | 2 +- 8 files changed, 515 insertions(+), 473 deletions(-) create mode 100644 library/alloc/src/collections/vec_deque/into_iter.rs create mode 100644 library/alloc/src/collections/vec_deque/iter.rs create mode 100644 library/alloc/src/collections/vec_deque/iter_mut.rs create mode 100644 library/alloc/src/collections/vec_deque/macros.rs rename library/alloc/src/collections/{vec_deque.rs => vec_deque/mod.rs} (86%) create mode 100644 library/alloc/src/collections/vec_deque/pair_slices.rs create mode 100644 library/alloc/src/collections/vec_deque/ring_slices.rs diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs new file mode 100644 index 0000000000000..465b058cd98e9 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -0,0 +1,57 @@ +use core::fmt; +use core::iter::FusedIterator; + +use super::VecDeque; + +/// An owning iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`into_iter`] method on [`VecDeque`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: VecDeque::into_iter +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + pub(crate) inner: VecDeque, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoIter").field(&self.inner).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.inner.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.inner.len(); + (len, Some(len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.inner.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs new file mode 100644 index 0000000000000..ad31b991cb6c3 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -0,0 +1,159 @@ +use core::fmt; +use core::iter::FusedIterator; +use core::ops::Try; + +use super::{count, wrap_index, RingSlices}; + +/// An iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`iter`] method on [`super::VecDeque`]. See its +/// documentation for more. +/// +/// [`iter`]: super::VecDeque::iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + pub(crate) ring: &'a [T], + pub(crate) tail: usize, + pub(crate) head: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for Iter<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + f.debug_tuple("Iter").field(&front).field(&back).finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Iter<'_, T> { + fn clone(&self) -> Self { + Iter { ring: self.ring, tail: self.tail, head: self.head } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.tail == self.head { + return None; + } + let tail = self.tail; + self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); + unsafe { Some(self.ring.get_unchecked(tail)) } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = count(self.tail, self.head, self.ring.len()); + (len, Some(len)) + } + + fn fold(self, mut accum: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = front.iter().fold(accum, &mut f); + back.iter().fold(accum, &mut f) + } + + fn try_fold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let (mut iter, final_res); + if self.tail <= self.head { + // single slice self.ring[self.tail..self.head] + iter = self.ring[self.tail..self.head].iter(); + final_res = iter.try_fold(init, &mut f); + } else { + // two slices: self.ring[self.tail..], self.ring[..self.head] + let (front, back) = self.ring.split_at(self.tail); + let mut back_iter = back.iter(); + let res = back_iter.try_fold(init, &mut f); + let len = self.ring.len(); + self.tail = (self.ring.len() - back_iter.len()) & (len - 1); + iter = front[..self.head].iter(); + final_res = iter.try_fold(res?, &mut f); + } + self.tail = self.head - iter.len(); + final_res + } + + fn nth(&mut self, n: usize) -> Option { + if n >= count(self.tail, self.head, self.ring.len()) { + self.tail = self.head; + None + } else { + self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); + self.next() + } + } + + #[inline] + fn last(mut self) -> Option<&'a T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.tail == self.head { + return None; + } + self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); + unsafe { Some(self.ring.get_unchecked(self.head)) } + } + + fn rfold(self, mut accum: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = back.iter().rfold(accum, &mut f); + front.iter().rfold(accum, &mut f) + } + + fn try_rfold(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try, + { + let (mut iter, final_res); + if self.tail <= self.head { + // single slice self.ring[self.tail..self.head] + iter = self.ring[self.tail..self.head].iter(); + final_res = iter.try_rfold(init, &mut f); + } else { + // two slices: self.ring[self.tail..], self.ring[..self.head] + let (front, back) = self.ring.split_at(self.tail); + let mut front_iter = front[..self.head].iter(); + let res = front_iter.try_rfold(init, &mut f); + self.head = front_iter.len(); + iter = back.iter(); + final_res = iter.try_rfold(res?, &mut f); + } + self.head = self.tail + iter.len(); + final_res + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for Iter<'_, T> { + fn is_empty(&self) -> bool { + self.head == self.tail + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for Iter<'_, T> {} diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs new file mode 100644 index 0000000000000..3d0c3094e26cd --- /dev/null +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -0,0 +1,128 @@ +use core::fmt; +use core::iter::FusedIterator; +use core::marker::PhantomData; + +use super::{count, wrap_index, RingSlices}; + +/// A mutable iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`super::VecDeque`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: super::VecDeque::iter_mut +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + // Internal safety invariant: the entire slice is dereferencable. + pub(crate) ring: *mut [T], + pub(crate) tail: usize, + pub(crate) head: usize, + pub(crate) phantom: PhantomData<&'a mut [T]>, +} + +// SAFETY: we do nothing thread-local and there is no interior mutability, +// so the usual structural `Send`/`Sync` apply. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IterMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&*front, &*back) }; + f.debug_tuple("IterMut").field(&front).field(&back).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.tail == self.head { + return None; + } + let tail = self.tail; + self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); + + unsafe { + let elem = self.ring.get_unchecked_mut(tail); + Some(&mut *elem) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = count(self.tail, self.head, self.ring.len()); + (len, Some(len)) + } + + fn fold(self, mut accum: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&mut *front, &mut *back) }; + accum = front.iter_mut().fold(accum, &mut f); + back.iter_mut().fold(accum, &mut f) + } + + fn nth(&mut self, n: usize) -> Option { + if n >= count(self.tail, self.head, self.ring.len()) { + self.tail = self.head; + None + } else { + self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); + self.next() + } + } + + #[inline] + fn last(mut self) -> Option<&'a mut T> { + self.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.tail == self.head { + return None; + } + self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); + + unsafe { + let elem = self.ring.get_unchecked_mut(self.head); + Some(&mut *elem) + } + } + + fn rfold(self, mut accum: Acc, mut f: F) -> Acc + where + F: FnMut(Acc, Self::Item) -> Acc, + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&mut *front, &mut *back) }; + accum = back.iter_mut().rfold(accum, &mut f); + front.iter_mut().rfold(accum, &mut f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IterMut<'_, T> { + fn is_empty(&self) -> bool { + self.head == self.tail + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IterMut<'_, T> {} diff --git a/library/alloc/src/collections/vec_deque/macros.rs b/library/alloc/src/collections/vec_deque/macros.rs new file mode 100644 index 0000000000000..0d59d312cf406 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/macros.rs @@ -0,0 +1,19 @@ +macro_rules! __impl_slice_eq1 { + ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { + #[stable(feature = "vec_deque_partial_eq_slice", since = "1.17.0")] + impl PartialEq<$rhs> for $lhs + where + A: PartialEq, + $($constraints)* + { + fn eq(&self, other: &$rhs) -> bool { + if self.len() != other.len() { + return false; + } + let (sa, sb) = self.as_slices(); + let (oa, ob) = other[..].split_at(sa.len()); + sa == oa && sb == ob + } + } + } +} diff --git a/library/alloc/src/collections/vec_deque.rs b/library/alloc/src/collections/vec_deque/mod.rs similarity index 86% rename from library/alloc/src/collections/vec_deque.rs rename to library/alloc/src/collections/vec_deque/mod.rs index 22b02a4f849b6..1c183858e7a5e 100644 --- a/library/alloc/src/collections/vec_deque.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -7,16 +7,13 @@ #![stable(feature = "rust1", since = "1.0.0")] -// ignore-tidy-filelength - -use core::array; use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{Hash, Hasher}; -use core::iter::{repeat_with, FromIterator, FusedIterator}; +use core::iter::{repeat_with, FromIterator}; use core::marker::PhantomData; -use core::mem::{self, replace, ManuallyDrop}; -use core::ops::{Index, IndexMut, Range, RangeBounds, Try}; +use core::mem::{self, ManuallyDrop}; +use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice; @@ -24,11 +21,37 @@ use crate::collections::TryReserveError; use crate::raw_vec::RawVec; use crate::vec::Vec; +#[macro_use] +mod macros; + #[stable(feature = "drain", since = "1.6.0")] pub use self::drain::Drain; mod drain; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::iter_mut::IterMut; + +mod iter_mut; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::into_iter::IntoIter; + +mod into_iter; + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::iter::Iter; + +mod iter; + +use self::pair_slices::PairSlices; + +mod pair_slices; + +use self::ring_slices::RingSlices; + +mod ring_slices; + #[cfg(test)] mod tests; @@ -68,67 +91,6 @@ pub struct VecDeque { buf: RawVec, } -/// PairSlices pairs up equal length slice parts of two deques -/// -/// For example, given deques "A" and "B" with the following division into slices: -/// -/// A: [0 1 2] [3 4 5] -/// B: [a b] [c d e] -/// -/// It produces the following sequence of matching slices: -/// -/// ([0 1], [a b]) -/// (\[2\], \[c\]) -/// ([3 4], [d e]) -/// -/// and the uneven remainder of either A or B is skipped. -struct PairSlices<'a, 'b, T> { - a0: &'a mut [T], - a1: &'a mut [T], - b0: &'b [T], - b1: &'b [T], -} - -impl<'a, 'b, T> PairSlices<'a, 'b, T> { - fn from(to: &'a mut VecDeque, from: &'b VecDeque) -> Self { - let (a0, a1) = to.as_mut_slices(); - let (b0, b1) = from.as_slices(); - PairSlices { a0, a1, b0, b1 } - } - - fn has_remainder(&self) -> bool { - !self.b0.is_empty() - } - - fn remainder(self) -> impl Iterator { - array::IntoIter::new([self.b0, self.b1]) - } -} - -impl<'a, 'b, T> Iterator for PairSlices<'a, 'b, T> { - type Item = (&'a mut [T], &'b [T]); - fn next(&mut self) -> Option { - // Get next part length - let part = cmp::min(self.a0.len(), self.b0.len()); - if part == 0 { - return None; - } - let (p0, p1) = replace(&mut self.a0, &mut []).split_at_mut(part); - let (q0, q1) = self.b0.split_at(part); - - // Move a1 into a0, if it's empty (and b1, b0 the same way). - self.a0 = p1; - self.b0 = q1; - if self.a0.is_empty() { - self.a0 = replace(&mut self.a1, &mut []); - } - if self.b0.is_empty() { - self.b0 = replace(&mut self.b1, &[]); - } - Some((p0, q0)) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Clone for VecDeque { fn clone(&self) -> VecDeque { @@ -2605,61 +2567,6 @@ fn wrap_index(index: usize, size: usize) -> usize { index & (size - 1) } -/// Returns the two slices that cover the `VecDeque`'s valid range -trait RingSlices: Sized { - fn slice(self, from: usize, to: usize) -> Self; - fn split_at(self, i: usize) -> (Self, Self); - - fn ring_slices(buf: Self, head: usize, tail: usize) -> (Self, Self) { - let contiguous = tail <= head; - if contiguous { - let (empty, buf) = buf.split_at(0); - (buf.slice(tail, head), empty) - } else { - let (mid, right) = buf.split_at(tail); - let (left, _) = mid.split_at(head); - (right, left) - } - } -} - -impl RingSlices for &[T] { - fn slice(self, from: usize, to: usize) -> Self { - &self[from..to] - } - fn split_at(self, i: usize) -> (Self, Self) { - (*self).split_at(i) - } -} - -impl RingSlices for &mut [T] { - fn slice(self, from: usize, to: usize) -> Self { - &mut self[from..to] - } - fn split_at(self, i: usize) -> (Self, Self) { - (*self).split_at_mut(i) - } -} - -impl RingSlices for *mut [T] { - fn slice(self, from: usize, to: usize) -> Self { - assert!(from <= to && to < self.len()); - // Not using `get_unchecked_mut` to keep this a safe operation. - let len = to - from; - ptr::slice_from_raw_parts_mut(self.as_mut_ptr().wrapping_add(from), len) - } - - fn split_at(self, mid: usize) -> (Self, Self) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - assert!(mid <= len); - ( - ptr::slice_from_raw_parts_mut(ptr, mid), - ptr::slice_from_raw_parts_mut(ptr.wrapping_add(mid), len - mid), - ) - } -} - /// Calculate the number of elements left to be read in the buffer #[inline] fn count(tail: usize, head: usize, size: usize) -> usize { @@ -2667,336 +2574,6 @@ fn count(tail: usize, head: usize, size: usize) -> usize { (head.wrapping_sub(tail)) & (size - 1) } -/// An iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`iter`] method on [`VecDeque`]. See its -/// documentation for more. -/// -/// [`iter`]: VecDeque::iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - ring: &'a [T], - tail: usize, - head: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for Iter<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - f.debug_tuple("Iter").field(&front).field(&back).finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Iter<'_, T> { - fn clone(&self) -> Self { - Iter { ring: self.ring, tail: self.tail, head: self.head } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - if self.tail == self.head { - return None; - } - let tail = self.tail; - self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(tail)) } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = count(self.tail, self.head, self.ring.len()); - (len, Some(len)) - } - - fn fold(self, mut accum: Acc, mut f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = front.iter().fold(accum, &mut f); - back.iter().fold(accum, &mut f) - } - - fn try_fold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - let (mut iter, final_res); - if self.tail <= self.head { - // single slice self.ring[self.tail..self.head] - iter = self.ring[self.tail..self.head].iter(); - final_res = iter.try_fold(init, &mut f); - } else { - // two slices: self.ring[self.tail..], self.ring[..self.head] - let (front, back) = self.ring.split_at(self.tail); - let mut back_iter = back.iter(); - let res = back_iter.try_fold(init, &mut f); - let len = self.ring.len(); - self.tail = (self.ring.len() - back_iter.len()) & (len - 1); - iter = front[..self.head].iter(); - final_res = iter.try_fold(res?, &mut f); - } - self.tail = self.head - iter.len(); - final_res - } - - fn nth(&mut self, n: usize) -> Option { - if n >= count(self.tail, self.head, self.ring.len()) { - self.tail = self.head; - None - } else { - self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); - self.next() - } - } - - #[inline] - fn last(mut self) -> Option<&'a T> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - if self.tail == self.head { - return None; - } - self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(self.head)) } - } - - fn rfold(self, mut accum: Acc, mut f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = back.iter().rfold(accum, &mut f); - front.iter().rfold(accum, &mut f) - } - - fn try_rfold(&mut self, init: B, mut f: F) -> R - where - Self: Sized, - F: FnMut(B, Self::Item) -> R, - R: Try, - { - let (mut iter, final_res); - if self.tail <= self.head { - // single slice self.ring[self.tail..self.head] - iter = self.ring[self.tail..self.head].iter(); - final_res = iter.try_rfold(init, &mut f); - } else { - // two slices: self.ring[self.tail..], self.ring[..self.head] - let (front, back) = self.ring.split_at(self.tail); - let mut front_iter = front[..self.head].iter(); - let res = front_iter.try_rfold(init, &mut f); - self.head = front_iter.len(); - iter = back.iter(); - final_res = iter.try_rfold(res?, &mut f); - } - self.head = self.tail + iter.len(); - final_res - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Iter<'_, T> { - fn is_empty(&self) -> bool { - self.head == self.tail - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Iter<'_, T> {} - -/// A mutable iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`VecDeque`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: VecDeque::iter_mut -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, T: 'a> { - // Internal safety invariant: the entire slice is dereferencable. - ring: *mut [T], - tail: usize, - head: usize, - phantom: PhantomData<&'a mut [T]>, -} - -// SAFETY: we do nothing thread-local and there is no interior mutability, -// so the usual structural `Send`/`Sync` apply. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for IterMut<'_, T> {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for IterMut<'_, T> {} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IterMut<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. - // The `IterMut` invariant also ensures everything is dereferencable. - let (front, back) = unsafe { (&*front, &*back) }; - f.debug_tuple("IterMut").field(&front).field(&back).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - #[inline] - fn next(&mut self) -> Option<&'a mut T> { - if self.tail == self.head { - return None; - } - let tail = self.tail; - self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); - - unsafe { - let elem = self.ring.get_unchecked_mut(tail); - Some(&mut *elem) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = count(self.tail, self.head, self.ring.len()); - (len, Some(len)) - } - - fn fold(self, mut accum: Acc, mut f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. - // The `IterMut` invariant also ensures everything is dereferencable. - let (front, back) = unsafe { (&mut *front, &mut *back) }; - accum = front.iter_mut().fold(accum, &mut f); - back.iter_mut().fold(accum, &mut f) - } - - fn nth(&mut self, n: usize) -> Option { - if n >= count(self.tail, self.head, self.ring.len()) { - self.tail = self.head; - None - } else { - self.tail = wrap_index(self.tail.wrapping_add(n), self.ring.len()); - self.next() - } - } - - #[inline] - fn last(mut self) -> Option<&'a mut T> { - self.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut T> { - if self.tail == self.head { - return None; - } - self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); - - unsafe { - let elem = self.ring.get_unchecked_mut(self.head); - Some(&mut *elem) - } - } - - fn rfold(self, mut accum: Acc, mut f: F) -> Acc - where - F: FnMut(Acc, Self::Item) -> Acc, - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. - // The `IterMut` invariant also ensures everything is dereferencable. - let (front, back) = unsafe { (&mut *front, &mut *back) }; - accum = back.iter_mut().rfold(accum, &mut f); - front.iter_mut().rfold(accum, &mut f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IterMut<'_, T> { - fn is_empty(&self) -> bool { - self.head == self.tail - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IterMut<'_, T> {} - -/// An owning iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`into_iter`] method on [`VecDeque`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: VecDeque::into_iter -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - inner: VecDeque, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("IntoIter").field(&self.inner).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.inner.pop_front() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.inner.len(); - (len, Some(len)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.inner.pop_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.inner.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for VecDeque { fn eq(&self, other: &VecDeque) -> bool { @@ -3039,26 +2616,6 @@ impl PartialEq for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] impl Eq for VecDeque {} -macro_rules! __impl_slice_eq1 { - ([$($vars:tt)*] $lhs:ty, $rhs:ty, $($constraints:tt)*) => { - #[stable(feature = "vec_deque_partial_eq_slice", since = "1.17.0")] - impl PartialEq<$rhs> for $lhs - where - A: PartialEq, - $($constraints)* - { - fn eq(&self, other: &$rhs) -> bool { - if self.len() != other.len() { - return false; - } - let (sa, sb) = self.as_slices(); - let (oa, ob) = other[..].split_at(sa.len()); - sa == oa && sb == ob - } - } - } -} - __impl_slice_eq1! { [] VecDeque, Vec, } __impl_slice_eq1! { [] VecDeque, &[B], } __impl_slice_eq1! { [] VecDeque, &mut [B], } diff --git a/library/alloc/src/collections/vec_deque/pair_slices.rs b/library/alloc/src/collections/vec_deque/pair_slices.rs new file mode 100644 index 0000000000000..812765d0b0ded --- /dev/null +++ b/library/alloc/src/collections/vec_deque/pair_slices.rs @@ -0,0 +1,66 @@ +use core::array; +use core::cmp::{self}; +use core::mem::replace; + +use super::VecDeque; + +/// PairSlices pairs up equal length slice parts of two deques +/// +/// For example, given deques "A" and "B" with the following division into slices: +/// +/// A: [0 1 2] [3 4 5] +/// B: [a b] [c d e] +/// +/// It produces the following sequence of matching slices: +/// +/// ([0 1], [a b]) +/// (\[2\], \[c\]) +/// ([3 4], [d e]) +/// +/// and the uneven remainder of either A or B is skipped. +pub struct PairSlices<'a, 'b, T> { + pub(crate) a0: &'a mut [T], + pub(crate) a1: &'a mut [T], + pub(crate) b0: &'b [T], + pub(crate) b1: &'b [T], +} + +impl<'a, 'b, T> PairSlices<'a, 'b, T> { + pub fn from(to: &'a mut VecDeque, from: &'b VecDeque) -> Self { + let (a0, a1) = to.as_mut_slices(); + let (b0, b1) = from.as_slices(); + PairSlices { a0, a1, b0, b1 } + } + + pub fn has_remainder(&self) -> bool { + !self.b0.is_empty() + } + + pub fn remainder(self) -> impl Iterator { + array::IntoIter::new([self.b0, self.b1]) + } +} + +impl<'a, 'b, T> Iterator for PairSlices<'a, 'b, T> { + type Item = (&'a mut [T], &'b [T]); + fn next(&mut self) -> Option { + // Get next part length + let part = cmp::min(self.a0.len(), self.b0.len()); + if part == 0 { + return None; + } + let (p0, p1) = replace(&mut self.a0, &mut []).split_at_mut(part); + let (q0, q1) = self.b0.split_at(part); + + // Move a1 into a0, if it's empty (and b1, b0 the same way). + self.a0 = p1; + self.b0 = q1; + if self.a0.is_empty() { + self.a0 = replace(&mut self.a1, &mut []); + } + if self.b0.is_empty() { + self.b0 = replace(&mut self.b1, &[]); + } + Some((p0, q0)) + } +} diff --git a/library/alloc/src/collections/vec_deque/ring_slices.rs b/library/alloc/src/collections/vec_deque/ring_slices.rs new file mode 100644 index 0000000000000..dd0fa7d6074c0 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/ring_slices.rs @@ -0,0 +1,56 @@ +use core::ptr::{self}; + +/// Returns the two slices that cover the `VecDeque`'s valid range +pub trait RingSlices: Sized { + fn slice(self, from: usize, to: usize) -> Self; + fn split_at(self, i: usize) -> (Self, Self); + + fn ring_slices(buf: Self, head: usize, tail: usize) -> (Self, Self) { + let contiguous = tail <= head; + if contiguous { + let (empty, buf) = buf.split_at(0); + (buf.slice(tail, head), empty) + } else { + let (mid, right) = buf.split_at(tail); + let (left, _) = mid.split_at(head); + (right, left) + } + } +} + +impl RingSlices for &[T] { + fn slice(self, from: usize, to: usize) -> Self { + &self[from..to] + } + fn split_at(self, i: usize) -> (Self, Self) { + (*self).split_at(i) + } +} + +impl RingSlices for &mut [T] { + fn slice(self, from: usize, to: usize) -> Self { + &mut self[from..to] + } + fn split_at(self, i: usize) -> (Self, Self) { + (*self).split_at_mut(i) + } +} + +impl RingSlices for *mut [T] { + fn slice(self, from: usize, to: usize) -> Self { + assert!(from <= to && to < self.len()); + // Not using `get_unchecked_mut` to keep this a safe operation. + let len = to - from; + ptr::slice_from_raw_parts_mut(self.as_mut_ptr().wrapping_add(from), len) + } + + fn split_at(self, mid: usize) -> (Self, Self) { + let len = self.len(); + let ptr = self.as_mut_ptr(); + assert!(mid <= len); + ( + ptr::slice_from_raw_parts_mut(ptr, mid), + ptr::slice_from_raw_parts_mut(ptr.wrapping_add(mid), len - mid), + ) + } +} diff --git a/src/test/ui/hygiene/panic-location.run.stderr b/src/test/ui/hygiene/panic-location.run.stderr index a437a7b50123b..216b31586da3e 100644 --- a/src/test/ui/hygiene/panic-location.run.stderr +++ b/src/test/ui/hygiene/panic-location.run.stderr @@ -1,2 +1,2 @@ -thread 'main' panicked at 'capacity overflow', $SRC_DIR/alloc/src/collections/vec_deque.rs:LL:COL +thread 'main' panicked at 'capacity overflow', $SRC_DIR/alloc/src/collections/vec_deque/mod.rs:LL:COL note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace From bf6902ca61ae6e91a3ab95bed88426926f245d02 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 13 Nov 2020 10:23:50 -0800 Subject: [PATCH 40/46] Add BTreeMap::retain and BTreeSet::retain --- library/alloc/src/collections/btree/map.rs | 24 +++++++++++++++++++ .../alloc/src/collections/btree/map/tests.rs | 11 +++++++++ library/alloc/src/collections/btree/set.rs | 24 +++++++++++++++++++ .../alloc/src/collections/btree/set/tests.rs | 11 +++++++++ 4 files changed, 70 insertions(+) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 49122f53d33ad..7151d3763f01f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -863,6 +863,30 @@ impl BTreeMap { } } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap = (0..8).map(|x| (x, x*10)).collect(); + /// // Keep only the elements with even-numbered keys. + /// map.retain(|&k, _| k % 2 == 0); + /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)])); + /// ``` + #[inline] + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.drain_filter(|k, v| !f(k, v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index dd3ebcccf76a5..11dbb584abdac 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -808,6 +808,17 @@ fn test_range_mut() { map.check(); } +#[test] +fn test_retain() { + let mut map: BTreeMap = (0..100).map(|x| (x, x * 10)).collect(); + + map.retain(|&k, _| k % 2 == 0); + assert_eq!(map.len(), 50); + assert_eq!(map[&2], 20); + assert_eq!(map[&4], 40); + assert_eq!(map[&6], 60); +} + mod test_drain_filter { use super::*; diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 684019f8f5f5e..1a807100653bc 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -798,6 +798,30 @@ impl BTreeSet { Recover::take(&mut self.map, value) } + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// #![feature(btree_retain)] + /// use std::collections::BTreeSet; + /// + /// let xs = [1, 2, 3, 4, 5, 6]; + /// let mut set: BTreeSet = xs.iter().cloned().collect(); + /// // Keep only the even numbers. + /// set.retain(|&k| k % 2 == 0); + /// assert!(set.iter().eq([2, 4, 6].iter())); + /// ``` + #[unstable(feature = "btree_retain", issue = "79025")] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.drain_filter(|v| !f(v)); + } + /// Moves all elements from `other` into `Self`, leaving `other` empty. /// /// # Examples diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 52cde8299e418..ef40a048a382e 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -324,6 +324,17 @@ fn test_is_subset() { assert_eq!(is_subset(&[99, 100], &large), false); } +#[test] +fn test_retain() { + let xs = [1, 2, 3, 4, 5, 6]; + let mut set: BTreeSet = xs.iter().cloned().collect(); + set.retain(|&k| k % 2 == 0); + assert_eq!(set.len(), 3); + assert!(set.contains(&2)); + assert!(set.contains(&4)); + assert!(set.contains(&6)); +} + #[test] fn test_drain_filter() { let mut x: BTreeSet<_> = [1].iter().copied().collect(); From 7eb1a1afcfd5564f0d78f37c9c6397a070d794c2 Mon Sep 17 00:00:00 2001 From: Camelid Date: Fri, 13 Nov 2020 11:23:12 -0800 Subject: [PATCH 41/46] Validate that locals have a corresponding `LocalDecl` --- compiler/rustc_mir/src/transform/validate.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index e1e6e71acb5a8..919e4a90a1716 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -183,6 +183,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) { + if self.body.local_decls.get(*local).is_none() { + self.fail( + location, + format!("local {:?} has no corresponding declaration in `body.local_decls`", local), + ); + } + if self.reachable_blocks.contains(location.block) && context.is_use() { // Uses of locals must occur while the local's storage is allocated. self.storage_liveness.seek_after_primary_effect(location); From ac4c1f58b9fe1b4182b8af598751afd88caa5db5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Nov 2020 00:05:05 +0300 Subject: [PATCH 42/46] rustc_resolve: Make `macro_rules` scope chain compression lazy --- .../rustc_resolve/src/build_reduced_graph.rs | 4 +-- compiler/rustc_resolve/src/lib.rs | 27 ++++++++++++------- compiler/rustc_resolve/src/macros.rs | 26 +++--------------- 3 files changed, 21 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 34145c3c138a1..493b9f15271ef 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1163,9 +1163,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope); assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation"); - let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)); - self.r.invocation_macro_rules_scopes.entry(invoc_id).or_default().insert(scope); - scope + self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 4e85c88c0e504..d18335ef2e63a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -976,9 +976,6 @@ pub struct Resolver<'a> { /// `macro_rules` scopes *produced* by expanding the macro invocations, /// include all the `macro_rules` items and other invocations generated by them. output_macro_rules_scopes: FxHashMap>, - /// References to all `MacroRulesScope::Invocation(invoc_id)`s, used to update such scopes - /// when their corresponding `invoc_id`s get expanded. - invocation_macro_rules_scopes: FxHashMap>>, /// Helper attributes that are in scope for the given expansion. helper_attrs: FxHashMap>, @@ -1310,7 +1307,6 @@ impl<'a> Resolver<'a> { non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)], invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), - invocation_macro_rules_scopes: Default::default(), helper_attrs: Default::default(), local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), @@ -1680,7 +1676,20 @@ impl<'a> Resolver<'a> { !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive)) } Scope::DeriveHelpersCompat => true, - Scope::MacroRules(..) => true, + Scope::MacroRules(macro_rules_scope) => { + // Use "path compression" on `macro_rules` scope chains. This is an optimization + // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. + // As another consequence of this optimization visitors never observe invocation + // scopes for macros that were already expanded. + while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() { + if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) { + macro_rules_scope.set(next_scope.get()); + } else { + break; + } + } + true + } Scope::CrateRoot => true, Scope::Module(..) => true, Scope::RegisteredAttrs => use_prelude, @@ -1716,11 +1725,9 @@ impl<'a> Resolver<'a> { MacroRulesScope::Binding(binding) => { Scope::MacroRules(binding.parent_macro_rules_scope) } - MacroRulesScope::Invocation(invoc_id) => Scope::MacroRules( - self.output_macro_rules_scopes.get(&invoc_id).cloned().unwrap_or_else( - || self.invocation_parent_scopes[&invoc_id].macro_rules, - ), - ), + MacroRulesScope::Invocation(invoc_id) => { + Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules) + } MacroRulesScope::Empty => Scope::Module(module), }, Scope::CrateRoot => match ns { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 6bc9419ea8411..e052b6b334529 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -62,8 +62,8 @@ pub enum MacroRulesScope<'a> { } /// `macro_rules!` scopes are always kept by reference and inside a cell. -/// The reason is that we update all scopes with value `MacroRulesScope::Invocation(invoc_id)` -/// in-place immediately after `invoc_id` gets expanded. +/// The reason is that we update scopes with value `MacroRulesScope::Invocation(invoc_id)` +/// in-place after `invoc_id` gets expanded. /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// which usually grow lineraly with the number of macro invocations /// in a module (including derives) and hurt performance. @@ -173,22 +173,6 @@ impl<'a> ResolverExpand for Resolver<'a> { let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); - // Update all `macro_rules` scopes referring to this invocation. This is an optimization - // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. - if let Some(invocation_scopes) = self.invocation_macro_rules_scopes.remove(&expansion) { - for invocation_scope in &invocation_scopes { - invocation_scope.set(output_macro_rules_scope.get()); - } - // All `macro_rules` scopes that previously referred to `expansion` - // are now rerouted to its output scope, if it's also an invocation. - if let MacroRulesScope::Invocation(invoc_id) = output_macro_rules_scope.get() { - self.invocation_macro_rules_scopes - .entry(invoc_id) - .or_default() - .extend(invocation_scopes); - } - } - parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); } @@ -687,11 +671,7 @@ impl<'a> Resolver<'a> { { Ok((macro_rules_binding.binding, Flags::MACRO_RULES)) } - MacroRulesScope::Invocation(invoc_id) - if !this.output_macro_rules_scopes.contains_key(&invoc_id) => - { - Err(Determinacy::Undetermined) - } + MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined), _ => Err(Determinacy::Determined), }, Scope::CrateRoot => { From 41c44b498f021f8028151109abf2805fa4269866 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 14 Nov 2020 01:29:30 +0100 Subject: [PATCH 43/46] Move Steal to rustc_data_structures. --- compiler/rustc_data_structures/src/lib.rs | 1 + .../src/ty => rustc_data_structures/src}/steal.rs | 11 +++++++++-- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_interface/src/queries.rs | 2 +- compiler/rustc_middle/src/arena.rs | 4 ++-- compiler/rustc_middle/src/ich/impls_ty.rs | 9 --------- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 1 - compiler/rustc_middle/src/ty/query/mod.rs | 2 +- compiler/rustc_mir/src/transform/mod.rs | 2 +- compiler/rustc_mir_build/src/build/mod.rs | 2 +- 11 files changed, 18 insertions(+), 20 deletions(-) rename compiler/{rustc_middle/src/ty => rustc_data_structures/src}/steal.rs (82%) diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 7669b78834c3f..b4b9160ad007d 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -102,6 +102,7 @@ pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; pub mod sso; +pub mod steal; pub mod tagged_ptr; pub mod temp_dir; pub mod unhash; diff --git a/compiler/rustc_middle/src/ty/steal.rs b/compiler/rustc_data_structures/src/steal.rs similarity index 82% rename from compiler/rustc_middle/src/ty/steal.rs rename to compiler/rustc_data_structures/src/steal.rs index 224e76845d708..e532a84cea3f2 100644 --- a/compiler/rustc_middle/src/ty/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -1,4 +1,5 @@ -use rustc_data_structures::sync::{MappedReadGuard, ReadGuard, RwLock}; +use crate::stable_hasher::{HashStable, StableHasher}; +use crate::sync::{MappedReadGuard, ReadGuard, RwLock}; /// The `Steal` struct is intended to used as the value for a query. /// Specifically, we sometimes have queries (*cough* MIR *cough*) @@ -31,7 +32,7 @@ impl Steal { pub fn borrow(&self) -> MappedReadGuard<'_, T> { ReadGuard::map(self.value.borrow(), |opt| match *opt { - None => bug!("attempted to read from stolen value"), + None => panic!("attempted to read from stolen value"), Some(ref v) => v, }) } @@ -42,3 +43,9 @@ impl Steal { value.expect("attempt to read from stolen value") } } + +impl> HashStable for Steal { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + self.borrow().hash_stable(hcx, hasher); + } +} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 548b6c03daa7e..8f8aac532c99a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,6 +6,7 @@ use rustc_ast::mut_visit::MutVisitor; use rustc_ast::{self as ast, visit}; use rustc_codegen_ssa::back::link::emit_metadata; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel}; @@ -20,7 +21,6 @@ use rustc_middle::dep_graph::DepGraph; use rustc_middle::middle; use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::steal::Steal; use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc_mir as mir; use rustc_mir_build as mir_build; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 1de7350a3e21c..a2704c3adbf00 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -3,6 +3,7 @@ use crate::passes::{self, BoxedResolver, QueryContext}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_errors::ErrorReported; @@ -12,7 +13,6 @@ use rustc_incremental::DepGraphFuture; use rustc_lint::LintStore; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; -use rustc_middle::ty::steal::Steal; use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; use rustc_serialize::json; use rustc_session::config::{self, OutputFilenames, OutputType}; diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index f6570cc95d27d..672073b1d3472 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -14,10 +14,10 @@ macro_rules! arena_types { [] layouts: rustc_target::abi::Layout, // AdtDef are interned and compared by address [] adt_def: rustc_middle::ty::AdtDef, - [] steal_mir: rustc_middle::ty::steal::Steal>, + [] steal_mir: rustc_data_structures::steal::Steal>, [decode] mir: rustc_middle::mir::Body<$tcx>, [] steal_promoted: - rustc_middle::ty::steal::Steal< + rustc_data_structures::steal::Steal< rustc_index::vec::IndexVec< rustc_middle::mir::Promoted, rustc_middle::mir::Body<$tcx> diff --git a/compiler/rustc_middle/src/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs index 8f15c99f951fe..69bb4e23c4c0d 100644 --- a/compiler/rustc_middle/src/ich/impls_ty.rs +++ b/compiler/rustc_middle/src/ich/impls_ty.rs @@ -184,15 +184,6 @@ impl<'a> HashStable> for ty::FloatVid { } } -impl<'a, T> HashStable> for ty::steal::Steal -where - T: HashStable>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - self.borrow().hash_stable(hcx, hasher); - } -} - impl<'a> HashStable> for crate::middle::privacy::AccessLevels { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1c6937e685c65..c11c8e5f969c3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -14,7 +14,6 @@ use crate::mir::interpret::{self, Allocation, ConstValue, Scalar}; use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; use crate::traits; use crate::ty::query::{self, TyCtxtAt}; -use crate::ty::steal::Steal; use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts}; use crate::ty::TyKind::*; use crate::ty::{ @@ -33,6 +32,7 @@ use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{ hash_stable_hashmap, HashStable, StableHasher, StableVec, }; +use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::ErrorReported; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0042b4a3a4279..ea031040e7d2d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -106,7 +106,6 @@ pub mod outlives; pub mod print; pub mod query; pub mod relate; -pub mod steal; pub mod subst; pub mod trait_def; pub mod util; diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 7ba4d5a14dffb..187f86a52f4dc 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -28,13 +28,13 @@ use crate::traits::query::{ }; use crate::traits::specialization_graph; use crate::traits::{self, ImplSource}; -use crate::ty::steal::Steal; use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableVec; +use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_errors::ErrorReported; diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index e3fea2d2701e5..690051f8659cd 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -1,6 +1,7 @@ use crate::{shim, util}; use required_consts::RequiredConstsVisitor; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::steal::Steal; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; @@ -8,7 +9,6 @@ use rustc_index::vec::IndexVec; use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::steal::Steal; use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; use std::borrow::Cow; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index f9995f43f5a8d..6982cdccd5205 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -24,7 +24,7 @@ use super::lints; crate fn mir_built<'tcx>( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam, -) -> &'tcx ty::steal::Steal> { +) -> &'tcx rustc_data_structures::steal::Steal> { if let Some(def) = def.try_upgrade(tcx) { return tcx.mir_built(def); } From 03cbee84afebef7b4bb6e3e76161ca715c7367fd Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 14 Nov 2020 03:45:10 -0500 Subject: [PATCH 44/46] Rename ItemEnum -> ItemKind, inner -> kind --- src/librustdoc/clean/auto_trait.rs | 2 +- src/librustdoc/clean/blanket_impl.rs | 2 +- src/librustdoc/clean/inline.rs | 10 +-- src/librustdoc/clean/mod.rs | 62 +++++++++---------- src/librustdoc/clean/types.rs | 33 +++++----- src/librustdoc/clean/utils.rs | 18 +++--- src/librustdoc/fold.rs | 16 ++--- src/librustdoc/formats/cache.rs | 18 +++--- src/librustdoc/formats/item_type.rs | 6 +- src/librustdoc/formats/mod.rs | 2 +- src/librustdoc/formats/renderer.rs | 2 +- src/librustdoc/html/render/cache.rs | 2 +- src/librustdoc/html/render/mod.rs | 57 +++++++++-------- .../passes/calculate_doc_coverage.rs | 2 +- .../passes/collect_intra_doc_links.rs | 8 +-- src/librustdoc/passes/collect_trait_impls.rs | 8 +-- src/librustdoc/passes/doc_test_lints.rs | 2 +- src/librustdoc/passes/strip_hidden.rs | 2 +- src/librustdoc/passes/stripper.rs | 8 +-- 19 files changed, 129 insertions(+), 131 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index f39b53f3c826a..a07d6b73f06c1 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -125,7 +125,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { def_id: self.cx.next_def_id(param_env_def_id.krate), stability: None, deprecation: None, - inner: ImplItem(Impl { + kind: ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: new_generics, provided_trait_methods: Default::default(), diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index de5a9a615557c..ed92361dd2335 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -98,7 +98,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { def_id: self.cx.next_def_id(impl_def_id.krate), stability: None, deprecation: None, - inner: ImplItem(Impl { + kind: ImplItem(Impl { unsafety: hir::Unsafety::Normal, generics: ( self.cx.tcx.generics_of(impl_def_id), diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index b3de70e590574..d6f8870c859f5 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -54,7 +54,7 @@ crate fn try_inline( debug!("attrs={:?}", attrs); let attrs_clone = attrs; - let inner = match res { + let kind = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, clean::TypeKind::Trait); ret.extend(build_impls(cx, Some(parent_module), did, attrs)); @@ -128,7 +128,7 @@ crate fn try_inline( source: cx.tcx.def_span(did).clean(cx), name: Some(name.clean(cx)), attrs, - inner, + kind, visibility: clean::Public, stability: cx.tcx.lookup_stability(did).cloned(), deprecation: cx.tcx.lookup_deprecation(did).clean(cx), @@ -446,7 +446,7 @@ crate fn build_impl( debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id()); ret.push(clean::Item { - inner: clean::ImplItem(clean::Impl { + kind: clean::ImplItem(clean::Impl { unsafety: hir::Unsafety::Normal, generics, provided_trait_methods: provided, @@ -498,7 +498,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) visibility: clean::Public, stability: None, deprecation: None, - inner: clean::ImportItem(clean::Import::new_simple( + kind: clean::ImportItem(clean::Import::new_simple( item.ident.to_string(), clean::ImportSource { path: clean::Path { @@ -555,7 +555,7 @@ fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static } } -fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemEnum { +fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind { let imported_from = cx.tcx.original_crate_name(did.krate); match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) { LoadedMacro::MacroDef(def, _) => { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 366548d5b5fa2..56ce0bae8bb7b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -42,7 +42,7 @@ use utils::*; pub use utils::{get_auto_trait_and_blanket_impls, krate, register_res}; pub use self::types::FnRetTy::*; -pub use self::types::ItemEnum::*; +pub use self::types::ItemKind::*; pub use self::types::SelfTy::*; pub use self::types::Type::*; pub use self::types::Visibility::{Inherited, Public}; @@ -276,7 +276,7 @@ impl Clean for doctree::Module<'_> { stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - inner: ModuleItem(Module { is_crate: self.is_crate, items }), + kind: ModuleItem(Module { is_crate: self.is_crate, items }), } } } @@ -916,7 +916,7 @@ impl Clean for doctree::Function<'_> { stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: did.to_def_id(), - inner: FunctionItem(Function { + kind: FunctionItem(Function { decl, generics, header: hir::FnHeader { constness, ..self.header }, @@ -1023,7 +1023,7 @@ impl Clean for doctree::Trait<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: TraitItem(Trait { + kind: TraitItem(Trait { auto: self.is_auto.clean(cx), unsafety: self.unsafety, items: self.items.iter().map(|ti| ti.clean(cx)).collect(), @@ -1047,7 +1047,7 @@ impl Clean for doctree::TraitAlias<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: TraitAliasItem(TraitAlias { + kind: TraitAliasItem(TraitAlias { generics: self.generics.clean(cx), bounds: self.bounds.clean(cx), }), @@ -1102,7 +1102,7 @@ impl Clean for hir::def::DefKind { impl Clean for hir::TraitItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let local_did = cx.tcx.hir().local_def_id(self.hir_id); - let inner = match self.kind { + let kind = match self.kind { hir::TraitItemKind::Const(ref ty, default) => { AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e))) } @@ -1140,7 +1140,7 @@ impl Clean for hir::TraitItem<'_> { visibility: Visibility::Inherited, stability: get_stability(cx, local_did.to_def_id()), deprecation: get_deprecation(cx, local_did.to_def_id()), - inner, + kind, } } } @@ -1148,7 +1148,7 @@ impl Clean for hir::TraitItem<'_> { impl Clean for hir::ImplItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { let local_did = cx.tcx.hir().local_def_id(self.hir_id); - let inner = match self.kind { + let kind = match self.kind { hir::ImplItemKind::Const(ref ty, expr) => { AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr))) } @@ -1175,14 +1175,14 @@ impl Clean for hir::ImplItem<'_> { visibility: self.vis.clean(cx), stability: get_stability(cx, local_did.to_def_id()), deprecation: get_deprecation(cx, local_did.to_def_id()), - inner, + kind, } } } impl Clean for ty::AssocItem { fn clean(&self, cx: &DocContext<'_>) -> Item { - let inner = match self.kind { + let kind = match self.kind { ty::AssocKind::Const => { let ty = cx.tcx.type_of(self.def_id); let default = if self.defaultness.has_value() { @@ -1343,7 +1343,7 @@ impl Clean for ty::AssocItem { def_id: self.def_id, attrs: inline::load_attrs(cx, self.def_id).clean(cx), source: cx.tcx.def_span(self.def_id).clean(cx), - inner, + kind, } } } @@ -1784,7 +1784,7 @@ impl Clean for hir::StructField<'_> { stability: get_stability(cx, local_did.to_def_id()), deprecation: get_deprecation(cx, local_did.to_def_id()), def_id: local_did.to_def_id(), - inner: StructFieldItem(self.ty.clean(cx)), + kind: StructFieldItem(self.ty.clean(cx)), } } } @@ -1799,7 +1799,7 @@ impl Clean for ty::FieldDef { stability: get_stability(cx, self.did), deprecation: get_deprecation(cx, self.did), def_id: self.did, - inner: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)), + kind: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)), } } } @@ -1835,7 +1835,7 @@ impl Clean for doctree::Struct<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: StructItem(Struct { + kind: StructItem(Struct { struct_type: self.struct_type, generics: self.generics.clean(cx), fields: self.fields.clean(cx), @@ -1855,7 +1855,7 @@ impl Clean for doctree::Union<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: UnionItem(Union { + kind: UnionItem(Union { struct_type: self.struct_type, generics: self.generics.clean(cx), fields: self.fields.clean(cx), @@ -1885,7 +1885,7 @@ impl Clean for doctree::Enum<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: EnumItem(Enum { + kind: EnumItem(Enum { variants: self.variants.iter().map(|v| v.clean(cx)).collect(), generics: self.generics.clean(cx), variants_stripped: false, @@ -1904,7 +1904,7 @@ impl Clean for doctree::Variant<'_> { stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - inner: VariantItem(Variant { kind: self.def.clean(cx) }), + kind: VariantItem(Variant { kind: self.def.clean(cx) }), } } } @@ -1930,7 +1930,7 @@ impl Clean for ty::VariantDef { def_id: field.did, stability: get_stability(cx, field.did), deprecation: get_deprecation(cx, field.did), - inner: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)), + kind: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)), }) .collect(), }), @@ -1941,7 +1941,7 @@ impl Clean for ty::VariantDef { source: cx.tcx.def_span(self.def_id).clean(cx), visibility: Inherited, def_id: self.def_id, - inner: VariantItem(Variant { kind }), + kind: VariantItem(Variant { kind }), stability: get_stability(cx, self.def_id), deprecation: get_deprecation(cx, self.def_id), } @@ -2057,7 +2057,7 @@ impl Clean for doctree::Typedef<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false), + kind: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false), } } } @@ -2072,7 +2072,7 @@ impl Clean for doctree::OpaqueTy<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: OpaqueTyItem(OpaqueTy { + kind: OpaqueTyItem(OpaqueTy { bounds: self.opaque_ty.bounds.clean(cx), generics: self.opaque_ty.generics.clean(cx), }), @@ -2100,7 +2100,7 @@ impl Clean for doctree::Static<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: StaticItem(Static { + kind: StaticItem(Static { type_: self.type_.clean(cx), mutability: self.mutability, expr: print_const_expr(cx, self.expr), @@ -2121,7 +2121,7 @@ impl Clean for doctree::Constant<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: ConstantItem(Constant { + kind: ConstantItem(Constant { type_: self.type_.clean(cx), expr: print_const_expr(cx, self.expr), value: print_evaluated_const(cx, def_id.to_def_id()), @@ -2175,7 +2175,7 @@ impl Clean> for doctree::Impl<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: ImplItem(Impl { + kind: ImplItem(Impl { unsafety: self.unsafety, generics: self.generics.clean(cx), provided_trait_methods: provided.clone(), @@ -2231,7 +2231,7 @@ impl Clean> for doctree::ExternCrate<'_> { visibility: self.vis.clean(cx), stability: None, deprecation: None, - inner: ExternCrateItem(self.name.clean(cx), self.path.clone()), + kind: ExternCrateItem(self.name.clean(cx), self.path.clone()), }] } } @@ -2302,7 +2302,7 @@ impl Clean> for doctree::Import<'_> { visibility: self.vis.clean(cx), stability: None, deprecation: None, - inner: ImportItem(Import::new_simple( + kind: ImportItem(Import::new_simple( self.name.clean(cx), resolve_use_source(cx, path), false, @@ -2322,14 +2322,14 @@ impl Clean> for doctree::Import<'_> { visibility: self.vis.clean(cx), stability: None, deprecation: None, - inner: ImportItem(inner), + kind: ImportItem(inner), }] } } impl Clean for doctree::ForeignItem<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { - let inner = match self.kind { + let kind = match self.kind { hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => { let abi = cx.tcx.hir().get_foreign_abi(self.id); let (generics, decl) = @@ -2364,7 +2364,7 @@ impl Clean for doctree::ForeignItem<'_> { visibility: self.vis.clean(cx), stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner, + kind, } } } @@ -2380,7 +2380,7 @@ impl Clean for doctree::Macro<'_> { stability: cx.stability(self.hid), deprecation: cx.deprecation(self.hid).clean(cx), def_id: self.def_id, - inner: MacroItem(Macro { + kind: MacroItem(Macro { source: format!( "macro_rules! {} {{\n{}}}", name, @@ -2405,7 +2405,7 @@ impl Clean for doctree::ProcMacro<'_> { stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), - inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }), + kind: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }), } } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 32b3f69ecd4f0..5b1d38fb05f27 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -41,7 +41,7 @@ use crate::formats::item_type::ItemType; use crate::html::render::cache::ExternalLocation; use self::FnRetTy::*; -use self::ItemEnum::*; +use self::ItemKind::*; use self::SelfTy::*; use self::Type::*; @@ -81,7 +81,7 @@ pub struct Item { /// Not everything has a name. E.g., impls pub name: Option, pub attrs: Attributes, - pub inner: ItemEnum, + pub kind: ItemKind, pub visibility: Visibility, pub def_id: DefId, pub stability: Option, @@ -90,14 +90,13 @@ pub struct Item { impl fmt::Debug for Item { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let fake = self.is_fake(); - let def_id: &dyn fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id }; + let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id }; fmt.debug_struct("Item") .field("source", &self.source) .field("name", &self.name) .field("attrs", &self.attrs) - .field("inner", &self.inner) + .field("kind", &self.kind) .field("visibility", &self.visibility) .field("def_id", def_id) .field("stability", &self.stability) @@ -124,7 +123,7 @@ impl Item { } pub fn is_crate(&self) -> bool { - match self.inner { + match self.kind { StrippedItem(box ModuleItem(Module { is_crate: true, .. })) | ModuleItem(Module { is_crate: true, .. }) => true, _ => false, @@ -176,14 +175,14 @@ impl Item { self.type_() == ItemType::Keyword } pub fn is_stripped(&self) -> bool { - match self.inner { + match self.kind { StrippedItem(..) => true, ImportItem(ref i) => !i.should_be_displayed, _ => false, } } pub fn has_stripped_fields(&self) -> Option { - match self.inner { + match self.kind { StructItem(ref _struct) => Some(_struct.fields_stripped), UnionItem(ref union) => Some(union.fields_stripped), VariantItem(Variant { kind: VariantKind::Struct(ref vstruct) }) => { @@ -227,8 +226,8 @@ impl Item { } pub fn is_default(&self) -> bool { - match self.inner { - ItemEnum::MethodItem(ref meth) => { + match self.kind { + ItemKind::MethodItem(ref meth) => { if let Some(defaultness) = meth.defaultness { defaultness.has_value() && !defaultness.is_final() } else { @@ -248,7 +247,7 @@ impl Item { } #[derive(Clone, Debug)] -pub enum ItemEnum { +pub enum ItemKind { ExternCrateItem(String, Option), ImportItem(Import), StructItem(Struct), @@ -282,23 +281,23 @@ pub enum ItemEnum { AssocConstItem(Type, Option), AssocTypeItem(Vec, Option), /// An item that has been stripped by a rustdoc pass - StrippedItem(Box), + StrippedItem(Box), KeywordItem(String), } -impl ItemEnum { +impl ItemKind { pub fn is_type_alias(&self) -> bool { match *self { - ItemEnum::TypedefItem(_, _) | ItemEnum::AssocTypeItem(_, _) => true, + ItemKind::TypedefItem(_, _) | ItemKind::AssocTypeItem(_, _) => true, _ => false, } } pub fn as_assoc_kind(&self) -> Option { match *self { - ItemEnum::AssocConstItem(..) => Some(AssocKind::Const), - ItemEnum::AssocTypeItem(..) => Some(AssocKind::Type), - ItemEnum::TyMethodItem(..) | ItemEnum::MethodItem(..) => Some(AssocKind::Fn), + ItemKind::AssocConstItem(..) => Some(AssocKind::Const), + ItemKind::AssocTypeItem(..) => Some(AssocKind::Type), + ItemKind::TyMethodItem(..) | ItemKind::MethodItem(..) => Some(AssocKind::Fn), _ => None, } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index f6258221e322d..e5fb656cbb916 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -2,7 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder; use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg, - GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime, + GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemKind, Lifetime, MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding, TypeKind, Visibility, WherePredicate, }; @@ -44,8 +44,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { let mut module = module.clean(cx); let mut masked_crates = FxHashSet::default(); - match module.inner { - ItemEnum::ModuleItem(ref module) => { + match module.kind { + ItemKind::ModuleItem(ref module) => { for it in &module.items { // `compiler_builtins` should be masked too, but we can't apply // `#[doc(masked)]` to the injected `extern crate` because it's unstable. @@ -62,8 +62,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx); { - let m = match module.inner { - ItemEnum::ModuleItem(ref mut m) => m, + let m = match module.kind { + ItemKind::ModuleItem(ref mut m) => m, _ => unreachable!(), }; m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| Item { @@ -74,7 +74,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { stability: get_stability(cx, def_id), deprecation: get_deprecation(cx, def_id), def_id, - inner: ItemEnum::PrimitiveItem(prim), + kind: ItemKind::PrimitiveItem(prim), })); m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| Item { source: Span::empty(), @@ -84,7 +84,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { stability: get_stability(cx, def_id), deprecation: get_deprecation(cx, def_id), def_id, - inner: ItemEnum::KeywordItem(kw), + kind: ItemKind::KeywordItem(kw), })); } @@ -355,8 +355,8 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V let tcx = cx.tcx; for item in items { - let target = match item.inner { - ItemEnum::TypedefItem(ref t, true) => &t.type_, + let target = match item.kind { + ItemKind::TypedefItem(ref t, true) => &t.type_, _ => continue, }; let primitive = match *target { diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index d4ada3278e6a1..694051aa54f4d 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -5,9 +5,9 @@ pub struct StripItem(pub Item); impl StripItem { pub fn strip(self) -> Option { match self.0 { - Item { inner: StrippedItem(..), .. } => Some(self.0), + Item { kind: StrippedItem(..), .. } => Some(self.0), mut i => { - i.inner = StrippedItem(box i.inner); + i.kind = StrippedItem(box i.kind); Some(i) } } @@ -20,8 +20,8 @@ pub trait DocFolder: Sized { } /// don't override! - fn fold_inner_recur(&mut self, inner: ItemEnum) -> ItemEnum { - match inner { + fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind { + match kind { StrippedItem(..) => unreachable!(), ModuleItem(i) => ModuleItem(self.fold_mod(i)), StructItem(mut i) => { @@ -72,14 +72,14 @@ pub trait DocFolder: Sized { /// don't override! fn fold_item_recur(&mut self, item: Item) -> Option { - let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item; + let Item { attrs, name, source, visibility, def_id, kind, stability, deprecation } = item; - let inner = match inner { + let kind = match kind { StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)), - _ => self.fold_inner_recur(inner), + _ => self.fold_inner_recur(kind), }; - Some(Item { attrs, name, source, inner, visibility, stability, deprecation, def_id }) + Some(Item { attrs, name, source, kind, visibility, stability, deprecation, def_id }) } fn fold_mod(&mut self, m: Module) -> Module { diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index b99321e8484c9..277571b11f51b 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -218,7 +218,7 @@ impl DocFolder for Cache { // If this is a stripped module, // we don't want it or its children in the search index. - let orig_stripped_mod = match item.inner { + let orig_stripped_mod = match item.kind { clean::StrippedItem(box clean::ModuleItem(..)) => { mem::replace(&mut self.stripped_mod, true) } @@ -227,7 +227,7 @@ impl DocFolder for Cache { // If the impl is from a masked crate or references something from a // masked crate then remove it completely. - if let clean::ImplItem(ref i) = item.inner { + if let clean::ImplItem(ref i) = item.kind { if self.masked_crates.contains(&item.def_id.krate) || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) @@ -238,12 +238,12 @@ impl DocFolder for Cache { // Propagate a trait method's documentation to all implementors of the // trait. - if let clean::TraitItem(ref t) = item.inner { + if let clean::TraitItem(ref t) = item.kind { self.traits.entry(item.def_id).or_insert_with(|| t.clone()); } // Collect all the implementors of traits. - if let clean::ImplItem(ref i) = item.inner { + if let clean::ImplItem(ref i) = item.kind { if let Some(did) = i.trait_.def_id() { if i.blanket_impl.is_none() { self.implementors @@ -256,7 +256,7 @@ impl DocFolder for Cache { // Index this method for searching later on. if let Some(ref s) = item.name { - let (parent, is_inherent_impl_item) = match item.inner { + let (parent, is_inherent_impl_item) = match item.kind { clean::StrippedItem(..) => ((None, None), false), clean::AssocConstItem(..) | clean::TypedefItem(_, true) if self.parent_is_trait_impl => @@ -345,7 +345,7 @@ impl DocFolder for Cache { _ => false, }; - match item.inner { + match item.kind { clean::StructItem(..) | clean::EnumItem(..) | clean::TypedefItem(..) @@ -384,7 +384,7 @@ impl DocFolder for Cache { // Maintain the parent stack let orig_parent_is_trait_impl = self.parent_is_trait_impl; - let parent_pushed = match item.inner { + let parent_pushed = match item.kind { clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem @@ -422,12 +422,12 @@ impl DocFolder for Cache { // Once we've recursively found all the generics, hoard off all the // implementations elsewhere. let ret = self.fold_item_recur(item).and_then(|item| { - if let clean::Item { inner: clean::ImplItem(_), .. } = item { + if let clean::Item { kind: clean::ImplItem(_), .. } = item { // Figure out the id of this impl. This may map to a // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxHashSet::default(); - if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { + if let clean::Item { kind: clean::ImplItem(ref i), .. } = item { match i.for_ { clean::ResolvedPath { did, .. } | clean::BorrowedRef { diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 696bdae94fc88..a0f4502f750f1 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -60,12 +60,12 @@ impl Serialize for ItemType { impl<'a> From<&'a clean::Item> for ItemType { fn from(item: &'a clean::Item) -> ItemType { - let inner = match item.inner { + let kind = match item.kind { clean::StrippedItem(box ref item) => item, - ref inner => inner, + ref kind => kind, }; - match *inner { + match *kind { clean::ModuleItem(..) => ItemType::Module, clean::ExternCrateItem(..) => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs index dcb0184c58cd2..b893d6c64ec94 100644 --- a/src/librustdoc/formats/mod.rs +++ b/src/librustdoc/formats/mod.rs @@ -32,7 +32,7 @@ pub struct Impl { impl Impl { pub fn inner_impl(&self) -> &clean::Impl { - match self.impl_item.inner { + match self.impl_item.kind { clean::ImplItem(ref impl_) => impl_, _ => panic!("non-impl item found in impl"), } diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 90ace4d44c47d..273e281925742 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -86,7 +86,7 @@ pub fn run_format( } cx.mod_item_in(&item, &name, &cache)?; - let module = match item.inner { + let module = match item.kind { clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m, _ => unreachable!(), }; diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs index add28de17edef..0541bf118e14f 100644 --- a/src/librustdoc/html/render/cache.rs +++ b/src/librustdoc/html/render/cache.rs @@ -165,7 +165,7 @@ pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { } crate fn get_index_search_type(item: &clean::Item) -> Option { - let (all_types, ret_types) = match item.inner { + let (all_types, ret_types) = match item.kind { clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types), clean::MethodItem(ref m) => (&m.all_types, &m.ret_types), clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 5ac0ffcfbf1c2..404ba07ddfdb8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -618,7 +618,7 @@ impl FormatRenderer for Context { // Render sidebar-items.js used throughout this module. if !self.render_redirect_pages { - let module = match item.inner { + let module = match item.kind { clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m, _ => unreachable!(), }; @@ -1717,7 +1717,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) write!(buf, ""); // out-of-band write!(buf, ""); - let name = match item.inner { + let name = match item.kind { clean::ModuleItem(ref m) => { if m.is_crate { "Crate " @@ -1766,7 +1766,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache) write!(buf, ""); // in-band - match item.inner { + match item.kind { clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { item_function(buf, cx, item, f) @@ -2133,7 +2133,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: ); } - match myitem.inner { + match myitem.kind { clean::ExternCrateItem(ref name, ref src) => { use crate::html::format::anchor; @@ -2169,7 +2169,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: continue; } - let unsafety_flag = match myitem.inner { + let unsafety_flag = match myitem.kind { clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) if func.header.unsafety == hir::Unsafety::Unsafe => { @@ -2582,7 +2582,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, } for (pos, m) in provided.iter().enumerate() { render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait); - match m.inner { + match m.kind { clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => { write!(w, ",\n {{ ... }}\n"); } @@ -2958,7 +2958,7 @@ fn render_assoc_item( where_clause = WhereClause { gens: g, indent, end_newline } ) } - match item.inner { + match item.kind { clean::StrippedItem(..) => {} clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), @@ -2994,7 +2994,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct let mut fields = s .fields .iter() - .filter_map(|f| match f.inner { + .filter_map(|f| match f.kind { clean::StructFieldItem(ref ty) => Some((f, ty)), _ => None, }) @@ -3044,7 +3044,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, let mut fields = s .fields .iter() - .filter_map(|f| match f.inner { + .filter_map(|f| match f.kind { clean::StructFieldItem(ref ty) => Some((f, ty)), _ => None, }) @@ -3097,7 +3097,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca for v in &e.variants { write!(w, " "); let name = v.name.as_ref().unwrap(); - match v.inner { + match v.kind { clean::VariantItem(ref var) => match var.kind { clean::VariantKind::CLike => write!(w, "{}", name), clean::VariantKind::Tuple(ref tys) => { @@ -3147,7 +3147,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca id = id, name = variant.name.as_ref().unwrap() ); - if let clean::VariantItem(ref var) = variant.inner { + if let clean::VariantItem(ref var) = variant.kind { if let clean::VariantKind::Tuple(ref tys) = var.kind { write!(w, "("); for (i, ty) in tys.iter().enumerate() { @@ -3164,8 +3164,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca document_non_exhaustive(w, variant); use crate::clean::{Variant, VariantKind}; - if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner - { + if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.kind { let variant_id = cx.derive_id(format!( "{}.{}.fields", ItemType::Variant, @@ -3179,7 +3178,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca ); for field in &s.fields { use crate::clean::StructFieldItem; - if let StructFieldItem(ref ty) = field.inner { + if let StructFieldItem(ref ty) = field.kind { let id = cx.derive_id(format!( "variant.{}.field.{}", variant.name.as_ref().unwrap(), @@ -3275,7 +3274,7 @@ fn render_struct( let mut has_visible_fields = false; write!(w, " {{"); for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { + if let clean::StructFieldItem(ref ty) = field.kind { write!( w, "\n{} {}{}: {},", @@ -3306,7 +3305,7 @@ fn render_struct( if i > 0 { write!(w, ", "); } - match field.inner { + match field.kind { clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), clean::StructFieldItem(ref ty) => { write!(w, "{}{}", field.visibility.print_with_space(), ty.print()) @@ -3352,7 +3351,7 @@ fn render_union( write!(w, " {{\n{}", tab); for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { + if let clean::StructFieldItem(ref ty) = field.kind { write!( w, " {}{}: {},\n{}", @@ -3516,7 +3515,7 @@ fn render_deref_methods( .inner_impl() .items .iter() - .find_map(|item| match item.inner { + .find_map(|item| match item.kind { clean::TypedefItem(ref t, true) => Some(match *t { clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), _ => (&t.type_, &t.type_), @@ -3538,7 +3537,7 @@ fn render_deref_methods( } fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { - let self_type_opt = match item.inner { + let self_type_opt = match item.kind { clean::MethodItem(ref method) => method.decl.self_type(), clean::TyMethodItem(ref method) => method.decl.self_type(), _ => None, @@ -3589,7 +3588,7 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String { )); let t_did = impl_.trait_.def_id().unwrap(); for it in &impl_.items { - if let clean::TypedefItem(ref tydef, _) = it.inner { + if let clean::TypedefItem(ref tydef, _) = it.kind { out.push_str(" "); assoc_type( &mut out, @@ -3657,7 +3656,7 @@ fn render_impl( fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute); if show_def_docs { for it in &i.inner_impl().items { - if let clean::TypedefItem(ref tydef, _) = it.inner { + if let clean::TypedefItem(ref tydef, _) = it.kind { write!(w, " "); assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), ""); write!(w, ";"); @@ -3728,14 +3727,14 @@ fn render_impl( }; let (is_hidden, extra_class) = - if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_type_alias()) + if (trait_.is_none() || item.doc_value().is_some() || item.kind.is_type_alias()) && !is_default_item { (false, "") } else { (true, " hidden") }; - match item.inner { + match item.kind { clean::MethodItem(clean::Method { .. }) | clean::TyMethodItem(clean::TyMethod { .. }) => { // Only render when the method is not static or we allow static methods @@ -4000,7 +3999,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca write!( buffer, "

{}{}

", - match it.inner { + match it.kind { clean::StructItem(..) => "Struct ", clean::TraitItem(..) => "Trait ", clean::PrimitiveItem(..) => "Primitive Type ", @@ -4040,7 +4039,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca it.name.as_ref().expect("crates always have a name") ); } - match it.inner { + match it.kind { clean::StructItem(ref s) => sidebar_struct(buffer, it, s), clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), @@ -4180,7 +4179,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String { .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) { if let Some((target, real_target)) = - impl_.inner_impl().items.iter().find_map(|item| match item.inner { + impl_.inner_impl().items.iter().find_map(|item| match item.kind { clean::TypedefItem(ref t, true) => Some(match *t { clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), _ => (&t.type_, &t.type_), @@ -4319,8 +4318,8 @@ fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) -> } fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { - match item.inner { - clean::ItemEnum::ImplItem(ref i) => { + match item.kind { + clean::ItemKind::ImplItem(ref i) => { if let Some(ref trait_) = i.trait_ { Some(( format!("{:#}", i.for_.print()), @@ -4470,7 +4469,7 @@ fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { fn get_struct_fields_name(fields: &[clean::Item]) -> String { let mut fields = fields .iter() - .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) + .filter(|f| if let clean::StructFieldItem(..) = f.kind { true } else { false }) .filter_map(|f| match f.name { Some(ref name) => { Some(format!("{name}", name = name)) diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index ced26fcf5b0e9..ef68bae10782d 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -187,7 +187,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> { impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { fn fold_item(&mut self, i: clean::Item) -> Option { - match i.inner { + match i.kind { _ if !i.def_id.is_local() => { // non-local items are skipped because they can be out of the users control, // especially in the case of trait impls, which rustdoc eagerly inlines diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index e0cb5bf1a4ee6..fd0dd339abdc2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -671,7 +671,7 @@ fn resolve_associated_trait_item( let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did); let mut candidates: Vec<_> = implicit_impls .flat_map(|impl_outer| { - match impl_outer.inner { + match impl_outer.kind { clean::ImplItem(impl_) => { debug!("considering auto or blanket impl for trait {:?}", impl_.trait_); // Give precedence to methods that were overridden @@ -681,14 +681,14 @@ fn resolve_associated_trait_item( return None; } let kind = assoc - .inner + .kind .as_assoc_kind() .expect("inner items for a trait should be associated items"); if kind.namespace() != ns { return None; } - trace!("considering associated item {:?}", assoc.inner); + trace!("considering associated item {:?}", assoc.kind); // We have a slight issue: normal methods come from `clean` types, // but provided methods come directly from `tcx`. // Fortunately, we don't need the whole method, we just need to know @@ -832,7 +832,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id); } - let current_item = match item.inner { + let current_item = match item.kind { clean::ModuleItem(..) => { if item.attrs.inner_docs { if item.def_id.is_top_level_module() { item.name.clone() } else { None } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 5eb3f98b12371..81de07302478b 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -55,11 +55,11 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in &new_items { - if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.inner { + if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.kind { if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() { let target = items .iter() - .find_map(|item| match item.inner { + .find_map(|item| match item.kind { TypedefItem(ref t, true) => Some(&t.type_), _ => None, }) @@ -75,7 +75,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } new_items.retain(|it| { - if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.inner { + if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.kind { cleaner.keep_item(for_) || trait_.as_ref().map_or(false, |t| cleaner.keep_item(t)) || blanket_impl.is_some() @@ -96,7 +96,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { } if let Some(ref mut it) = krate.module { - if let ModuleItem(Module { ref mut items, .. }) = it.inner { + if let ModuleItem(Module { ref mut items, .. }) = it.kind { items.extend(synth.impls); items.extend(new_items); } else { diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 686ec51fb0604..432b9bee5ef85 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -58,7 +58,7 @@ impl crate::doctest::Tester for Tests { } pub fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { - if matches!(item.inner, + if matches!(item.kind, clean::StructFieldItem(_) | clean::VariantItem(_) | clean::AssocConstItem(_, _) diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index f82e72b488bb7..4b9e150eb1e30 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -41,7 +41,7 @@ impl<'a> DocFolder for Stripper<'a> { if i.attrs.lists(sym::doc).has_word(sym::hidden) { debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name); // use a dedicated hidden item for given item type if any - match i.inner { + match i.kind { clean::StructFieldItem(..) | clean::ModuleItem(..) => { // We need to recurse into stripped modules to // strip things like impl methods but when doing so diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 9b4f62235f5e8..4250c2b48fc50 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -13,7 +13,7 @@ pub struct Stripper<'a> { impl<'a> DocFolder for Stripper<'a> { fn fold_item(&mut self, i: Item) -> Option { - match i.inner { + match i.kind { clean::StrippedItem(..) => { // We need to recurse into stripped modules to strip things // like impl methods but when doing so we must not add any @@ -86,7 +86,7 @@ impl<'a> DocFolder for Stripper<'a> { clean::KeywordItem(..) => {} } - let fastreturn = match i.inner { + let fastreturn = match i.kind { // nothing left to do for traits (don't want to filter their // methods out, visibility controlled by the trait) clean::TraitItem(..) => true, @@ -123,7 +123,7 @@ pub struct ImplStripper<'a> { impl<'a> DocFolder for ImplStripper<'a> { fn fold_item(&mut self, i: Item) -> Option { - if let clean::ImplItem(ref imp) = i.inner { + if let clean::ImplItem(ref imp) = i.kind { // emptied none trait impls can be stripped if imp.trait_.is_none() && imp.items.is_empty() { return None; @@ -162,7 +162,7 @@ pub struct ImportStripper; impl DocFolder for ImportStripper { fn fold_item(&mut self, i: Item) -> Option { - match i.inner { + match i.kind { clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => { None } From b825ae7d28294de48297cb2e426f68c2331b3fdc Mon Sep 17 00:00:00 2001 From: Who? Me?! Date: Sat, 14 Nov 2020 07:20:25 -0600 Subject: [PATCH 45/46] Style nit Co-authored-by: matthewjasper <20113453+matthewjasper@users.noreply.github.com> --- src/test/ui/match/issue-72680.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/match/issue-72680.rs b/src/test/ui/match/issue-72680.rs index e6a723a6609f8..5b933edc8205e 100644 --- a/src/test/ui/match/issue-72680.rs +++ b/src/test/ui/match/issue-72680.rs @@ -11,7 +11,7 @@ fn main() { assert!(!f("a", 0)); assert!(!f("b", 0)); - assert!(!f("asdf", 032)); + assert!(!f("asdf", 32)); //// From 8cf35643106bba09b5d6c71ceac74dc58573f371 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Wed, 11 Nov 2020 13:15:15 +0000 Subject: [PATCH 46/46] Add underscore expressions for destructuring assignments Co-authored-by: varkor --- compiler/rustc_ast/src/ast.rs | 3 + compiler/rustc_ast/src/mut_visit.rs | 1 + compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_lowering/src/expr.rs | 19 +++- compiler/rustc_ast_passes/src/feature_gate.rs | 6 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 3 + src/test/ui/cross/cross-file-errors/main.rs | 3 +- .../ui/cross/cross-file-errors/main.stderr | 22 ++++- .../nested_destructure.rs | 3 + .../slice_destructure.rs | 2 + .../slice_destructure_fail.rs | 1 + .../slice_destructure_fail.stderr | 8 +- .../struct_destructure.rs | 6 +- .../struct_destructure_fail.rs | 2 + .../struct_destructure_fail.stderr | 32 ++++++- .../tuple_destructure.rs | 2 + .../tuple_destructure_fail.rs | 1 + .../tuple_destructure_fail.stderr | 13 ++- .../tuple_struct_destructure.rs | 4 +- .../tuple_struct_destructure_fail.rs | 4 + .../tuple_struct_destructure_fail.stderr | 28 ++++-- .../underscore-range-expr-gating.rs | 2 + .../underscore-range-expr-gating.stderr | 13 ++- ...fn-or-tuple-struct-with-underscore-args.rs | 18 ++-- ...r-tuple-struct-with-underscore-args.stderr | 89 +++++++++++++++---- .../clippy/clippy_lints/src/utils/sugg.rs | 1 + 27 files changed, 243 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3e953729aabec..328086af183d6 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1192,6 +1192,7 @@ impl Expr { ExprKind::Field(..) => ExprPrecedence::Field, ExprKind::Index(..) => ExprPrecedence::Index, ExprKind::Range(..) => ExprPrecedence::Range, + ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, ExprKind::Break(..) => ExprPrecedence::Break, @@ -1324,6 +1325,8 @@ pub enum ExprKind { Index(P, P), /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). Range(Option>, Option>, RangeLimits), + /// An underscore, used in destructuring assignment to ignore a value. + Underscore, /// Variable reference, possibly containing `::` and/or type /// parameters (e.g., `foo::bar::`). diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 26097980e8be4..ddae0ab03e404 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1232,6 +1232,7 @@ pub fn noop_visit_expr( visit_opt(e1, |e1| vis.visit_expr(e1)); visit_opt(e2, |e2| vis.visit_expr(e2)); } + ExprKind::Underscore => {} ExprKind::Path(qself, path) => { vis.visit_qself(qself); vis.visit_path(path); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 49b521afcdc78..560064182e18d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -806,6 +806,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_expr, start); walk_list!(visitor, visit_expr, end); } + ExprKind::Underscore => {} ExprKind::Path(ref maybe_qself, ref path) => { if let Some(ref qself) = *maybe_qself { visitor.visit_ty(&qself.ty); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 330776fc8c598..ecbe97bd45a0b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -164,6 +164,16 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Range(ref e1, ref e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } + ExprKind::Underscore => { + self.sess + .struct_span_err( + e.span, + "in expressions, `_` can only be used on the left-hand side of an assignment", + ) + .span_label(e.span, "`_` not allowed here") + .emit(); + hir::ExprKind::Err + } ExprKind::Path(ref qself, ref path) => { let qpath = self.lower_qpath( e.id, @@ -863,7 +873,10 @@ impl<'hir> LoweringContext<'_, 'hir> { // Return early in case of an ordinary assignment. fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool { match &lhs.kind { - ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false, + ExprKind::Array(..) + | ExprKind::Struct(..) + | ExprKind::Tup(..) + | ExprKind::Underscore => false, // Check for tuple struct constructor. ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(), ExprKind::Paren(e) => { @@ -943,6 +956,10 @@ impl<'hir> LoweringContext<'_, 'hir> { assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { match &lhs.kind { + // Underscore pattern. + ExprKind::Underscore => { + return self.pat_without_dbm(lhs.span, hir::PatKind::Wild); + } // Slice patterns. ExprKind::Array(elements) => { let (pats, rest) = diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2831675cb3671..181783441f3ff 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); - gate_all!(destructuring_assignment, "destructuring assignments are unstable"); + if sess.parse_sess.span_diagnostic.err_count() == 0 { + // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is + // involved, so we only emit errors where there are no other parsing errors. + gate_all!(destructuring_assignment, "destructuring assignments are unstable"); + } // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index a566200c33896..887b60f98f75f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2068,6 +2068,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren(e, fake_prec); } } + ast::ExprKind::Underscore => self.s.word("_"), ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), ast::ExprKind::Break(opt_label, ref opt_expr) => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 188bf227c4249..ffbf786491df2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1089,6 +1089,9 @@ impl<'a> Parser<'a> { self.parse_yield_expr(attrs) } else if self.eat_keyword(kw::Let) { self.parse_let_expr(attrs) + } else if self.eat_keyword(kw::Underscore) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); + Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { // Don't complain about bare semicolons after unclosed braces // recovery in order to keep the error count down. Fixing the diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs index 74e9461803c52..1902ab94d4c0d 100644 --- a/src/test/ui/cross/cross-file-errors/main.rs +++ b/src/test/ui/cross/cross-file-errors/main.rs @@ -3,5 +3,6 @@ mod underscore; fn main() { underscore!(); - //~^ ERROR expected expression, found reserved identifier `_` + //~^ ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR destructuring assignments are unstable } diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr index f9101d8a583d3..b8658745060b0 100644 --- a/src/test/ui/cross/cross-file-errors/main.stderr +++ b/src/test/ui/cross/cross-file-errors/main.stderr @@ -1,15 +1,31 @@ -error: expected expression, found reserved identifier `_` +error[E0658]: destructuring assignments are unstable --> $DIR/underscore.rs:8:9 | LL | _ - | ^ expected expression + | ^ | ::: $DIR/main.rs:5:5 | LL | underscore!(); | -------------- in this macro invocation | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/underscore.rs:8:9 + | +LL | _ + | ^ `_` not allowed here + | + ::: $DIR/main.rs:5:5 + | +LL | underscore!(); + | -------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs index 393dfc16c0a1c..0d45ff7da7249 100644 --- a/src/test/ui/destructuring-assignment/nested_destructure.rs +++ b/src/test/ui/destructuring-assignment/nested_destructure.rs @@ -14,4 +14,7 @@ fn main() { Struct { a: TupleStruct((a, b), c), b: [d] } = Struct { a: TupleStruct((0, 1), 2), b: [3] }; assert_eq!((a, b, c, d), (0, 1, 2, 3)); + + // unnested underscore: just discard + _ = 1; } diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs index 3dd10aff19c72..76cdc1260fcde 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -9,6 +9,8 @@ fn main() { let mut c; [a, .., b, c] = [1, 2, 3, 4, 5]; assert_eq!((a, b, c), (1, 4, 5)); + [_, a, _] = [1, 2, 3]; + assert_eq!((a, b), (2, 4)); [..] = [1, 2, 3]; [c, ..] = [5, 6, 6]; assert_eq!(c, 5); diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs index f636ea3511c26..90d93892f7f22 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -4,4 +4,5 @@ fn main() { let (mut a, mut b); [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2 + [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2 } diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr index 728687deb8bbb..cc412c72df51d 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2 LL | [a, a, b] = [1, 2]; | ^^^^^^^^^ expected 2 elements -error: aborting due to 2 previous errors +error[E0527]: pattern requires 1 element but array has 2 + --> $DIR/slice_destructure_fail.rs:7:3 + | +LL | [_] = [1, 2]; + | ^^^ expected 2 elements + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0527`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs index b3a96ee157346..2bcbd9d0d742e 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -12,8 +12,10 @@ fn main() { assert_eq!((a, b), (0, 1)); Struct { a: b, b: a } = Struct { a: 1, b: 2 }; assert_eq!((a,b), (2, 1)); + Struct { a: _, b } = Struct { a: 1, b: 2 }; + assert_eq!((a, b), (2, 2)); Struct { a, .. } = Struct { a: 1, b: 3 }; - assert_eq!((a, b), (1, 1)); + assert_eq!((a, b), (1, 2)); Struct { .. } = Struct { a: 1, b: 4 }; - assert_eq!((a, b), (1, 1)); + assert_eq!((a, b), (1, 2)); } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs index c22695ed38849..4aa327b61f497 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -9,6 +9,8 @@ fn main() { let mut c; let d = Struct { a: 0, b: 1 }; Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` + Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` + //~| ERROR expected identifier, found reserved identifier `_` Struct { a, ..d } = Struct { a: 1, b: 2 }; //~^ ERROR functional record updates are not allowed in destructuring assignments Struct { a, .. }; //~ ERROR base expression required after `..` diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index 4da4698804f1a..81661a357e750 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -1,11 +1,19 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/struct_destructure_fail.rs:12:17 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ------ ^ expected identifier, found reserved identifier + | | + | while parsing this struct + error: functional record updates are not allowed in destructuring assignments - --> $DIR/struct_destructure_fail.rs:12:19 + --> $DIR/struct_destructure_fail.rs:14:19 | LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; | ^ help: consider removing the trailing pattern error: base expression required after `..` - --> $DIR/struct_destructure_fail.rs:14:19 + --> $DIR/struct_destructure_fail.rs:16:19 | LL | Struct { a, .. }; | ^ add a base expression here @@ -16,6 +24,22 @@ error[E0026]: struct `Struct` does not have a field named `c` LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; | ^ struct `Struct` does not have this field -error: aborting due to 3 previous errors +error[E0027]: pattern does not mention field `b` + --> $DIR/struct_destructure_fail.rs:12:5 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ^^^^^^^^^^^^^^^ missing field `b` + | +help: include the missing field in the pattern + | +LL | Struct { a, b, _ } = Struct { a: 1, b: 2 }; + | ^^^ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | Struct { a, .., _ } = Struct { a: 1, b: 2 }; + | ^^^^ + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0026`. +Some errors have detailed explanations: E0026, E0027. +For more information about an error, try `rustc --explain E0026`. diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs index 16aafc4693f3f..2096182d421cf 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -16,6 +16,8 @@ fn main() { assert_eq!((a, b), (2, 2)); (b, ..) = (5, 6, 7); assert_eq!(b, 5); + (a, _) = (8, 9); + assert_eq!(a, 8); // Test for a non-Copy type (String): let (mut c, mut d); diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs index b76f4968e6249..5524e91dc401b 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -7,4 +7,5 @@ fn main() { (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern (a, a, b) = (1, 2); //~ ERROR mismatched types (C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment + (_,) = (1, 2); //~ ERROR mismatched types } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr index a60e1cb1eec62..1146b88278d49 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -25,7 +25,18 @@ LL | (C, ..) = (0,1); | | | cannot assign to this expression -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/tuple_destructure_fail.rs:10:5 + | +LL | (_,) = (1, 2); + | ^^^^ ------ this expression has type `({integer}, {integer})` + | | + | expected a tuple with 2 elements, found one with 1 element + | + = note: expected type `({integer}, {integer})` + found tuple `(_,)` + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0070, E0308. For more information about an error, try `rustc --explain E0070`. diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs index 106a9b16db459..7b5c5ad2bae26 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -23,8 +23,10 @@ fn main() { assert_eq!((a, b), (0, 1)); TupleStruct(a, .., b) = TupleStruct(1, 2); assert_eq!((a, b), (1, 2)); + TupleStruct(_, a) = TupleStruct(2, 2); + assert_eq!((a, b), (2, 2)); TupleStruct(..) = TupleStruct(3, 4); - assert_eq!((a, b), (1, 2)); + assert_eq!((a, b), (2, 2)); TupleStruct(5,6).assign(&mut a, &mut b); assert_eq!((a, b), (5, 6)); Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8); diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs index 61ae42a51751f..c39db06117767 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -29,8 +29,12 @@ fn main() { TupleStruct(a, a, b) = TupleStruct(1, 2); //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields + TupleStruct(_) = TupleStruct(1, 2); + //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2); //~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields + Enum::SingleVariant(_) = Enum::SingleVariant(1, 2); + //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields // Check if `test` is recognized as not a tuple struct but a function call: test() = TupleStruct(0, 0); diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index 863eedecf7697..0e7174e5b19d6 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -23,17 +23,35 @@ LL | struct TupleStruct(S, T); LL | TupleStruct(a, a, b) = TupleStruct(1, 2); | ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3 -error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields +error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields --> $DIR/tuple_struct_destructure_fail.rs:32:5 | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +LL | TupleStruct(_) = TupleStruct(1, 2); + | ^^^^^^^^^^^^^^ expected 2 fields, found 1 + +error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:34:5 + | LL | SingleVariant(S, T) | ------------------- tuple variant defined here ... LL | Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3 +error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:36:5 + | +LL | SingleVariant(S, T) + | ------------------- tuple variant defined here +... +LL | Enum::SingleVariant(_) = Enum::SingleVariant(1, 2); + | ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1 + error[E0070]: invalid left-hand side of assignment - --> $DIR/tuple_struct_destructure_fail.rs:36:12 + --> $DIR/tuple_struct_destructure_fail.rs:40:12 | LL | test() = TupleStruct(0, 0); | ------ ^ @@ -41,7 +59,7 @@ LL | test() = TupleStruct(0, 0); | cannot assign to this expression error[E0070]: invalid left-hand side of assignment - --> $DIR/tuple_struct_destructure_fail.rs:38:14 + --> $DIR/tuple_struct_destructure_fail.rs:42:14 | LL | (test)() = TupleStruct(0, 0); | -------- ^ @@ -49,14 +67,14 @@ LL | (test)() = TupleStruct(0, 0); | cannot assign to this expression error[E0070]: invalid left-hand side of assignment - --> $DIR/tuple_struct_destructure_fail.rs:40:38 + --> $DIR/tuple_struct_destructure_fail.rs:44:38 | LL | as Test>::test() = TupleStruct(0, 0); | -------------------------------- ^ | | | cannot assign to this expression -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0023, E0070. For more information about an error, try `rustc --explain E0023`. diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs index b41f2f52a3d6f..4ed4f56702c32 100644 --- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs @@ -4,5 +4,7 @@ struct S { x : u32 } #[cfg(FALSE)] fn foo() { + _; //~ ERROR destructuring assignments are unstable + S { x: 5, .. }; //~ ERROR destructuring assignments are unstable } diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr index 442e36cd3065e..a5ed761a01c33 100644 --- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr @@ -1,5 +1,14 @@ error[E0658]: destructuring assignments are unstable - --> $DIR/underscore-range-expr-gating.rs:7:15 + --> $DIR/underscore-range-expr-gating.rs:7:5 + | +LL | _; + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore-range-expr-gating.rs:9:15 | LL | S { x: 5, .. }; | ^^ @@ -7,6 +16,6 @@ LL | S { x: 5, .. }; = note: see issue #71126 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs index a8ea3faefe876..00638e04f5db9 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs @@ -8,12 +8,18 @@ trait T { fn main() { let _: usize = foo(_, _); - //~^ ERROR expected expression - //~| ERROR expected expression + //~^ ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: S = S(_, _); - //~^ ERROR expected expression - //~| ERROR expected expression + //~^ ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: usize = T::baz(_, _); - //~^ ERROR expected expression - //~| ERROR expected expression + //~^ ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR `_` can only be used on the left-hand side of an assignment + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable } diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr index a6d1c4b859f2f..248fa6b9c9cb2 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr @@ -1,38 +1,93 @@ -error: expected expression, found reserved identifier `_` +error[E0658]: destructuring assignments are unstable --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 | LL | let _: usize = foo(_, _); - | ^ expected expression + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27 + | +LL | let _: usize = foo(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18 + | +LL | let _: S = S(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21 + | +LL | let _: S = S(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 + | +LL | let _: usize = foo(_, _); + | ^ `_` not allowed here -error: expected expression, found reserved identifier `_` +error: in expressions, `_` can only be used on the left-hand side of an assignment --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27 | LL | let _: usize = foo(_, _); - | ^ expected expression + | ^ `_` not allowed here -error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18 +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18 | LL | let _: S = S(_, _); - | ^ expected expression + | ^ `_` not allowed here -error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21 +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21 | LL | let _: S = S(_, _); - | ^ expected expression + | ^ `_` not allowed here -error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27 +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27 | LL | let _: usize = T::baz(_, _); - | ^ expected expression + | ^ `_` not allowed here -error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30 +error: in expressions, `_` can only be used on the left-hand side of an assignment + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30 | LL | let _: usize = T::baz(_, _); - | ^ expected expression + | ^ `_` not allowed here -error: aborting due to 6 previous errors +error: aborting due to 12 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs index 625120b880eb5..1fcd41e4dbfed 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs @@ -170,6 +170,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Paren(..) + | ast::ExprKind::Underscore | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..)