From 1cfc1874b526fd8a681ebfaf64c554077586c8b1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 24 May 2021 18:18:32 +0200 Subject: [PATCH 01/15] document PartialEq, PartialOrd, Ord requirements more explicitly --- library/core/src/cmp.rs | 76 ++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 3f0acf435fe8d..fc02e3a3a1b5c 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -27,12 +27,23 @@ use self::Ordering::*; /// Trait for equality comparisons which are [partial equivalence /// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation). /// +/// `x.eq(y)` can also be written `x == y`, and `x.ne(y)` can be written `x != y`. +/// We use the easier-to-read infix notation in the remainder of this documentation. +/// /// This trait allows for partial equality, for types that do not have a full /// equivalence relation. For example, in floating point numbers `NaN != NaN`, /// so floating point types implement `PartialEq` but not [`trait@Eq`]. /// -/// Formally, the equality must be (for all `a`, `b`, `c` of type `A`, `B`, -/// `C`): +/// Implementations must ensure that `eq` and `ne` are consistent with each other: +/// `a != b` if and only if `!(a == b)`. +/// This is ensured by the default implementation of `ne`. +/// If [`PartialOrd`] or [`Ord`] are also implemented for `Self` and `Rhs`, their methods must also +/// be consistent with `PartialEq` (see the documentation of those traits for the exact +/// requirememts). It's easy to accidentally make them disagree by deriving some of the traits and +/// manually implementing others. +/// +/// The equality relation `==` must satisfy the following conditions +/// (for all `a`, `b`, `c` of type `A`, `B`, `C`): /// /// - **Symmetric**: if `A: PartialEq` and `B: PartialEq`, then **`a == b` /// implies `b == a`**; and @@ -53,15 +64,6 @@ use self::Ordering::*; /// /// ## How can I implement `PartialEq`? /// -/// `PartialEq` only requires the [`eq`] method to be implemented; [`ne`] is defined -/// in terms of it by default. Any manual implementation of [`ne`] *must* respect -/// the rule that [`eq`] is a strict inverse of [`ne`]; that is, `!(a == b)` if and -/// only if `a != b`. -/// -/// Implementations of `PartialEq`, [`PartialOrd`], and [`Ord`] *must* agree with -/// each other. It's easy to accidentally make them disagree by deriving some -/// of the traits and manually implementing others. -/// /// An example implementation for a domain in which two books are considered /// the same book if their ISBN matches, even if the formats differ: /// @@ -634,7 +636,19 @@ impl Clone for Reverse { /// An order is a total order if it is (for all `a`, `b` and `c`): /// /// - total and asymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and -/// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. +/// - transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. +/// +/// Implementations of [`PartialEq`], [`PartialOrd`], and `Ord` *must* agree with each other (for +/// all `a` and `b`): +/// +/// - `a.partial_cmp(b) == Some(a.cmp(b))` +/// - `a.cmp(b) == Ordering::Equal` if and only if `a == b` +/// (already follows from the above and the requirements of `PartialOrd`) +/// +/// It's easy to accidentally make them disagree by +/// deriving some of the traits and manually implementing others. +/// +/// Furthermore, the `max`, `min`, and `clamp` methods of this trait must be consistent with `cmp`. /// /// ## Derivable /// @@ -659,12 +673,6 @@ impl Clone for Reverse { /// Then you must define an implementation for [`cmp`]. You may find it useful to use /// [`cmp`] on your type's fields. /// -/// Implementations of [`PartialEq`], [`PartialOrd`], and `Ord` *must* -/// agree with each other. That is, `a.cmp(b) == Ordering::Equal` if -/// and only if `a == b` and `Some(a.cmp(b)) == a.partial_cmp(b)` for -/// all `a` and `b`. It's easy to accidentally make them disagree by -/// deriving some of the traits and manually implementing others. -/// /// Here's an example where you want to sort people by height only, disregarding `id` /// and `name`: /// @@ -824,15 +832,41 @@ impl PartialOrd for Ordering { /// Trait for values that can be compared for a sort-order. /// +/// The `lt`, `le`, `gt`, and `ge` methods of this trait can be called using +/// the `<`, `<=`, `>`, and `>=` operators, respectively. +/// +/// The methods of this trait must be consistent with each other and with those of `PartialEq` in +/// the following sense: +/// +/// - `a == b` if and only if `partial_cmp(a, b) == Some(Equal)`. +/// - `a < b` if and only if `partial_cmp(a, b) == Some(Less)`. +/// - `a > b` if and only if `partial_cmp(a, b) == Some(Greater)`. +/// - `a <= b` if and only if `a < b || a == b`. +/// - `a >= b` if and only if `a > b || a == b`. +/// - `a != b` if and only if `!(a == b)` (already part of `PartialEq`). +/// +/// If [`Ord`] is also implemented for `Self` and `Rhs`, it must also be consistent with +/// `partial_cmp` (see the documentation of that trait for the exact requirements). It's +/// easy to accidentally make them disagree by deriving some of the traits and manually +/// implementing others. +/// /// The comparison must satisfy, for all `a`, `b` and `c`: /// -/// - asymmetry: if `a < b` then `!(a > b)`, as well as `a > b` implying `!(a < b)`; and /// - transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. +/// - duality: `a < b` if and only if `b > a`. /// /// Note that these requirements mean that the trait itself must be implemented symmetrically and /// transitively: if `T: PartialOrd` and `U: PartialOrd` then `U: PartialOrd` and `T: /// PartialOrd`. /// +/// ## Corollaries +/// +/// The following corollaries follow from the above requirements: +/// +/// - irreflexivity of `<` and `>`: `!(a < a)`, `!(a > a)` +/// - transitivity of `>`: if `a > b` and `b > c` then `a > c` +/// - duality of `partial_cmp`: `partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)` +/// /// ## Derivable /// /// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a @@ -850,10 +884,6 @@ impl PartialOrd for Ordering { /// /// `PartialOrd` requires your type to be [`PartialEq`]. /// -/// Implementations of [`PartialEq`], `PartialOrd`, and [`Ord`] *must* agree with each other. It's -/// easy to accidentally make them disagree by deriving some of the traits and manually -/// implementing others. -/// /// If your type is [`Ord`], you can implement [`partial_cmp`] by using [`cmp`]: /// /// ``` From a7abd130926a42f1cbaf4c03d4a5fa3450bd3789 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 27 May 2021 12:01:49 +0200 Subject: [PATCH 02/15] make Ord doc style consistent with the other two; explain which properties are ensured by default impls --- library/core/src/cmp.rs | 43 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index fc02e3a3a1b5c..a17cd63258335 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -35,8 +35,10 @@ use self::Ordering::*; /// so floating point types implement `PartialEq` but not [`trait@Eq`]. /// /// Implementations must ensure that `eq` and `ne` are consistent with each other: -/// `a != b` if and only if `!(a == b)`. -/// This is ensured by the default implementation of `ne`. +/// +/// - `a != b` if and only if `!(a == b)` +/// (ensured by the default implementation). +/// /// If [`PartialOrd`] or [`Ord`] are also implemented for `Self` and `Rhs`, their methods must also /// be consistent with `PartialEq` (see the documentation of those traits for the exact /// requirememts). It's easy to accidentally make them disagree by deriving some of the traits and @@ -633,22 +635,25 @@ impl Clone for Reverse { /// Trait for types that form a [total order](https://en.wikipedia.org/wiki/Total_order). /// -/// An order is a total order if it is (for all `a`, `b` and `c`): +/// Implementations must ensure to be consistent with the [`PartialOrd`] implementation, and that +/// `max`, `min`, and `clamp` are consistent with `cmp`: /// -/// - total and asymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and -/// - transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. +/// - `partial_cmp(a, b) == Some(cmp(a, b))`. +/// - `max(a, b) == max_by(a, b, cmp)` (ensured by the default implementation). +/// - `min(a, b) == min_by(a, b, cmp)` (ensured by the default implementation). +/// - For `a.clamp(min, max)`, see the [method docs](#method.clamp) +/// (ensured by the default implementation). /// -/// Implementations of [`PartialEq`], [`PartialOrd`], and `Ord` *must* agree with each other (for -/// all `a` and `b`): +/// It's easy to accidentally make `cmp` and `partial_cmp` disagree by +/// deriving some of the traits and manually implementing others. /// -/// - `a.partial_cmp(b) == Some(a.cmp(b))` -/// - `a.cmp(b) == Ordering::Equal` if and only if `a == b` -/// (already follows from the above and the requirements of `PartialOrd`) +/// ## Corollaries /// -/// It's easy to accidentally make them disagree by -/// deriving some of the traits and manually implementing others. +/// From the above and the requirements of `PartialOrd`, it follows that `<` defines a strict total order. +/// This means that for all `a`, `b` and `c`: /// -/// Furthermore, the `max`, `min`, and `clamp` methods of this trait must be consistent with `cmp`. +/// - exactly one of `a < b`, `a == b` or `a > b` is true; and +/// - `<` is transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. /// /// ## Derivable /// @@ -839,10 +844,14 @@ impl PartialOrd for Ordering { /// the following sense: /// /// - `a == b` if and only if `partial_cmp(a, b) == Some(Equal)`. -/// - `a < b` if and only if `partial_cmp(a, b) == Some(Less)`. -/// - `a > b` if and only if `partial_cmp(a, b) == Some(Greater)`. -/// - `a <= b` if and only if `a < b || a == b`. -/// - `a >= b` if and only if `a > b || a == b`. +/// - `a < b` if and only if `partial_cmp(a, b) == Some(Less)` +/// (ensured by the default implementation). +/// - `a > b` if and only if `partial_cmp(a, b) == Some(Greater)` +/// (ensured by the default implementation). +/// - `a <= b` if and only if `a < b || a == b` +/// (ensured by the default implementation). +/// - `a >= b` if and only if `a > b || a == b` +/// (ensured by the default implementation). /// - `a != b` if and only if `!(a == b)` (already part of `PartialEq`). /// /// If [`Ord`] is also implemented for `Self` and `Rhs`, it must also be consistent with From 7dd2577abf7420cee54b0281448442d7bcf72165 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 8 Jun 2021 11:28:06 -0700 Subject: [PATCH 03/15] Fix linkchecker redirection tests. These were changed in #84703. --- src/tools/linkchecker/tests/broken_redir/redir-bad.html | 1 + src/tools/linkchecker/tests/redirect_loop/redir-bad.html | 1 + src/tools/linkchecker/tests/valid/inner/redir-bad.html | 1 + src/tools/linkchecker/tests/valid/inner/redir.html | 1 + 4 files changed, 4 insertions(+) diff --git a/src/tools/linkchecker/tests/broken_redir/redir-bad.html b/src/tools/linkchecker/tests/broken_redir/redir-bad.html index 3e376629f74fe..9c580d8e07ee8 100644 --- a/src/tools/linkchecker/tests/broken_redir/redir-bad.html +++ b/src/tools/linkchecker/tests/broken_redir/redir-bad.html @@ -2,6 +2,7 @@ + Redirection diff --git a/src/tools/linkchecker/tests/redirect_loop/redir-bad.html b/src/tools/linkchecker/tests/redirect_loop/redir-bad.html index fe7780e6739bf..bc567caa78b7e 100644 --- a/src/tools/linkchecker/tests/redirect_loop/redir-bad.html +++ b/src/tools/linkchecker/tests/redirect_loop/redir-bad.html @@ -2,6 +2,7 @@ + Redirection

Redirecting to redir-bad.html...

diff --git a/src/tools/linkchecker/tests/valid/inner/redir-bad.html b/src/tools/linkchecker/tests/valid/inner/redir-bad.html index d21336e7e738b..f32683efe67ec 100644 --- a/src/tools/linkchecker/tests/valid/inner/redir-bad.html +++ b/src/tools/linkchecker/tests/valid/inner/redir-bad.html @@ -2,6 +2,7 @@ + Redirection

Redirecting to xxx...

diff --git a/src/tools/linkchecker/tests/valid/inner/redir.html b/src/tools/linkchecker/tests/valid/inner/redir.html index 1808b23aed856..3a52a89738537 100644 --- a/src/tools/linkchecker/tests/valid/inner/redir.html +++ b/src/tools/linkchecker/tests/valid/inner/redir.html @@ -2,6 +2,7 @@ + Redirection

Redirecting to redir-target.html...

From bbd053216341cd3843b8f12da791f533e4480b8d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 8 Jun 2021 16:15:43 -0700 Subject: [PATCH 04/15] Test the linkchecker itself. --- src/bootstrap/builder/tests.rs | 5 +++++ src/bootstrap/test.rs | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 4d7c207e3ab8b..e7fb8c0d4d5d2 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -613,9 +613,14 @@ mod dist { // Note that the stages here are +1 than what they actually are because // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is // not 0. + // + // The stage 0 copy is the one downloaded for bootstrapping. It is + // (currently) needed to run "cargo test" on the linkchecker, and + // should be relatively "free". assert_eq!( first(builder.cache.all::()), &[ + tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }, tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, ] diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index cc7c143d47461..9debfee58e25f 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -124,8 +124,25 @@ You can skip linkcheck with --exclude src/tools/linkchecker" builder.info(&format!("Linkcheck ({})", host)); + // Test the linkchecker itself. + let bootstrap_host = builder.config.build; + let compiler = builder.compiler(0, bootstrap_host); + let cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolBootstrap, + bootstrap_host, + "test", + "src/tools/linkchecker", + SourceType::InTree, + &[], + ); + try_run(builder, &mut cargo.into()); + + // Build all the default documentation. builder.default_doc(&[]); + // Run the linkchecker. let _time = util::timeit(&builder); try_run( builder, From dbc8a1c25a79fa5ce1f1343b0d9c2ede161eb467 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 8 Jun 2021 16:23:19 -0700 Subject: [PATCH 05/15] Change the linkchecker self-tests to validate more output. --- src/tools/linkchecker/tests/checks.rs | 49 +++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/tools/linkchecker/tests/checks.rs b/src/tools/linkchecker/tests/checks.rs index c6ec999e5cfe2..531c323a9bcb6 100644 --- a/src/tools/linkchecker/tests/checks.rs +++ b/src/tools/linkchecker/tests/checks.rs @@ -15,7 +15,7 @@ fn run(dirname: &str) -> (ExitStatus, String, String) { fn broken_test(dirname: &str, expected: &str) { let (status, stdout, stderr) = run(dirname); assert!(!status.success()); - if !stdout.contains(expected) { + if !contains(expected, &stdout) { panic!( "stdout did not contain expected text: {}\n\ --- stdout:\n\ @@ -27,6 +27,25 @@ fn broken_test(dirname: &str, expected: &str) { } } +fn contains(expected: &str, actual: &str) -> bool { + // Normalize for Windows paths. + let actual = actual.replace('\\', "/"); + actual.lines().any(|mut line| { + for (i, part) in expected.split("[..]").enumerate() { + match line.find(part) { + Some(j) => { + if i == 0 && j != 0 { + return false; + } + line = &line[j + part.len()..]; + } + None => return false, + } + } + line.is_empty() || expected.ends_with("[..]") + }) +} + fn valid_test(dirname: &str) { let (status, stdout, stderr) = run(dirname); if !status.success() { @@ -48,30 +67,46 @@ fn valid() { #[test] fn basic_broken() { - broken_test("basic_broken", "bar.html"); + broken_test("basic_broken", "foo.html:3: broken link - `bar.html`"); } #[test] fn broken_fragment_local() { - broken_test("broken_fragment_local", "#somefrag"); + broken_test( + "broken_fragment_local", + "foo.html:3: broken link fragment `#somefrag` pointing to `foo.html`", + ); } #[test] fn broken_fragment_remote() { - broken_test("broken_fragment_remote/inner", "#somefrag"); + broken_test( + "broken_fragment_remote/inner", + "foo.html:3: broken link fragment `#somefrag` pointing to `foo.html`", + ); } #[test] fn broken_redir() { - broken_test("broken_redir", "sometarget"); + broken_test( + "broken_redir", + "foo.html:3: broken redirect from `redir-bad.html` to `sometarget`", + ); } #[test] fn directory_link() { - broken_test("directory_link", "somedir"); + broken_test( + "directory_link", + "foo.html:3: directory link to `somedir` (directory links should use index.html instead)", + ); } #[test] fn redirect_loop() { - broken_test("redirect_loop", "redir-bad.html"); + broken_test( + "redirect_loop", + "foo.html:3: redirect from `redir-bad.html` to `[..]redirect_loop/redir-bad.html` \ + which is also a redirect (not supported)", + ); } From 1e9f0b367e0b7dc9902502cfa09bcc52d2215261 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 8 Jun 2021 17:12:48 -0700 Subject: [PATCH 06/15] linkchecker: Fix bug where fragment errors printed the wrong path. --- src/tools/linkchecker/main.rs | 2 +- src/tools/linkchecker/tests/checks.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 47960c3f6cc2b..15edd628cdffc 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -347,7 +347,7 @@ impl Checker { } else { report.errors += 1; print!("{}:{}: broken link fragment ", pretty_path, i + 1); - println!("`#{}` pointing to `{}`", fragment, pretty_path); + println!("`#{}` pointing to `{}`", fragment, target_pretty_path); }; } }); diff --git a/src/tools/linkchecker/tests/checks.rs b/src/tools/linkchecker/tests/checks.rs index 531c323a9bcb6..1a0b1b00e0de0 100644 --- a/src/tools/linkchecker/tests/checks.rs +++ b/src/tools/linkchecker/tests/checks.rs @@ -82,7 +82,8 @@ fn broken_fragment_local() { fn broken_fragment_remote() { broken_test( "broken_fragment_remote/inner", - "foo.html:3: broken link fragment `#somefrag` pointing to `foo.html`", + "foo.html:3: broken link fragment `#somefrag` pointing to \ + `[..]/broken_fragment_remote/bar.html`", ); } From 052d77ea5669380fafbc210b517e51f7c7281c68 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 10 Jun 2021 12:06:47 +0900 Subject: [PATCH 07/15] Account for bad placeholder errors on consts/statics with trait objects --- compiler/rustc_typeck/src/collect.rs | 8 ++++++++ src/test/ui/error-codes/E0121.rs | 3 +-- src/test/ui/typeck/issue-75889.rs | 6 ++++++ src/test/ui/typeck/issue-75889.stderr | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/typeck/issue-75889.rs create mode 100644 src/test/ui/typeck/issue-75889.stderr diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index ee3ac3b62d9ec..89bfb77b855b7 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -808,6 +808,14 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { match it.kind { hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id), hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id), + hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => { + // (#75889): Account for `const C: dyn Fn() -> _ = "";` + if let hir::TyKind::TraitObject(..) = ty.kind { + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_item(it); + placeholder_type_error(tcx, None, &[], visitor.0, false, None); + } + } _ => (), } } diff --git a/src/test/ui/error-codes/E0121.rs b/src/test/ui/error-codes/E0121.rs index f8b4d61b32301..98cd6d54c1fd4 100644 --- a/src/test/ui/error-codes/E0121.rs +++ b/src/test/ui/error-codes/E0121.rs @@ -2,5 +2,4 @@ fn foo() -> _ { 5 } //~ ERROR E0121 static BAR: _ = "test"; //~ ERROR E0121 -fn main() { -} +fn main() {} diff --git a/src/test/ui/typeck/issue-75889.rs b/src/test/ui/typeck/issue-75889.rs new file mode 100644 index 0000000000000..84c067ed0c7a5 --- /dev/null +++ b/src/test/ui/typeck/issue-75889.rs @@ -0,0 +1,6 @@ +// Regression test for #75889. + +const FOO: dyn Fn() -> _ = ""; //~ ERROR E0121 +static BOO: dyn Fn() -> _ = ""; //~ ERROR E0121 + +fn main() {} diff --git a/src/test/ui/typeck/issue-75889.stderr b/src/test/ui/typeck/issue-75889.stderr new file mode 100644 index 0000000000000..0a8a3c9e7433e --- /dev/null +++ b/src/test/ui/typeck/issue-75889.stderr @@ -0,0 +1,15 @@ +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-75889.rs:3:24 + | +LL | const FOO: dyn Fn() -> _ = ""; + | ^ not allowed in type signatures + +error[E0121]: the type placeholder `_` is not allowed within types on item signatures + --> $DIR/issue-75889.rs:4:25 + | +LL | static BOO: dyn Fn() -> _ = ""; + | ^ not allowed in type signatures + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0121`. From 45675f3d9527a5212e96c922dfe4aa1efb7c7a4f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 15 Jun 2021 10:20:08 +0200 Subject: [PATCH 08/15] wording Co-authored-by: Jane Lusby --- library/core/src/cmp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a17cd63258335..34f7764c49b56 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -635,7 +635,7 @@ impl Clone for Reverse { /// Trait for types that form a [total order](https://en.wikipedia.org/wiki/Total_order). /// -/// Implementations must ensure to be consistent with the [`PartialOrd`] implementation, and that +/// Implementations must be consistent with the [`PartialOrd`] implementation, and ensure /// `max`, `min`, and `clamp` are consistent with `cmp`: /// /// - `partial_cmp(a, b) == Some(cmp(a, b))`. From bde95700691cad1c508eb396e190a9d8638f024f Mon Sep 17 00:00:00 2001 From: The8472 Date: Tue, 8 Jun 2021 23:09:56 +0200 Subject: [PATCH 09/15] Lazify is_really_default condition in the RustdocGUI bootstrap step The `RustdocGUI::should_run` condition spawns `npm list` several times which adds up to seconds of wall-time. Evaluate the condition lazily to to keep `./x.py test tidy` and similar short-running tasks fast. --- Cargo.lock | 1 + src/bootstrap/Cargo.toml | 1 + src/bootstrap/builder.rs | 28 +++++++++++++++++++++++----- src/bootstrap/test.rs | 6 +++--- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f06506eaa458..f476466235cbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,7 @@ dependencies = [ "libc", "merge", "num_cpus", + "once_cell", "opener", "pretty_assertions", "serde", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 8445d811e0f39..d1e666936f88a 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -50,6 +50,7 @@ time = "0.1" ignore = "0.4.10" opener = "0.4" merge = "0.1.0" +once_cell = "1.7.2" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index bc499fdba5996..e2f605257bd95 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -29,6 +29,8 @@ use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir}; use crate::{Build, DocTests, GitRepo, Mode}; pub use crate::Compiler; +// FIXME: replace with std::lazy after it gets stabilized and reaches beta +use once_cell::sync::Lazy; pub struct Builder<'a> { pub build: &'a Build, @@ -195,7 +197,7 @@ impl StepDescription { if paths.is_empty() || builder.config.include_default_paths { for (desc, should_run) in v.iter().zip(&should_runs) { - if desc.default && should_run.is_really_default { + if desc.default && should_run.is_really_default() { for pathset in &should_run.paths { desc.maybe_run(builder, pathset); } @@ -228,7 +230,11 @@ impl StepDescription { } } -#[derive(Clone)] +enum ReallyDefault<'a> { + Bool(bool), + Lazy(Lazy bool + 'a>>), +} + pub struct ShouldRun<'a> { pub builder: &'a Builder<'a>, // use a BTreeSet to maintain sort order @@ -236,7 +242,7 @@ pub struct ShouldRun<'a> { // If this is a default rule, this is an additional constraint placed on // its run. Generally something like compiler docs being enabled. - is_really_default: bool, + is_really_default: ReallyDefault<'a>, } impl<'a> ShouldRun<'a> { @@ -244,15 +250,27 @@ impl<'a> ShouldRun<'a> { ShouldRun { builder, paths: BTreeSet::new(), - is_really_default: true, // by default no additional conditions + is_really_default: ReallyDefault::Bool(true), // by default no additional conditions } } pub fn default_condition(mut self, cond: bool) -> Self { - self.is_really_default = cond; + self.is_really_default = ReallyDefault::Bool(cond); + self + } + + pub fn lazy_default_condition(mut self, lazy_cond: Box bool + 'a>) -> Self { + self.is_really_default = ReallyDefault::Lazy(Lazy::new(lazy_cond)); self } + pub fn is_really_default(&self) -> bool { + match &self.is_really_default { + ReallyDefault::Bool(val) => *val, + ReallyDefault::Lazy(lazy) => *lazy.deref(), + } + } + /// Indicates it should run if the command-line selects the given crate or /// any of its (local) dependencies. /// diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 0b7a0e25df1ac..319b56a0b7006 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -806,15 +806,15 @@ impl Step for RustdocGUI { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; let run = run.suite_path("src/test/rustdoc-gui"); - run.default_condition( + run.lazy_default_condition(Box::new(move || { builder.config.nodejs.is_some() && builder .config .npm .as_ref() .map(|p| check_if_browser_ui_test_is_installed(p)) - .unwrap_or(false), - ) + .unwrap_or(false) + })) } fn make_run(run: RunConfig<'_>) { From 87fb7cb58f48e047f46777c925aa9b124dd77d91 Mon Sep 17 00:00:00 2001 From: hi-rustin Date: Fri, 18 Jun 2021 15:21:41 +0800 Subject: [PATCH 10/15] Updated release note --- RELEASES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 4b753a2b32fff..2b159e7d00434 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1749,6 +1749,7 @@ Language - [You can now use `#[repr(transparent)]` on univariant `enum`s.][68122] Meaning that you can create an enum that has the exact layout and ABI of the type it contains. +- [You can now use outer attribute procedural macros on inline modules.][64273] - [There are some *syntax-only* changes:][67131] - `default` is syntactically allowed before items in `trait` definitions. - Items in `impl`s (i.e. `const`s, `type`s, and `fn`s) may syntactically @@ -1810,6 +1811,7 @@ Compatibility Notes [67935]: https://github.com/rust-lang/rust/pull/67935/ [68339]: https://github.com/rust-lang/rust/pull/68339/ [68122]: https://github.com/rust-lang/rust/pull/68122/ +[64273]: https://github.com/rust-lang/rust/pull/64273/ [67712]: https://github.com/rust-lang/rust/pull/67712/ [67887]: https://github.com/rust-lang/rust/pull/67887/ [67131]: https://github.com/rust-lang/rust/pull/67131/ From 8b518542d042a0b046d44d5c2b754976481c80ce Mon Sep 17 00:00:00 2001 From: The8472 Date: Sat, 19 Jun 2021 02:20:22 +0200 Subject: [PATCH 11/15] fix panic-safety in specialized Zip::next_back This was unsound since a panic in a.next_back() would result in the length not being updated which would then lead to the same element being revisited in the side-effect preserving code. --- library/core/src/iter/adapters/zip.rs | 3 ++- library/core/tests/iter/adapters/zip.rs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 8a6955060e82f..46bf93dcc01e2 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -295,9 +295,10 @@ where let sz_a = self.a.size(); if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len { for _ in 0..sz_a - self.len { + self.a_len -= 1; self.a.next_back(); } - self.a_len = self.len; + debug_assert_eq!(self.a_len, self.len); } let sz_b = self.b.size(); if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len { diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index 797bfd957f906..fce88daee195a 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -1,5 +1,7 @@ use super::*; use core::iter::*; +use std::panic::catch_unwind; +use std::panic::AssertUnwindSafe; #[test] fn test_zip_nth() { @@ -232,6 +234,29 @@ fn test_zip_trusted_random_access_composition() { assert_eq!(z2.next().unwrap(), ((1, 1), 1)); } +#[test] +fn test_zip_trusted_random_access_next_back_drop() { + let mut counter = 0; + + let it = [42].iter().map(|e| { + let c = counter; + counter += 1; + if c == 0 { + panic!("bomb"); + } + + e + }); + let it2 = [(); 0].iter(); + let mut zip = it.zip(it2); + catch_unwind(AssertUnwindSafe(|| { + zip.next_back(); + })) + .unwrap_err(); + assert!(zip.next().is_none()); + assert_eq!(counter, 1); +} + #[test] fn test_double_ended_zip() { let xs = [1, 2, 3, 4, 5, 6]; From b4734b7c38dc2b9e84dd0358114534f55bd1849e Mon Sep 17 00:00:00 2001 From: The8472 Date: Sun, 20 Jun 2021 12:20:05 +0200 Subject: [PATCH 12/15] disable test on platforms that don't support unwinding --- library/core/tests/iter/adapters/zip.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index fce88daee195a..585cfbb90e40c 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -1,7 +1,5 @@ use super::*; use core::iter::*; -use std::panic::catch_unwind; -use std::panic::AssertUnwindSafe; #[test] fn test_zip_nth() { @@ -235,7 +233,11 @@ fn test_zip_trusted_random_access_composition() { } #[test] +#[cfg(panic = "unwind")] fn test_zip_trusted_random_access_next_back_drop() { + use std::panic::catch_unwind; + use std::panic::AssertUnwindSafe; + let mut counter = 0; let it = [42].iter().map(|e| { From 3f0729f37877d32565a6829327db84a84b27fc3b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 20 Jun 2021 18:10:02 +0300 Subject: [PATCH 13/15] expand: Move some more derive logic to `rustc_builtin_macros` --- compiler/rustc_builtin_macros/src/cfg_eval.rs | 11 ++++--- compiler/rustc_builtin_macros/src/derive.rs | 6 ++-- compiler/rustc_expand/src/base.rs | 2 +- compiler/rustc_expand/src/expand.rs | 30 ++----------------- compiler/rustc_resolve/src/macros.rs | 2 +- 5 files changed, 13 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 79dc857074d59..415822c457305 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -24,19 +24,18 @@ crate fn expand( annotatable: Annotatable, ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); - cfg_eval(ecx, annotatable) + vec![cfg_eval(ecx, annotatable)] } -crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec { - let mut visitor = CfgEval { +crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable { + CfgEval { cfg: &mut StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, config_tokens: true, }, - }; - let annotatable = visitor.configure_annotatable(annotatable); - vec![annotatable] + } + .configure_annotatable(annotatable) } struct CfgEval<'a, 'b> { diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 1bb050a40cee2..60d6bae38b56c 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -26,6 +26,8 @@ impl MultiItemModifier for Expander { return ExpandResult::Ready(vec![item]); } + let item = cfg_eval(ecx, item); + let result = ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| { let template = @@ -54,12 +56,12 @@ impl MultiItemModifier for Expander { report_path_args(sess, &meta); meta.path }) - .map(|path| (path, None)) + .map(|path| (path, item.clone(), None)) .collect() }); match result { - Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)), + Ok(()) => ExpandResult::Ready(vec![item]), Err(Indeterminate) => ExpandResult::Retry(item), } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index aab2741c85240..96fd6cb68e8b9 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -835,7 +835,7 @@ impl SyntaxExtension { /// Error type that denotes indeterminacy. pub struct Indeterminate; -pub type DeriveResolutions = Vec<(ast::Path, Option>)>; +pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option>)>; pub trait ResolverExpand { fn next_node_id(&mut self) -> NodeId; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 39c0447bd099e..234a49fbbf125 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -500,42 +500,16 @@ impl<'a, 'b> MacroExpander<'a, 'b> { .resolver .take_derive_resolutions(expn_id) .map(|derives| { - enum AnnotatableRef<'a> { - Item(&'a P), - Stmt(&'a ast::Stmt), - } - let item = match &fragment { - AstFragment::Items(items) => match &items[..] { - [item] => AnnotatableRef::Item(item), - _ => unreachable!(), - }, - AstFragment::Stmts(stmts) => match &stmts[..] { - [stmt] => AnnotatableRef::Stmt(stmt), - _ => unreachable!(), - }, - _ => unreachable!(), - }; - derive_invocations.reserve(derives.len()); derives .into_iter() - .map(|(path, _exts)| { + .map(|(path, item, _exts)| { // FIXME: Consider using the derive resolutions (`_exts`) // instead of enqueuing the derives to be resolved again later. let expn_id = ExpnId::fresh(None); derive_invocations.push(( Invocation { - kind: InvocationKind::Derive { - path, - item: match item { - AnnotatableRef::Item(item) => { - Annotatable::Item(item.clone()) - } - AnnotatableRef::Stmt(stmt) => { - Annotatable::Stmt(P(stmt.clone())) - } - }, - }, + kind: InvocationKind::Derive { path, item }, fragment_kind, expansion_data: ExpansionData { id: expn_id, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 3f7db2b6962fa..38d052f988c63 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -380,7 +380,7 @@ impl<'a> ResolverExpand for Resolver<'a> { has_derive_copy: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; - for (i, (path, opt_ext)) in entry.resolutions.iter_mut().enumerate() { + for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() { if opt_ext.is_none() { *opt_ext = Some( match self.resolve_macro_path( From d9fd5eaae86fe9b21ec38da6acda296e64f4e93d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 20 Jun 2021 18:52:10 +0300 Subject: [PATCH 14/15] cfg_eval: Replace multiple `unwrap`s with a single `unwrap` --- compiler/rustc_builtin_macros/src/cfg_eval.rs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 415822c457305..d7b46f282151f 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -36,48 +36,48 @@ crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable { }, } .configure_annotatable(annotatable) + // Since the item itself has already been configured by the `InvocationCollector`, + // we know that fold result vector will contain exactly one element. + .unwrap() } struct CfgEval<'a, 'b> { cfg: &'a mut StripUnconfigured<'b>, } -fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable { - // Since the item itself has already been configured by the InvocationCollector, - // we know that fold result vector will contain exactly one element +fn flat_map_annotatable( + vis: &mut impl MutVisitor, + annotatable: Annotatable, +) -> Option { match annotatable { - Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()), + Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item), Annotatable::TraitItem(item) => { - Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap()) + vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem) } Annotatable::ImplItem(item) => { - Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap()) + vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem) } Annotatable::ForeignItem(item) => { - Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap()) + vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem) } Annotatable::Stmt(stmt) => { - Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap())) + vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt) } - Annotatable::Expr(mut expr) => Annotatable::Expr({ + Annotatable::Expr(mut expr) => { vis.visit_expr(&mut expr); - expr - }), - Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()), - Annotatable::ExprField(field) => { - Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap()) + Some(Annotatable::Expr(expr)) } - Annotatable::PatField(fp) => { - Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap()) + Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm), + Annotatable::ExprField(field) => { + vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField) } + Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField), Annotatable::GenericParam(param) => { - Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap()) - } - Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()), - Annotatable::FieldDef(sf) => { - Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap()) + vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam) } - Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()), + Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param), + Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef), + Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant), } } @@ -122,11 +122,11 @@ impl CfgEval<'_, '_> { self.cfg.configure(node) } - pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable { + fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option { // Tokenizing and re-parsing the `Annotatable` can have a significant // performance impact, so try to avoid it if possible if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) { - return annotatable; + return Some(annotatable); } // The majority of parsed attribute targets will never need to have early cfg-expansion From 37d0d2705de7f4103527ab769e61934c9c4a8953 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 21 Jun 2021 02:05:37 +0800 Subject: [PATCH 15/15] Do not set depth to 0 in fully_expand_fragment --- compiler/rustc_expand/src/expand.rs | 2 +- ...ssue-84632-eager-expansion-recursion-limit.rs | 16 ++++++++++++++++ ...-84632-eager-expansion-recursion-limit.stderr | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs create mode 100644 src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 39c0447bd099e..37a4765a4be2b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -427,7 +427,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment { let orig_expansion_data = self.cx.current_expansion.clone(); let orig_force_mode = self.cx.force_mode; - self.cx.current_expansion.depth = 0; // Collect all macro invocations and replace them with placeholders. let (mut fragment_with_placeholders, mut invocations) = @@ -488,6 +487,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data; + let depth = depth - orig_expansion_data.depth; self.cx.current_expansion = invoc.expansion_data.clone(); self.cx.force_mode = force; diff --git a/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs new file mode 100644 index 0000000000000..9139775c805a2 --- /dev/null +++ b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs @@ -0,0 +1,16 @@ +// Regression test for #84632: Recursion limit is ignored +// for builtin macros that eagerly expands. + +#![recursion_limit = "15"] +macro_rules! a { + () => (""); + (A) => (concat!("", a!())); + (A, $($A:ident),*) => (concat!("", a!($($A),*))) + //~^ ERROR recursion limit reached + //~| HELP consider adding +} + +fn main() { + a!(A, A, A, A, A); + a!(A, A, A, A, A, A, A, A, A, A, A); +} diff --git a/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr new file mode 100644 index 0000000000000..e6067e3334988 --- /dev/null +++ b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr @@ -0,0 +1,14 @@ +error: recursion limit reached while expanding `concat!` + --> $DIR/issue-84632-eager-expansion-recursion-limit.rs:8:28 + | +LL | (A, $($A:ident),*) => (concat!("", a!($($A),*))) + | ^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | a!(A, A, A, A, A, A, A, A, A, A, A); + | ------------------------------------ in this macro invocation + | + = help: consider adding a `#![recursion_limit="30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`) + = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error +

Redirecting to sometarget...