diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3c95226aebf6..bf3c22744f165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,9 +102,6 @@ jobs: - name: install MSYS2 run: src/ci/scripts/install-msys2.sh if: success() && !env.SKIP_JOB - - name: install MSYS2 packages - run: src/ci/scripts/install-msys2-packages.sh - if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh if: success() && !env.SKIP_JOB @@ -212,9 +209,6 @@ jobs: - name: install MSYS2 run: src/ci/scripts/install-msys2.sh if: success() && !env.SKIP_JOB - - name: install MSYS2 packages - run: src/ci/scripts/install-msys2-packages.sh - if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh if: success() && !env.SKIP_JOB @@ -434,11 +428,6 @@ jobs: NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 os: windows-latest-xl - - name: x86_64-msvc-aux - env: - RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 - RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc" - os: windows-latest-xl - name: x86_64-msvc-cargo env: SCRIPT: python x.py test src/tools/cargotest src/tools/cargo @@ -564,9 +553,6 @@ jobs: - name: install MSYS2 run: src/ci/scripts/install-msys2.sh if: success() && !env.SKIP_JOB - - name: install MSYS2 packages - run: src/ci/scripts/install-msys2-packages.sh - if: success() && !env.SKIP_JOB - name: install MinGW run: src/ci/scripts/install-mingw.sh if: success() && !env.SKIP_JOB diff --git a/Cargo.lock b/Cargo.lock index 01510b7168172..009767934d447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,7 +282,7 @@ checksum = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" [[package]] name = "cargo" -version = "0.46.0" +version = "0.47.0" dependencies = [ "anyhow", "atty", @@ -1434,9 +1434,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" dependencies = [ "compiler_builtins", "libc", @@ -1848,9 +1848,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.12.5+1.0.0" +version = "0.12.7+1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eadeec65514971355bf7134967a543f71372f35b53ac6c7143e7bd157f07535" +checksum = "bcd07968649bcb7b9351ecfde53ca4d27673cccfdf57c84255ec18710f3153e0" dependencies = [ "cc", "libc", diff --git a/config.toml.example b/config.toml.example index cf8fe4e082ac3..d995554913f84 100644 --- a/config.toml.example +++ b/config.toml.example @@ -69,7 +69,7 @@ # the same format as above, but since these targets are experimental, they are # not built by default and the experimental Rust compilation targets that depend # on them will not work unless the user opts in to building them. -#experimental-targets = "" +#experimental-targets = "AVR" # Cap the number of parallel linker invocations when compiling LLVM. # This can be useful when building LLVM with debug info, which significantly diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index ffdd8485181f4..ea5300bdfc04c 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -52,6 +52,8 @@ pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash { /// it's been assembled. type Output: Clone; + /// Whether this step is run by default as part of its respective phase. + /// `true` here can still be overwritten by `should_run` calling `default_condition`. const DEFAULT: bool = false; /// If true, then this rule should be skipped if --target was specified, but --host was not @@ -371,7 +373,6 @@ impl<'a> Builder<'a> { test::UiFullDeps, test::Rustdoc, test::Pretty, - test::RunPassValgrindPretty, test::Crate, test::CrateLibrustc, test::CrateRustdoc, diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index b3999118e3de4..c09b73b042013 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -983,7 +983,13 @@ pub fn stream_cargo( for line in stdout.lines() { let line = t!(line); match serde_json::from_str::>(&line) { - Ok(msg) => cb(msg), + Ok(msg) => { + if builder.config.json_output { + // Forward JSON to stdout. + println!("{}", line); + } + cb(msg) + } // If this was informational, just print it out and continue Err(_) => println!("{}", line), } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 5e966d7055bf3..a752d8045f7b4 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -619,19 +619,21 @@ impl Step for DebuggerScripts { cp_debugger_script("natvis/libcore.natvis"); cp_debugger_script("natvis/libstd.natvis"); } else { - cp_debugger_script("debugger_pretty_printers_common.py"); + cp_debugger_script("rust_types.py"); // gdb debugger scripts builder.install(&builder.src.join("src/etc/rust-gdb"), &sysroot.join("bin"), 0o755); builder.install(&builder.src.join("src/etc/rust-gdbgui"), &sysroot.join("bin"), 0o755); cp_debugger_script("gdb_load_rust_pretty_printers.py"); - cp_debugger_script("gdb_rust_pretty_printing.py"); + cp_debugger_script("gdb_lookup.py"); + cp_debugger_script("gdb_providers.py"); // lldb debugger scripts builder.install(&builder.src.join("src/etc/rust-lldb"), &sysroot.join("bin"), 0o755); - cp_debugger_script("lldb_rust_formatters.py"); + cp_debugger_script("lldb_lookup.py"); + cp_debugger_script("lldb_providers.py"); } } } diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index d8c97fc741478..12a1734e21c7e 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -6,12 +6,6 @@ Q := @ BOOTSTRAP_ARGS := endif -ifdef EXCLUDE_CARGO -AUX_ARGS := -else -AUX_ARGS := src/tools/cargo src/tools/cargotest -endif - BOOTSTRAP := $(CFG_PYTHON) $(CFG_SRC_DIR)src/bootstrap/bootstrap.py all: @@ -48,8 +42,8 @@ check: $(Q)$(BOOTSTRAP) test $(BOOTSTRAP_ARGS) check-aux: $(Q)$(BOOTSTRAP) test \ - src/test/run-pass-valgrind/pretty \ - $(AUX_ARGS) \ + src/tools/cargo \ + src/tools/cargotest \ $(BOOTSTRAP_ARGS) check-bootstrap: $(Q)$(CFG_PYTHON) $(CFG_SRC_DIR)src/bootstrap/bootstrap_test.py diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 5b6e953484369..252a6316e574b 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -144,7 +144,7 @@ impl Step for Llvm { let llvm_exp_targets = match builder.config.llvm_experimental_targets { Some(ref s) => s, - None => "", + None => "AVR", }; let assertions = if builder.config.llvm_assertions { "ON" } else { "OFF" }; diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index a99e39ed35428..b8c5751565838 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -154,6 +154,7 @@ impl Step for Cargotest { fn run(self, builder: &Builder<'_>) { let compiler = builder.compiler(self.stage, self.host); builder.ensure(compile::Rustc { compiler, target: compiler.host }); + let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); // Note that this is a short, cryptic, and not scoped directory name. This // is currently to minimize the length of path on Windows where we otherwise @@ -165,7 +166,7 @@ impl Step for Cargotest { let mut cmd = builder.tool_cmd(Tool::CargoTest); try_run( builder, - cmd.arg(&builder.initial_cargo) + cmd.arg(&cargo) .arg(&out_dir) .env("RUSTC", builder.rustc(compiler)) .env("RUSTDOC", builder.rustdoc(compiler)), @@ -553,7 +554,7 @@ impl Step for Clippy { builder.add_rustc_lib_path(compiler, &mut cargo); - try_run(builder, &mut cargo.into()); + builder.run(&mut cargo.into()); } } @@ -929,13 +930,6 @@ host_test!(UiFullDeps { path: "src/test/ui-fulldeps", mode: "ui", suite: "ui-ful host_test!(Rustdoc { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" }); host_test!(Pretty { path: "src/test/pretty", mode: "pretty", suite: "pretty" }); -test!(RunPassValgrindPretty { - path: "src/test/run-pass-valgrind/pretty", - mode: "pretty", - suite: "run-pass-valgrind", - default: false, - host: true -}); default_test!(RunMake { path: "src/test/run-make", mode: "run-make", suite: "run-make" }); diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 6cd9f9029c948..9c95de0a81eae 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -595,6 +595,7 @@ macro_rules! tool_extended { $toolstate:ident, $path:expr, $tool_name:expr, + stable = $stable:expr, $extra_deps:block;)+) => { $( #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -606,17 +607,22 @@ macro_rules! tool_extended { impl Step for $name { type Output = Option; - const DEFAULT: bool = true; + const DEFAULT: bool = true; // Overwritten below const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; run.path($path).default_condition( builder.config.extended - && builder.config.tools.as_ref().map_or(true, |tools| { - tools.iter().any(|tool| match tool.as_ref() { - "clippy" => $tool_name == "clippy-driver", - x => $tool_name == x, + && builder.config.tools.as_ref().map_or( + // By default, on nightly/dev enable all tools, else only + // build stable tools. + $stable || builder.build.unstable_features(), + // If `tools` is set, search list for this tool. + |tools| { + tools.iter().any(|tool| match tool.as_ref() { + "clippy" => $tool_name == "clippy-driver", + x => $tool_name == x, }) }), ) @@ -652,12 +658,12 @@ macro_rules! tool_extended { // Note: tools need to be also added to `Builder::get_step_descriptions` in `build.rs` // to make `./x.py build ` work. tool_extended!((self, builder), - Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", {}; - CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", {}; - Clippy, clippy, "src/tools/clippy", "clippy-driver", {}; - Miri, miri, "src/tools/miri", "miri", {}; - CargoMiri, miri, "src/tools/miri/cargo-miri", "cargo-miri", {}; - Rls, rls, "src/tools/rls", "rls", { + Cargofmt, rustfmt, "src/tools/rustfmt", "cargo-fmt", stable=true, {}; + CargoClippy, clippy, "src/tools/clippy", "cargo-clippy", stable=true, {}; + Clippy, clippy, "src/tools/clippy", "clippy-driver", stable=true, {}; + Miri, miri, "src/tools/miri", "miri", stable=false, {}; + CargoMiri, miri, "src/tools/miri/cargo-miri", "cargo-miri", stable=false, {}; + Rls, rls, "src/tools/rls", "rls", stable=true, { builder.ensure(Clippy { compiler: self.compiler, target: self.target, @@ -665,7 +671,7 @@ tool_extended!((self, builder), }); self.extra_features.push("clippy".to_owned()); }; - Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", {}; + Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, {}; ); impl<'a> Builder<'a> { diff --git a/src/ci/azure-pipelines/auto.yml b/src/ci/azure-pipelines/auto.yml index f8fa7b727d179..3de27bc54c5c0 100644 --- a/src/ci/azure-pipelines/auto.yml +++ b/src/ci/azure-pipelines/auto.yml @@ -142,10 +142,6 @@ jobs: # FIXME(#59637) NO_DEBUG_ASSERTIONS: 1 NO_LLVM_ASSERTIONS: 1 - # MSVC aux tests - x86_64-msvc-aux: - RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 - INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc x86_64-msvc-cargo: SCRIPT: python x.py test src/tools/cargotest src/tools/cargo INITIAL_RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld diff --git a/src/ci/azure-pipelines/steps/run.yml b/src/ci/azure-pipelines/steps/run.yml index 85ff3e52a841d..e43116c06b6b7 100644 --- a/src/ci/azure-pipelines/steps/run.yml +++ b/src/ci/azure-pipelines/steps/run.yml @@ -82,10 +82,6 @@ steps: displayName: Install msys2 condition: and(succeeded(), not(variables.SKIP_JOB)) -- bash: src/ci/scripts/install-msys2-packages.sh - displayName: Install msys2 packages - condition: and(succeeded(), not(variables.SKIP_JOB)) - - bash: src/ci/scripts/install-mingw.sh displayName: Install MinGW condition: and(succeeded(), not(variables.SKIP_JOB)) diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index 74ba2f0eadb25..1ae412340cb11 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -3,7 +3,7 @@ # # Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and # musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions. -# Right now we have: Binutils 2.27, GCC 6.4.0, musl 1.1.22. +# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.1.24. set -ex hide_output() { @@ -33,11 +33,13 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -git clone https://github.com/richfelker/musl-cross-make -b v0.9.8 +git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9 cd musl-cross-make +# A few commits ahead of v0.9.9 to include the cowpatch fix: +git checkout a54eb56f33f255dfca60be045f12a5cfaf5a72a9 -hide_output make -j$(nproc) TARGET=$TARGET -hide_output make install TARGET=$TARGET OUTPUT=$OUTPUT +hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.1.24 +hide_output make install TARGET=$TARGET MUSL_VER=1.1.24 OUTPUT=$OUTPUT cd - diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index d847c407aba67..58393a5719a10 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -24,7 +24,7 @@ shift # Apparently applying `-fPIC` everywhere allows them to link successfully. export CFLAGS="-fPIC $CFLAGS" -MUSL=musl-1.1.22 +MUSL=musl-1.1.24 # may have been downloaded in a previous run if [ ! -d $MUSL ]; then diff --git a/src/ci/docker/wasm32/Dockerfile b/src/ci/docker/wasm32/Dockerfile index 91c492d03c179..8232539edda77 100644 --- a/src/ci/docker/wasm32/Dockerfile +++ b/src/ci/docker/wasm32/Dockerfile @@ -27,6 +27,9 @@ ENV PATH=$PATH:/emsdk-portable ENV PATH=$PATH:/emsdk-portable/upstream/emscripten/ ENV PATH=$PATH:/emsdk-portable/node/12.9.1_64bit/bin/ ENV BINARYEN_ROOT=/emsdk-portable/upstream/ +ENV EMSDK=/emsdk-portable +ENV EM_CONFIG=/emsdk-portable/.emscripten +ENV EM_CACHE=/emsdk-portable/upstream/emscripten/cache ENV TARGETS=wasm32-unknown-emscripten diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 92fec593a5410..a052d0879a3db 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -147,10 +147,6 @@ x--expand-yaml-anchors--remove: run: src/ci/scripts/install-msys2.sh <<: *step - - name: install MSYS2 packages - run: src/ci/scripts/install-msys2-packages.sh - <<: *step - - name: install MinGW run: src/ci/scripts/install-mingw.sh <<: *step @@ -496,12 +492,6 @@ jobs: NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - - name: x86_64-msvc-aux - env: - RUST_CHECK_TARGET: check-aux EXCLUDE_CARGO=1 - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc - <<: *job-windows-xl - - name: x86_64-msvc-cargo env: SCRIPT: python x.py test src/tools/cargotest src/tools/cargo diff --git a/src/ci/scripts/install-msys2-packages.sh b/src/ci/scripts/install-msys2-packages.sh deleted file mode 100755 index ff7479c05d04e..0000000000000 --- a/src/ci/scripts/install-msys2-packages.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if isWindows; then - pacman -S --noconfirm --needed base-devel ca-certificates make diffutils tar \ - binutils - - # Detect the native Python version installed on the agent. On GitHub - # Actions, the C:\hostedtoolcache\windows\Python directory contains a - # subdirectory for each installed Python version. - # - # The -V flag of the sort command sorts the input by version number. - native_python_version="$(ls /c/hostedtoolcache/windows/Python | sort -Vr | head -n 1)" - - # Make sure we use the native python interpreter instead of some msys equivalent - # one way or another. The msys interpreters seem to have weird path conversions - # baked in which break LLVM's build system one way or another, so let's use the - # native version which keeps everything as native as possible. - python_home="/c/hostedtoolcache/windows/Python/${native_python_version}/x64" - cp "${python_home}/python.exe" "${python_home}/python3.exe" - ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64" - ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64\\Scripts" -fi diff --git a/src/ci/scripts/install-msys2.sh b/src/ci/scripts/install-msys2.sh index 3c3b5007f8697..3a0c965a67710 100755 --- a/src/ci/scripts/install-msys2.sh +++ b/src/ci/scripts/install-msys2.sh @@ -1,10 +1,6 @@ #!/bin/bash # Download and install MSYS2, needed primarily for the test suite (run-make) but # also used by the MinGW toolchain for assembling things. -# -# FIXME: we should probe the default azure image and see if we can use the MSYS2 -# toolchain there. (if there's even one there). For now though this gets the job -# done. set -euo pipefail IFS=$'\n\t' @@ -12,17 +8,26 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" if isWindows; then - # Pre-followed the api/v2 URL to the CDN since the API can be a bit flakey - curl -sSL https://packages.chocolatey.org/msys2.20190524.0.0.20191030.nupkg > \ - msys2.nupkg - curl -sSL https://packages.chocolatey.org/chocolatey-core.extension.1.3.5.1.nupkg > \ - chocolatey-core.extension.nupkg - choco install -s . msys2 \ - --params="/InstallDir:$(ciCheckoutPath)/msys2 /NoPath" -y --no-progress - rm msys2.nupkg chocolatey-core.extension.nupkg - mkdir -p "$(ciCheckoutPath)/msys2/home/${USERNAME}" - ciCommandAddPath "$(ciCheckoutPath)/msys2/usr/bin" + msys2Path="c:/msys64" + mkdir -p "${msys2Path}/home/${USERNAME}" + ciCommandAddPath "${msys2Path}/usr/bin" echo "switching shell to use our own bash" - ciCommandSetEnv CI_OVERRIDE_SHELL "$(ciCheckoutPath)/msys2/usr/bin/bash.exe" + ciCommandSetEnv CI_OVERRIDE_SHELL "${msys2Path}/usr/bin/bash.exe" + + # Detect the native Python version installed on the agent. On GitHub + # Actions, the C:\hostedtoolcache\windows\Python directory contains a + # subdirectory for each installed Python version. + # + # The -V flag of the sort command sorts the input by version number. + native_python_version="$(ls /c/hostedtoolcache/windows/Python | sort -Vr | head -n 1)" + + # Make sure we use the native python interpreter instead of some msys equivalent + # one way or another. The msys interpreters seem to have weird path conversions + # baked in which break LLVM's build system one way or another, so let's use the + # native version which keeps everything as native as possible. + python_home="/c/hostedtoolcache/windows/Python/${native_python_version}/x64" + cp "${python_home}/python.exe" "${python_home}/python3.exe" + ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64" + ciCommandAddPath "C:\\hostedtoolcache\\windows\\Python\\${native_python_version}\\x64\\Scripts" fi diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index efadae1c5fb9d..18010bebcf0e7 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -416,7 +416,7 @@ without including it in your main documentation. For example, you could write th `lib.rs` to test your README as part of your doctests: ```rust,ignore -#![feature(extern_doc)] +#![feature(external_doc)] #[doc(include="../README.md")] #[cfg(doctest)] diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index ea560a6d70915..fbb40f1d2f3d4 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -201,7 +201,7 @@ fn mul(a: u64, b: u64) -> u128 { ); } - (hi as u128) << 64 + lo as u128 + ((hi as u128) << 64) + lo as u128 } ``` @@ -382,7 +382,9 @@ The macro will initially be supported only on ARM, AArch64, x86, x86-64 and RISC The assembler template uses the same syntax as [format strings][format-syntax] (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by [RFC #2795][rfc-2795]) are not supported. -As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after any named arguments if any. Explicit register operands cannot be used by placeholders in the template string. All other operands must appear at least once in the template string, otherwise a compiler error is generated. +As with format strings, named arguments must appear after positional arguments. Explicit register operands must appear at the end of the operand list, after named arguments if any. + +Explicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated. The exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler. diff --git a/src/etc/debugger_pretty_printers_common.py b/src/etc/debugger_pretty_printers_common.py deleted file mode 100644 index b3f8f50636bee..0000000000000 --- a/src/etc/debugger_pretty_printers_common.py +++ /dev/null @@ -1,401 +0,0 @@ -""" -This module provides an abstraction layer over common Rust pretty printing -functionality needed by both GDB and LLDB. -""" - -import re - -# Type codes that indicate the kind of type as it appears in DWARF debug -# information. This code alone is not sufficient to determine the Rust type. -# For example structs, tuples, fat pointers, or enum variants will all have -# DWARF_TYPE_CODE_STRUCT. -DWARF_TYPE_CODE_STRUCT = 1 -DWARF_TYPE_CODE_UNION = 2 -DWARF_TYPE_CODE_PTR = 3 -DWARF_TYPE_CODE_ARRAY = 4 -DWARF_TYPE_CODE_ENUM = 5 - -# These constants specify the most specific kind of type that could be -# determined for a given value. -TYPE_KIND_UNKNOWN = -1 -TYPE_KIND_EMPTY = 0 -TYPE_KIND_SLICE = 1 -TYPE_KIND_REGULAR_STRUCT = 2 -TYPE_KIND_TUPLE = 3 -TYPE_KIND_TUPLE_STRUCT = 4 -TYPE_KIND_CSTYLE_VARIANT = 5 -TYPE_KIND_TUPLE_VARIANT = 6 -TYPE_KIND_STRUCT_VARIANT = 7 -TYPE_KIND_STR_SLICE = 8 -TYPE_KIND_STD_VEC = 9 -TYPE_KIND_STD_STRING = 10 -TYPE_KIND_REGULAR_ENUM = 11 -TYPE_KIND_COMPRESSED_ENUM = 12 -TYPE_KIND_SINGLETON_ENUM = 13 -TYPE_KIND_CSTYLE_ENUM = 14 -TYPE_KIND_PTR = 15 -TYPE_KIND_FIXED_SIZE_VEC = 16 -TYPE_KIND_REGULAR_UNION = 17 -TYPE_KIND_OS_STRING = 18 -TYPE_KIND_STD_VECDEQUE = 19 -TYPE_KIND_STD_BTREESET = 20 -TYPE_KIND_STD_BTREEMAP = 21 - -ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" -ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR" - -# Slice related constants -SLICE_FIELD_NAME_DATA_PTR = "data_ptr" -SLICE_FIELD_NAME_LENGTH = "length" -SLICE_FIELD_NAMES = [SLICE_FIELD_NAME_DATA_PTR, SLICE_FIELD_NAME_LENGTH] - -# std::Vec<> related constants -STD_VEC_FIELD_NAME_LENGTH = "len" -STD_VEC_FIELD_NAME_BUF = "buf" -STD_VEC_FIELD_NAMES = [STD_VEC_FIELD_NAME_BUF, - STD_VEC_FIELD_NAME_LENGTH] - -# std::collections::VecDeque<> related constants -STD_VECDEQUE_FIELD_NAME_TAIL = "tail" -STD_VECDEQUE_FIELD_NAME_HEAD = "head" -STD_VECDEQUE_FIELD_NAME_BUF = "buf" -STD_VECDEQUE_FIELD_NAMES = [STD_VECDEQUE_FIELD_NAME_TAIL, - STD_VECDEQUE_FIELD_NAME_HEAD, - STD_VECDEQUE_FIELD_NAME_BUF] - -# std::collections::BTreeSet<> related constants -STD_BTREESET_FIELD_NAMES = ["map"] - -# std::collections::BTreeMap<> related constants -STD_BTREEMAP_FIELD_NAMES = ["root", "length"] - -# std::String related constants -STD_STRING_FIELD_NAMES = ["vec"] - -# std::ffi::OsString related constants -OS_STRING_FIELD_NAMES = ["inner"] - - -class Type(object): - """ - This class provides a common interface for type-oriented operations. - Sub-classes are supposed to wrap a debugger-specific type-object and - provide implementations for the abstract methods in this class. - """ - - def __init__(self): - self.__type_kind = None - - def get_unqualified_type_name(self): - """ - Implementations of this method should return the unqualified name of the - type-object they are wrapping. Some examples: - - 'int' -> 'int' - 'std::vec::Vec' -> 'Vec' - '&std::option::Option' -> '&std::option::Option' - - As you can see, type arguments stay fully qualified. - """ - raise NotImplementedError("Override this method") - - def get_dwarf_type_kind(self): - """ - Implementations of this method should return the correct - DWARF_TYPE_CODE_* value for the wrapped type-object. - """ - raise NotImplementedError("Override this method") - - def get_fields(self): - """ - Implementations of this method should return a list of field-objects of - this type. For Rust-enums (i.e. with DWARF_TYPE_CODE_UNION) these field- - objects represent the variants of the enum. Field-objects must have a - `name` attribute that gives their name as specified in DWARF. - """ - assert ((self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION)) - raise NotImplementedError("Override this method") - - def get_wrapped_value(self): - """ - Returns the debugger-specific type-object wrapped by this object. This - is sometimes needed for doing things like pointer-arithmetic in GDB. - """ - raise NotImplementedError("Override this method") - - def get_type_kind(self): - """This method returns the TYPE_KIND_* value for this type-object.""" - if self.__type_kind is None: - dwarf_type_code = self.get_dwarf_type_kind() - - if dwarf_type_code == DWARF_TYPE_CODE_STRUCT: - self.__type_kind = self.__classify_struct() - elif dwarf_type_code == DWARF_TYPE_CODE_UNION: - self.__type_kind = self.__classify_union() - elif dwarf_type_code == DWARF_TYPE_CODE_PTR: - self.__type_kind = TYPE_KIND_PTR - elif dwarf_type_code == DWARF_TYPE_CODE_ARRAY: - self.__type_kind = TYPE_KIND_FIXED_SIZE_VEC - else: - self.__type_kind = TYPE_KIND_UNKNOWN - return self.__type_kind - - def __classify_struct(self): - assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT - - unqualified_type_name = self.get_unqualified_type_name() - - # STR SLICE - if unqualified_type_name == "&str": - return TYPE_KIND_STR_SLICE - - # REGULAR SLICE - if (unqualified_type_name.startswith(("&[", "&mut [")) and - unqualified_type_name.endswith("]") and - self.__conforms_to_field_layout(SLICE_FIELD_NAMES)): - return TYPE_KIND_SLICE - - fields = self.get_fields() - field_count = len(fields) - - # EMPTY STRUCT - if field_count == 0: - return TYPE_KIND_EMPTY - - # STD VEC - if (unqualified_type_name.startswith("Vec<") and - self.__conforms_to_field_layout(STD_VEC_FIELD_NAMES)): - return TYPE_KIND_STD_VEC - - # STD COLLECTION VECDEQUE - if (unqualified_type_name.startswith("VecDeque<") and - self.__conforms_to_field_layout(STD_VECDEQUE_FIELD_NAMES)): - return TYPE_KIND_STD_VECDEQUE - - # STD COLLECTION BTREESET - if (unqualified_type_name.startswith("BTreeSet<") and - self.__conforms_to_field_layout(STD_BTREESET_FIELD_NAMES)): - return TYPE_KIND_STD_BTREESET - - # STD COLLECTION BTREEMAP - if (unqualified_type_name.startswith("BTreeMap<") and - self.__conforms_to_field_layout(STD_BTREEMAP_FIELD_NAMES)): - return TYPE_KIND_STD_BTREEMAP - - # STD STRING - if (unqualified_type_name.startswith("String") and - self.__conforms_to_field_layout(STD_STRING_FIELD_NAMES)): - return TYPE_KIND_STD_STRING - - # OS STRING - if (unqualified_type_name == "OsString" and - self.__conforms_to_field_layout(OS_STRING_FIELD_NAMES)): - return TYPE_KIND_OS_STRING - - # ENUM VARIANTS - if fields[0].name == ENUM_DISR_FIELD_NAME: - if field_count == 1: - return TYPE_KIND_CSTYLE_VARIANT - elif self.__all_fields_conform_to_tuple_field_naming(1): - return TYPE_KIND_TUPLE_VARIANT - else: - return TYPE_KIND_STRUCT_VARIANT - - # TUPLE - if self.__all_fields_conform_to_tuple_field_naming(0): - if unqualified_type_name.startswith("("): - return TYPE_KIND_TUPLE - else: - return TYPE_KIND_TUPLE_STRUCT - - # REGULAR STRUCT - return TYPE_KIND_REGULAR_STRUCT - - def __classify_union(self): - assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION - - union_members = self.get_fields() - union_member_count = len(union_members) - if union_member_count == 0: - return TYPE_KIND_EMPTY - - first_variant_name = union_members[0].name - if first_variant_name is None: - if union_member_count == 1: - return TYPE_KIND_SINGLETON_ENUM - else: - return TYPE_KIND_REGULAR_ENUM - elif first_variant_name.startswith(ENCODED_ENUM_PREFIX): - assert union_member_count == 1 - return TYPE_KIND_COMPRESSED_ENUM - else: - return TYPE_KIND_REGULAR_UNION - - def __conforms_to_field_layout(self, expected_fields): - actual_fields = self.get_fields() - actual_field_count = len(actual_fields) - - if actual_field_count != len(expected_fields): - return False - - for i in range(0, actual_field_count): - if actual_fields[i].name != expected_fields[i]: - return False - - return True - - def __all_fields_conform_to_tuple_field_naming(self, start_index): - fields = self.get_fields() - field_count = len(fields) - - for i in range(start_index, field_count): - field_name = fields[i].name - if (field_name is None) or (re.match(r"__\d+$", field_name) is None): - return False - return True - - -class Value(object): - """ - This class provides a common interface for value-oriented operations. - Sub-classes are supposed to wrap a debugger-specific value-object and - provide implementations for the abstract methods in this class. - """ - def __init__(self, ty): - self.type = ty - - def get_child_at_index(self, index): - """Returns the value of the field, array element or variant at the given index""" - raise NotImplementedError("Override this method") - - def as_integer(self): - """ - Try to convert the wrapped value into a Python integer. This should - always succeed for values that are pointers or actual integers. - """ - raise NotImplementedError("Override this method") - - def get_wrapped_value(self): - """ - Returns the debugger-specific value-object wrapped by this object. This - is sometimes needed for doing things like pointer-arithmetic in GDB. - """ - raise NotImplementedError("Override this method") - - -class EncodedEnumInfo(object): - """ - This class provides facilities for handling enum values with compressed - encoding where a non-null field in one variant doubles as the discriminant. - """ - - def __init__(self, enum_val): - assert enum_val.type.get_type_kind() == TYPE_KIND_COMPRESSED_ENUM - variant_name = enum_val.type.get_fields()[0].name - last_separator_index = variant_name.rfind("$") - start_index = len(ENCODED_ENUM_PREFIX) - indices_substring = variant_name[start_index:last_separator_index].split("$") - self.__enum_val = enum_val - self.__disr_field_indices = [int(index) for index in indices_substring] - self.__null_variant_name = variant_name[last_separator_index + 1:] - - def is_null_variant(self): - ty = self.__enum_val.type - sole_variant_val = self.__enum_val.get_child_at_index(0) - discriminant_val = sole_variant_val - for disr_field_index in self.__disr_field_indices: - discriminant_val = discriminant_val.get_child_at_index(disr_field_index) - - # If the discriminant field is a fat pointer we have to consider the - # first word as the true discriminant - if discriminant_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT: - discriminant_val = discriminant_val.get_child_at_index(0) - - return discriminant_val.as_integer() == 0 - - def get_non_null_variant_val(self): - return self.__enum_val.get_child_at_index(0) - - def get_null_variant_name(self): - return self.__null_variant_name - - -def get_discriminant_value_as_integer(enum_val): - assert enum_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION - # we can take any variant here because the discriminant has to be the same - # for all of them. - variant_val = enum_val.get_child_at_index(0) - disr_val = variant_val.get_child_at_index(0) - return disr_val.as_integer() - - -def extract_length_ptr_and_cap_from_std_vec(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VEC - length_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_LENGTH) - buf_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_BUF) - - length = vec_val.get_child_at_index(length_field_index).as_integer() - buf = vec_val.get_child_at_index(buf_field_index) - - vec_ptr_val = buf.get_child_at_index(0) - capacity = buf.get_child_at_index(1).as_integer() - data_ptr = vec_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr, capacity) - - -def extract_tail_head_ptr_and_cap_from_std_vecdeque(vec_val): - assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VECDEQUE - tail_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_TAIL) - head_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_HEAD) - buf_field_index = STD_VECDEQUE_FIELD_NAMES.index(STD_VECDEQUE_FIELD_NAME_BUF) - - tail = vec_val.get_child_at_index(tail_field_index).as_integer() - head = vec_val.get_child_at_index(head_field_index).as_integer() - buf = vec_val.get_child_at_index(buf_field_index) - - vec_ptr_val = buf.get_child_at_index(0) - capacity = buf.get_child_at_index(1).as_integer() - data_ptr = vec_ptr_val.get_child_at_index(0) - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (tail, head, data_ptr, capacity) - - -def extract_length_and_ptr_from_slice(slice_val): - assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or - slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE) - - length_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_LENGTH) - ptr_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_DATA_PTR) - - length = slice_val.get_child_at_index(length_field_index).as_integer() - data_ptr = slice_val.get_child_at_index(ptr_field_index) - - assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR - return (length, data_ptr) - - -UNQUALIFIED_TYPE_MARKERS = frozenset(["(", "[", "&", "*"]) - - -def extract_type_name(qualified_type_name): - """Extracts the type name from a fully qualified path""" - if qualified_type_name[0] in UNQUALIFIED_TYPE_MARKERS: - return qualified_type_name - - end_of_search = qualified_type_name.find("<") - if end_of_search < 0: - end_of_search = len(qualified_type_name) - - index = qualified_type_name.rfind("::", 0, end_of_search) - if index < 0: - return qualified_type_name - else: - return qualified_type_name[index + 2:] - - -try: - compat_str = unicode # Python 2 -except NameError: - compat_str = str diff --git a/src/etc/gdb_load_rust_pretty_printers.py b/src/etc/gdb_load_rust_pretty_printers.py index fe38c49d2707d..856b5df2de70b 100644 --- a/src/etc/gdb_load_rust_pretty_printers.py +++ b/src/etc/gdb_load_rust_pretty_printers.py @@ -1,3 +1,3 @@ import gdb -import gdb_rust_pretty_printing -gdb_rust_pretty_printing.register_printers(gdb.current_objfile()) +import gdb_lookup +gdb_lookup.register_printers(gdb.current_objfile()) diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py new file mode 100644 index 0000000000000..2a46eaadad6f9 --- /dev/null +++ b/src/etc/gdb_lookup.py @@ -0,0 +1,92 @@ +import gdb +import re + +from gdb_providers import * +from rust_types import * + + +rust_enabled = 'set language rust' in gdb.execute('complete set language ru', to_string=True) +_gdb_version_matched = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) +gdb_version = [int(num) for num in _gdb_version_matched.groups()] if _gdb_version_matched else [] + +def register_printers(objfile): + objfile.pretty_printers.append(lookup) + + +# BACKCOMPAT: rust 1.35 +def is_hashbrown_hashmap(hash_map): + return len(hash_map.type.fields()) == 1 + + +def classify_rust_type(type): + type_class = type.code + if type_class == gdb.TYPE_CODE_STRUCT: + return classify_struct(type.tag, type.fields()) + if type_class == gdb.TYPE_CODE_UNION: + return classify_union(type.fields()) + + return RustType.OTHER + + +def check_enum_discriminant(valobj): + content = valobj[valobj.type.fields()[0]] + fields = content.type.fields() + if len(fields) > 1: + discriminant = int(content[fields[0]]) + 1 + if discriminant > len(fields): + # invalid discriminant + return False + return True + + +def lookup(valobj): + rust_type = classify_rust_type(valobj.type) + + if rust_type == RustType.ENUM: + # use enum provider only for GDB <7.12 + if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): + if check_enum_discriminant(valobj): + return EnumProvider(valobj) + + if rust_type == RustType.STD_STRING: + return StdStringProvider(valobj) + if rust_type == RustType.STD_OS_STRING: + return StdOsStringProvider(valobj) + if rust_type == RustType.STD_STR and not rust_enabled: + return StdStrProvider(valobj) + + if rust_type == RustType.STD_VEC: + return StdVecProvider(valobj) + if rust_type == RustType.STD_VEC_DEQUE: + return StdVecDequeProvider(valobj) + if rust_type == RustType.STD_BTREE_SET: + return StdBTreeSetProvider(valobj) + if rust_type == RustType.STD_BTREE_MAP: + return StdBTreeMapProvider(valobj) + if rust_type == RustType.STD_HASH_MAP: + if is_hashbrown_hashmap(valobj): + return StdHashMapProvider(valobj) + else: + return StdOldHashMapProvider(valobj) + if rust_type == RustType.STD_HASH_SET: + hash_map = valobj["map"] + if is_hashbrown_hashmap(hash_map): + return StdHashMapProvider(hash_map, show_values=False) + else: + return StdOldHashMapProvider(hash_map, show_values=False) + + if rust_type == RustType.STD_RC: + return StdRcProvider(valobj) + if rust_type == RustType.STD_ARC: + return StdRcProvider(valobj, is_atomic=True) + + if rust_type == RustType.STD_CELL: + return StdCellProvider(valobj) + if rust_type == RustType.STD_REF: + return StdRefProvider(valobj) + if rust_type == RustType.STD_REF_MUT: + return StdRefProvider(valobj) + if rust_type == RustType.STD_REF_CELL: + return StdRefCellProvider(valobj) + + return None diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py new file mode 100644 index 0000000000000..cec9c56a23522 --- /dev/null +++ b/src/etc/gdb_providers.py @@ -0,0 +1,385 @@ +from sys import version_info + +import gdb +from gdb import lookup_type + +if version_info[0] >= 3: + xrange = range + +ZERO_FIELD = "__0" +FIRST_FIELD = "__1" + + +def unwrap_unique_or_non_null(unique_or_nonnull): + # BACKCOMPAT: rust 1.32 + # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 + ptr = unique_or_nonnull["pointer"] + return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ZERO_FIELD] + + +class EnumProvider: + def __init__(self, valobj): + content = valobj[valobj.type.fields()[0]] + fields = content.type.fields() + self.empty = len(fields) == 0 + if not self.empty: + if len(fields) == 1: + discriminant = 0 + else: + discriminant = int(content[fields[0]]) + 1 + self.active_variant = content[fields[discriminant]] + self.name = fields[discriminant].name + self.full_name = "{}::{}".format(valobj.type.name, self.name) + else: + self.full_name = valobj.type.name + + def to_string(self): + return self.full_name + + def children(self): + if not self.empty: + yield self.name, self.active_variant + + +class StdStringProvider: + def __init__(self, valobj): + self.valobj = valobj + vec = valobj["vec"] + self.length = int(vec["len"]) + self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + @staticmethod + def display_hint(): + return "string" + + +class StdOsStringProvider: + def __init__(self, valobj): + self.valobj = valobj + buf = self.valobj["inner"]["inner"] + is_windows = "Wtf8Buf" in buf.type.name + vec = buf[ZERO_FIELD] if is_windows else buf + + self.length = int(vec["len"]) + self.data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + def display_hint(self): + return "string" + + +class StdStrProvider: + def __init__(self, valobj): + self.valobj = valobj + self.length = int(valobj["length"]) + self.data_ptr = valobj["data_ptr"] + + def to_string(self): + return self.data_ptr.lazy_string(encoding="utf-8", length=self.length) + + @staticmethod + def display_hint(): + return "string" + + +class StdVecProvider: + def __init__(self, valobj): + self.valobj = valobj + self.length = int(valobj["len"]) + self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + + def to_string(self): + return "Vec(size={})".format(self.length) + + def children(self): + saw_inaccessible = False + for index in xrange(self.length): + element_ptr = self.data_ptr + index + if saw_inaccessible: + return + try: + # rust-lang/rust#64343: passing deref expr to `str` allows + # catching exception on garbage pointer + str(element_ptr.dereference()) + yield "[{}]".format(index), element_ptr.dereference() + except RuntimeError: + saw_inaccessible = True + yield str(index), "inaccessible" + + @staticmethod + def display_hint(): + return "array" + + +class StdVecDequeProvider: + def __init__(self, valobj): + self.valobj = valobj + self.head = int(valobj["head"]) + self.tail = int(valobj["tail"]) + self.cap = int(valobj["buf"]["cap"]) + self.data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + if self.head >= self.tail: + self.size = self.head - self.tail + else: + self.size = self.cap + self.head - self.tail + + def to_string(self): + return "VecDeque(size={})".format(self.size) + + def children(self): + for index in xrange(0, self.size): + value = (self.data_ptr + ((self.tail + index) % self.cap)).dereference() + yield "[{}]".format(index), value + + @staticmethod + def display_hint(): + return "array" + + +class StdRcProvider: + def __init__(self, valobj, is_atomic=False): + self.valobj = valobj + self.is_atomic = is_atomic + self.ptr = unwrap_unique_or_non_null(valobj["ptr"]) + self.value = self.ptr["data" if is_atomic else "value"] + self.strong = self.ptr["strong"]["v" if is_atomic else "value"]["value"] + self.weak = self.ptr["weak"]["v" if is_atomic else "value"]["value"] - 1 + + def to_string(self): + if self.is_atomic: + return "Arc(strong={}, weak={})".format(int(self.strong), int(self.weak)) + else: + return "Rc(strong={}, weak={})".format(int(self.strong), int(self.weak)) + + def children(self): + yield "value", self.value + yield "strong", self.strong + yield "weak", self.weak + + +class StdCellProvider: + def __init__(self, valobj): + self.value = valobj["value"]["value"] + + def to_string(self): + return "Cell" + + def children(self): + yield "value", self.value + + +class StdRefProvider: + def __init__(self, valobj): + self.value = valobj["value"].dereference() + self.borrow = valobj["borrow"]["borrow"]["value"]["value"] + + def to_string(self): + borrow = int(self.borrow) + if borrow >= 0: + return "Ref(borrow={})".format(borrow) + else: + return "Ref(borrow_mut={})".format(-borrow) + + def children(self): + yield "*value", self.value + yield "borrow", self.borrow + + +class StdRefCellProvider: + def __init__(self, valobj): + self.value = valobj["value"]["value"] + self.borrow = valobj["borrow"]["value"]["value"] + + def to_string(self): + borrow = int(self.borrow) + if borrow >= 0: + return "RefCell(borrow={})".format(borrow) + else: + return "RefCell(borrow_mut={})".format(-borrow) + + def children(self): + yield "value", self.value + yield "borrow", self.borrow + + +# Yield each key (and optionally value) from a BoxedNode. +def children_of_node(boxed_node, height, want_values): + def cast_to_internal(node): + internal_type_name = str(node.type.target()).replace("LeafNode", "InternalNode", 1) + internal_type = lookup_type(internal_type_name) + return node.cast(internal_type.pointer()) + + node_ptr = unwrap_unique_or_non_null(boxed_node["ptr"]) + node_ptr = cast_to_internal(node_ptr) if height > 0 else node_ptr + leaf = node_ptr["data"] if height > 0 else node_ptr.dereference() + keys = leaf["keys"] + values = leaf["vals"] + length = int(leaf["len"]) + + for i in xrange(0, length + 1): + if height > 0: + child_ptr = node_ptr["edges"][i]["value"]["value"] + for child in children_of_node(child_ptr, height - 1, want_values): + yield child + if i < length: + if want_values: + yield keys[i]["value"]["value"], values[i]["value"]["value"] + else: + yield keys[i]["value"]["value"] + + +class StdBTreeSetProvider: + def __init__(self, valobj): + self.valobj = valobj + + def to_string(self): + return "BTreeSet(size={})".format(self.valobj["map"]["length"]) + + def children(self): + inner_map = self.valobj["map"] + if inner_map["length"] > 0: + root = inner_map["root"] + if "core::option::Option<" in root.type.name: + type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] + root = root.cast(gdb.lookup_type(type_name)) + + node_ptr = root["node"] + for i, child in enumerate(children_of_node(node_ptr, root["height"], False)): + yield "[{}]".format(i), child + + @staticmethod + def display_hint(): + return "array" + + +class StdBTreeMapProvider: + def __init__(self, valobj): + self.valobj = valobj + + def to_string(self): + return "BTreeMap(size={})".format(self.valobj["length"]) + + def children(self): + if self.valobj["length"] > 0: + root = self.valobj["root"] + if "core::option::Option<" in root.type.name: + type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] + root = root.cast(gdb.lookup_type(type_name)) + + node_ptr = root["node"] + for i, child in enumerate(children_of_node(node_ptr, root["height"], True)): + yield "key{}".format(i), child[0] + yield "val{}".format(i), child[1] + + @staticmethod + def display_hint(): + return "map" + + +# BACKCOMPAT: rust 1.35 +class StdOldHashMapProvider: + def __init__(self, valobj, show_values=True): + self.valobj = valobj + self.show_values = show_values + + self.table = self.valobj["table"] + self.size = int(self.table["size"]) + self.hashes = self.table["hashes"] + self.hash_uint_type = self.hashes.type + self.hash_uint_size = self.hashes.type.sizeof + self.modulo = 2 ** self.hash_uint_size + self.data_ptr = self.hashes[ZERO_FIELD]["pointer"] + + self.capacity_mask = int(self.table["capacity_mask"]) + self.capacity = (self.capacity_mask + 1) % self.modulo + + marker = self.table["marker"].type + self.pair_type = marker.template_argument(0) + self.pair_type_size = self.pair_type.sizeof + + self.valid_indices = [] + for idx in range(self.capacity): + data_ptr = self.data_ptr.cast(self.hash_uint_type.pointer()) + address = data_ptr + idx + hash_uint = address.dereference() + hash_ptr = hash_uint[ZERO_FIELD]["pointer"] + if int(hash_ptr) != 0: + self.valid_indices.append(idx) + + def to_string(self): + if self.show_values: + return "HashMap(size={})".format(self.size) + else: + return "HashSet(size={})".format(self.size) + + def children(self): + start = int(self.data_ptr) & ~1 + + hashes = self.hash_uint_size * self.capacity + align = self.pair_type_size + len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~( + (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo + + pairs_offset = hashes + len_rounded_up + pairs_start = gdb.Value(start + pairs_offset).cast(self.pair_type.pointer()) + + for index in range(self.size): + table_index = self.valid_indices[index] + idx = table_index & self.capacity_mask + element = (pairs_start + idx).dereference() + if self.show_values: + yield "key{}".format(index), element[ZERO_FIELD] + yield "val{}".format(index), element[FIRST_FIELD] + else: + yield "[{}]".format(index), element[ZERO_FIELD] + + def display_hint(self): + return "map" if self.show_values else "array" + + +class StdHashMapProvider: + def __init__(self, valobj, show_values=True): + self.valobj = valobj + self.show_values = show_values + + table = self.valobj["base"]["table"] + capacity = int(table["bucket_mask"]) + 1 + ctrl = table["ctrl"]["pointer"] + + self.size = int(table["items"]) + self.data_ptr = table["data"]["pointer"] + self.pair_type = self.data_ptr.dereference().type + + self.valid_indices = [] + for idx in range(capacity): + address = ctrl + idx + value = address.dereference() + is_presented = value & 128 == 0 + if is_presented: + self.valid_indices.append(idx) + + def to_string(self): + if self.show_values: + return "HashMap(size={})".format(self.size) + else: + return "HashSet(size={})".format(self.size) + + def children(self): + pairs_start = self.data_ptr + + for index in range(self.size): + idx = self.valid_indices[index] + element = (pairs_start + idx).dereference() + if self.show_values: + yield "key{}".format(index), element[ZERO_FIELD] + yield "val{}".format(index), element[FIRST_FIELD] + else: + yield "[{}]".format(index), element[ZERO_FIELD] + + def display_hint(self): + return "map" if self.show_values else "array" diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py deleted file mode 100755 index d580329cb504e..0000000000000 --- a/src/etc/gdb_rust_pretty_printing.py +++ /dev/null @@ -1,466 +0,0 @@ -import gdb -import re -import sys -import debugger_pretty_printers_common as rustpp - -# We want a version of `range` which doesn't allocate an intermediate list, -# specifically it should use a lazy iterator. In Python 2 this was `xrange`, but -# if we're running with Python 3 then we need to use `range` instead. -if sys.version_info[0] >= 3: - xrange = range - -rust_enabled = 'set language rust' in gdb.execute('complete set language ru', to_string=True) - -# The btree pretty-printers fail in a confusing way unless -# https://sourceware.org/bugzilla/show_bug.cgi?id=21763 is fixed. -# This fix went in 8.1, so check for that. -# See https://github.com/rust-lang/rust/issues/56730 -gdb_81 = False -_match = re.search('([0-9]+)\\.([0-9]+)', gdb.VERSION) -if _match: - if int(_match.group(1)) > 8 or (int(_match.group(1)) == 8 and int(_match.group(2)) >= 1): - gdb_81 = True - -# =============================================================================== -# GDB Pretty Printing Module for Rust -# =============================================================================== - - -class GdbType(rustpp.Type): - - def __init__(self, ty): - super(GdbType, self).__init__() - self.ty = ty - self.fields = None - - def get_unqualified_type_name(self): - tag = self.ty.tag - - if tag is None: - return tag - - return rustpp.extract_type_name(tag).replace("&'static ", "&") - - def get_dwarf_type_kind(self): - if self.ty.code == gdb.TYPE_CODE_STRUCT: - return rustpp.DWARF_TYPE_CODE_STRUCT - - if self.ty.code == gdb.TYPE_CODE_UNION: - return rustpp.DWARF_TYPE_CODE_UNION - - if self.ty.code == gdb.TYPE_CODE_PTR: - return rustpp.DWARF_TYPE_CODE_PTR - - if self.ty.code == gdb.TYPE_CODE_ENUM: - return rustpp.DWARF_TYPE_CODE_ENUM - - def get_fields(self): - assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION)) - if self.fields is None: - self.fields = list(self.ty.fields()) - return self.fields - - def get_wrapped_value(self): - return self.ty - - -class GdbValue(rustpp.Value): - def __init__(self, gdb_val): - super(GdbValue, self).__init__(GdbType(gdb_val.type)) - self.gdb_val = gdb_val - self.children = {} - - def get_child_at_index(self, index): - child = self.children.get(index) - if child is None: - gdb_field = get_field_at_index(self.gdb_val, index) - child = GdbValue(self.gdb_val[gdb_field]) - self.children[index] = child - return child - - def as_integer(self): - if self.gdb_val.type.code == gdb.TYPE_CODE_PTR: - as_str = rustpp.compat_str(self.gdb_val).split()[0] - return int(as_str, 0) - return int(self.gdb_val) - - def get_wrapped_value(self): - return self.gdb_val - - -def register_printers(objfile): - """Registers Rust pretty printers for the given objfile""" - objfile.pretty_printers.append(rust_pretty_printer_lookup_function) - - -def rust_pretty_printer_lookup_function(gdb_val): - """ - Returns the correct Rust pretty printer for the given value - if there is one - """ - - val = GdbValue(gdb_val) - type_kind = val.type.get_type_kind() - - if type_kind == rustpp.TYPE_KIND_SLICE: - return RustSlicePrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_VEC: - return RustStdVecPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_VECDEQUE: - return RustStdVecDequePrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_BTREESET and gdb_81: - return RustStdBTreeSetPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_BTREEMAP and gdb_81: - return RustStdBTreeMapPrinter(val) - - if type_kind == rustpp.TYPE_KIND_STD_STRING: - return RustStdStringPrinter(val) - - if type_kind == rustpp.TYPE_KIND_OS_STRING: - return RustOsStringPrinter(val) - - # Checks after this point should only be for "compiler" types -- - # things that gdb's Rust language support knows about. - if rust_enabled: - return None - - if type_kind == rustpp.TYPE_KIND_EMPTY: - return RustEmptyPrinter(val) - - if type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT: - return RustStructPrinter(val, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STR_SLICE: - return RustStringSlicePrinter(val) - - if type_kind == rustpp.TYPE_KIND_TUPLE: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=True, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT: - return RustStructPrinter(val, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT: - return RustCStyleVariantPrinter(val.get_child_at_index(0)) - - if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT: - return RustStructPrinter(val, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM: - variant = get_field_at_index(gdb_val, 0) - return rust_pretty_printer_lookup_function(gdb_val[variant]) - - if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM: - # This is a regular enum, extract the discriminant - discriminant_val = rustpp.get_discriminant_value_as_integer(val) - variant = get_field_at_index(gdb_val, discriminant_val) - return rust_pretty_printer_lookup_function(gdb_val[variant]) - - if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM: - encoded_enum_info = rustpp.EncodedEnumInfo(val) - if encoded_enum_info.is_null_variant(): - return IdentityPrinter(encoded_enum_info.get_null_variant_name()) - - non_null_val = encoded_enum_info.get_non_null_variant_val() - return rust_pretty_printer_lookup_function(non_null_val.get_wrapped_value()) - - # No pretty printer has been found - return None - - -# =------------------------------------------------------------------------------ -# Pretty Printer Classes -# =------------------------------------------------------------------------------ -class RustEmptyPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - return self.__val.type.get_unqualified_type_name() - - -class RustStructPrinter(object): - def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like): - self.__val = val - self.__omit_first_field = omit_first_field - self.__omit_type_name = omit_type_name - self.__is_tuple_like = is_tuple_like - - def to_string(self): - if self.__omit_type_name: - return None - return self.__val.type.get_unqualified_type_name() - - def children(self): - cs = [] - wrapped_value = self.__val.get_wrapped_value() - - for number, field in enumerate(self.__val.type.get_fields()): - field_value = wrapped_value[field.name] - if self.__is_tuple_like: - cs.append((str(number), field_value)) - else: - cs.append((field.name, field_value)) - - if self.__omit_first_field: - cs = cs[1:] - - return cs - - def display_hint(self): - if self.__is_tuple_like: - return "array" - else: - return "" - - -class RustSlicePrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % length)) - - def children(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - raw_ptr = data_ptr.get_wrapped_value() - - for index in xrange(0, length): - yield (str(index), (raw_ptr + index).dereference()) - - -class RustStringSlicePrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) - raw_ptr = data_ptr.get_wrapped_value() - return raw_ptr.lazy_string(encoding="utf-8", length=length) - - def display_hint(self): - return "string" - - -class RustStdVecPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i, cap: %i)" % (length, cap))) - - def children(self): - saw_inaccessible = False - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) - gdb_ptr = data_ptr.get_wrapped_value() - for index in xrange(0, length): - if saw_inaccessible: - return - try: - # rust-lang/rust#64343: passing deref expr to `str` allows - # catching exception on garbage pointer - str((gdb_ptr + index).dereference()) - yield (str(index), (gdb_ptr + index).dereference()) - except RuntimeError: - saw_inaccessible = True - yield (str(index), "inaccessible") - - -class RustStdVecDequePrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - (tail, head, data_ptr, cap) = \ - rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val) - if head >= tail: - size = head - tail - else: - size = cap + head - tail - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i, cap: %i)" % (size, cap))) - - def children(self): - (tail, head, data_ptr, cap) = \ - rustpp.extract_tail_head_ptr_and_cap_from_std_vecdeque(self.__val) - gdb_ptr = data_ptr.get_wrapped_value() - if head >= tail: - size = head - tail - else: - size = cap + head - tail - for index in xrange(0, size): - yield (str(index), (gdb_ptr + ((tail + index) % cap)).dereference()) - - -# Yield each key (and optionally value) from a BoxedNode. -def children_of_node(boxed_node, height, want_values): - node_ptr = boxed_node['ptr']['pointer'] - if height > 0: - type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode', 1) - node_type = gdb.lookup_type(type_name) - node_ptr = node_ptr.cast(node_type.pointer()) - leaf = node_ptr['data'] - else: - leaf = node_ptr.dereference() - keys = leaf['keys'] - if want_values: - values = leaf['vals'] - length = int(leaf['len']) - for i in xrange(0, length + 1): - if height > 0: - child_ptr = node_ptr['edges'][i]['value']['value'] - for child in children_of_node(child_ptr, height - 1, want_values): - yield child - if i < length: - if want_values: - yield (keys[i]['value']['value'], values[i]['value']['value']) - else: - yield keys[i]['value']['value'] - - -class RustStdBTreeSetPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "array" - - def to_string(self): - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % self.__val.get_wrapped_value()['map']['length'])) - - def children(self): - prev_idx = None - innermap = GdbValue(self.__val.get_wrapped_value()['map']) - if innermap.get_wrapped_value()['length'] > 0: - root = GdbValue(innermap.get_wrapped_value()['root']) - type_name = str(root.type.ty.name).replace('core::option::Option<', '', 1)[:-1] - root = root.get_wrapped_value().cast(gdb.lookup_type(type_name)) - node_ptr = root['node'] - i = 0 - for child in children_of_node(node_ptr, root['height'], False): - yield (str(i), child) - i = i + 1 - - -class RustStdBTreeMapPrinter(object): - def __init__(self, val): - self.__val = val - - @staticmethod - def display_hint(): - return "map" - - def to_string(self): - return (self.__val.type.get_unqualified_type_name() + - ("(len: %i)" % self.__val.get_wrapped_value()['length'])) - - def children(self): - if self.__val.get_wrapped_value()['length'] > 0: - root = GdbValue(self.__val.get_wrapped_value()['root']) - type_name = str(root.type.ty.name).replace('core::option::Option<', '', 1)[:-1] - root = root.get_wrapped_value().cast(gdb.lookup_type(type_name)) - node_ptr = root['node'] - i = 0 - for child in children_of_node(node_ptr, root['height'], True): - yield (str(i), child[0]) - yield (str(i), child[1]) - i = i + 1 - - -class RustStdStringPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - vec = self.__val.get_child_at_index(0) - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) - return data_ptr.get_wrapped_value().lazy_string(encoding="utf-8", - length=length) - - def display_hint(self): - return "string" - - -class RustOsStringPrinter(object): - def __init__(self, val): - self.__val = val - - def to_string(self): - buf = self.__val.get_child_at_index(0) - vec = buf.get_child_at_index(0) - if vec.type.get_unqualified_type_name() == "Wtf8Buf": - vec = vec.get_child_at_index(0) - - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec( - vec) - return data_ptr.get_wrapped_value().lazy_string(length=length) - - def display_hint(self): - return "string" - - -class RustCStyleVariantPrinter(object): - def __init__(self, val): - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM - self.__val = val - - def to_string(self): - return str(self.__val.get_wrapped_value()) - - -class IdentityPrinter(object): - def __init__(self, string): - self.string = string - - def to_string(self): - return self.string - - -def get_field_at_index(gdb_val, index): - i = 0 - for field in gdb_val.type.fields(): - if i == index: - return field - i += 1 - return None diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands new file mode 100644 index 0000000000000..f470c62d89927 --- /dev/null +++ b/src/etc/lldb_commands @@ -0,0 +1,19 @@ +command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_lookup.py\" +type synthetic add -l lldb_lookup.synthetic_lookup -x \".*\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)String$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&str$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&\\[.+\\]$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::ffi::([a-z_]+::)+)OsString$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Vec<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)VecDeque<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeSet<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeMap<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashMap<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashSet<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Rc<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Arc<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Cell<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Ref<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefMut<.+>$\" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefCell<.+>$\" --category Rust +type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py new file mode 100644 index 0000000000000..13420fbaf0a75 --- /dev/null +++ b/src/etc/lldb_lookup.py @@ -0,0 +1,115 @@ +import lldb + +from lldb_providers import * +from rust_types import RustType, classify_struct, classify_union + + +# BACKCOMPAT: rust 1.35 +def is_hashbrown_hashmap(hash_map): + return len(hash_map.type.fields) == 1 + + +def classify_rust_type(type): + type_class = type.GetTypeClass() + if type_class == lldb.eTypeClassStruct: + return classify_struct(type.name, type.fields) + if type_class == lldb.eTypeClassUnion: + return classify_union(type.fields) + + return RustType.OTHER + + +def summary_lookup(valobj, dict): + # type: (SBValue, dict) -> str + """Returns the summary provider for the given value""" + rust_type = classify_rust_type(valobj.GetType()) + + if rust_type == RustType.STD_STRING: + return StdStringSummaryProvider(valobj, dict) + if rust_type == RustType.STD_OS_STRING: + return StdOsStringSummaryProvider(valobj, dict) + if rust_type == RustType.STD_STR: + return StdStrSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_VEC: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_VEC_DEQUE: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_SLICE: + return SizeSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_HASH_MAP: + return SizeSummaryProvider(valobj, dict) + if rust_type == RustType.STD_HASH_SET: + return SizeSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_RC: + return StdRcSummaryProvider(valobj, dict) + if rust_type == RustType.STD_ARC: + return StdRcSummaryProvider(valobj, dict) + + if rust_type == RustType.STD_REF: + return StdRefSummaryProvider(valobj, dict) + if rust_type == RustType.STD_REF_MUT: + return StdRefSummaryProvider(valobj, dict) + if rust_type == RustType.STD_REF_CELL: + return StdRefSummaryProvider(valobj, dict) + + return "" + + +def synthetic_lookup(valobj, dict): + # type: (SBValue, dict) -> object + """Returns the synthetic provider for the given value""" + rust_type = classify_rust_type(valobj.GetType()) + + if rust_type == RustType.STRUCT: + return StructSyntheticProvider(valobj, dict) + if rust_type == RustType.STRUCT_VARIANT: + return StructSyntheticProvider(valobj, dict, is_variant=True) + if rust_type == RustType.TUPLE: + return TupleSyntheticProvider(valobj, dict) + if rust_type == RustType.TUPLE_VARIANT: + return TupleSyntheticProvider(valobj, dict, is_variant=True) + if rust_type == RustType.EMPTY: + return EmptySyntheticProvider(valobj, dict) + if rust_type == RustType.REGULAR_ENUM: + discriminant = valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned() + return synthetic_lookup(valobj.GetChildAtIndex(discriminant), dict) + if rust_type == RustType.SINGLETON_ENUM: + return synthetic_lookup(valobj.GetChildAtIndex(0), dict) + + if rust_type == RustType.STD_VEC: + return StdVecSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_VEC_DEQUE: + return StdVecDequeSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_SLICE: + return StdSliceSyntheticProvider(valobj, dict) + + if rust_type == RustType.STD_HASH_MAP: + if is_hashbrown_hashmap(valobj): + return StdHashMapSyntheticProvider(valobj, dict) + else: + return StdOldHashMapSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_HASH_SET: + hash_map = valobj.GetChildAtIndex(0) + if is_hashbrown_hashmap(hash_map): + return StdHashMapSyntheticProvider(hash_map, dict, show_values=False) + else: + return StdOldHashMapSyntheticProvider(hash_map, dict, show_values=False) + + if rust_type == RustType.STD_RC: + return StdRcSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_ARC: + return StdRcSyntheticProvider(valobj, dict, is_atomic=True) + + if rust_type == RustType.STD_CELL: + return StdCellSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF: + return StdRefSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF_MUT: + return StdRefSyntheticProvider(valobj, dict) + if rust_type == RustType.STD_REF_CELL: + return StdRefSyntheticProvider(valobj, dict, is_cell=True) + + return DefaultSynthteticProvider(valobj, dict) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py new file mode 100644 index 0000000000000..3c7817b3a618d --- /dev/null +++ b/src/etc/lldb_providers.py @@ -0,0 +1,715 @@ +import sys + +from lldb import SBValue, SBData, SBError, eBasicTypeLong, eBasicTypeUnsignedLong, \ + eBasicTypeUnsignedChar + +# from lldb.formatters import Logger + +#################################################################################################### +# This file contains two kinds of pretty-printers: summary and synthetic. +# +# Important classes from LLDB module: +# SBValue: the value of a variable, a register, or an expression +# SBType: the data type; each SBValue has a corresponding SBType +# +# Summary provider is a function with the type `(SBValue, dict) -> str`. +# The first parameter is the object encapsulating the actual variable being displayed; +# The second parameter is an internal support parameter used by LLDB, and you should not touch it. +# +# Synthetic children is the way to provide a children-based representation of the object's value. +# Synthetic provider is a class that implements the following interface: +# +# class SyntheticChildrenProvider: +# def __init__(self, SBValue, dict) +# def num_children(self) +# def get_child_index(self, str) +# def get_child_at_index(self, int) +# def update(self) +# def has_children(self) +# def get_value(self) +# +# +# You can find more information and examples here: +# 1. https://lldb.llvm.org/varformats.html +# 2. https://lldb.llvm.org/python-reference.html +# 3. https://lldb.llvm.org/python_reference/lldb.formatters.cpp.libcxx-pysrc.html +# 4. https://github.com/llvm-mirror/lldb/tree/master/examples/summaries/cocoa +#################################################################################################### + +PY3 = sys.version_info[0] == 3 + + +class ValueBuilder: + def __init__(self, valobj): + # type: (SBValue) -> ValueBuilder + self.valobj = valobj + process = valobj.GetProcess() + self.endianness = process.GetByteOrder() + self.pointer_size = process.GetAddressByteSize() + + def from_int(self, name, value): + # type: (str, int) -> SBValue + type = self.valobj.GetType().GetBasicType(eBasicTypeLong) + data = SBData.CreateDataFromSInt64Array(self.endianness, self.pointer_size, [value]) + return self.valobj.CreateValueFromData(name, data, type) + + def from_uint(self, name, value): + # type: (str, int) -> SBValue + type = self.valobj.GetType().GetBasicType(eBasicTypeUnsignedLong) + data = SBData.CreateDataFromUInt64Array(self.endianness, self.pointer_size, [value]) + return self.valobj.CreateValueFromData(name, data, type) + + +def unwrap_unique_or_non_null(unique_or_nonnull): + # BACKCOMPAT: rust 1.32 + # https://github.com/rust-lang/rust/commit/7a0911528058e87d22ea305695f4047572c5e067 + ptr = unique_or_nonnull.GetChildMemberWithName("pointer") + return ptr if ptr.TypeIsPointerType() else ptr.GetChildAtIndex(0) + + +class DefaultSynthteticProvider: + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> DefaultSynthteticProvider + # logger = Logger.Logger() + # logger >> "Default synthetic provider for " + str(valobj.GetName()) + self.valobj = valobj + + def num_children(self): + # type: () -> int + return self.valobj.GetNumChildren() + + def get_child_index(self, name): + # type: (str) -> int + return self.valobj.GetIndexOfChildWithName(name) + + def get_child_at_index(self, index): + # type: (int) -> SBValue + return self.valobj.GetChildAtIndex(index) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return self.valobj.MightHaveChildren() + + +class EmptySyntheticProvider: + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> EmptySyntheticProvider + # logger = Logger.Logger() + # logger >> "[EmptySyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + + def num_children(self): + # type: () -> int + return 0 + + def get_child_index(self, name): + # type: (str) -> int + return None + + def get_child_at_index(self, index): + # type: (int) -> SBValue + return None + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return False + + +def SizeSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + return 'size=' + str(valobj.GetNumChildren()) + + +def vec_to_string(vec): + length = vec.GetNumChildren() + chars = [vec.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(length)] + return bytes(chars).decode(errors='replace') if PY3 else "".join(chr(char) for char in chars) + + +def StdStringSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdStringSummaryProvider] for " + str(valobj.GetName()) + vec = valobj.GetChildAtIndex(0) + return '"%s"' % vec_to_string(vec) + + +def StdOsStringSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdOsStringSummaryProvider] for " + str(valobj.GetName()) + buf = valobj.GetChildAtIndex(0).GetChildAtIndex(0) + is_windows = "Wtf8Buf" in buf.type.name + vec = buf.GetChildAtIndex(0) if is_windows else buf + return '"%s"' % vec_to_string(vec) + + +def StdStrSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + # logger = Logger.Logger() + # logger >> "[StdStrSummaryProvider] for " + str(valobj.GetName()) + + length = valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + if length == 0: + return '""' + + data_ptr = valobj.GetChildMemberWithName("data_ptr") + + start = data_ptr.GetValueAsUnsigned() + error = SBError() + process = data_ptr.GetProcess() + data = process.ReadMemory(start, length, error) + data = data.decode(encoding='UTF-8') if PY3 else data + return '"%s"' % data + + +class StructSyntheticProvider: + """Pretty-printer for structs and struct enum variants""" + + def __init__(self, valobj, dict, is_variant=False): + # type: (SBValue, dict, bool) -> StructSyntheticProvider + # logger = Logger.Logger() + self.valobj = valobj + self.is_variant = is_variant + self.type = valobj.GetType() + self.fields = {} + + if is_variant: + self.fields_count = self.type.GetNumberOfFields() - 1 + real_fields = self.type.fields[1:] + else: + self.fields_count = self.type.GetNumberOfFields() + real_fields = self.type.fields + + for number, field in enumerate(real_fields): + self.fields[field.name] = number + + def num_children(self): + # type: () -> int + return self.fields_count + + def get_child_index(self, name): + # type: (str) -> int + return self.fields.get(name, -1) + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if self.is_variant: + field = self.type.GetFieldAtIndex(index + 1) + else: + field = self.type.GetFieldAtIndex(index) + return self.valobj.GetChildMemberWithName(field.name) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +class TupleSyntheticProvider: + """Pretty-printer for tuples and tuple enum variants""" + + def __init__(self, valobj, dict, is_variant=False): + # type: (SBValue, dict, bool) -> TupleSyntheticProvider + # logger = Logger.Logger() + self.valobj = valobj + self.is_variant = is_variant + self.type = valobj.GetType() + + if is_variant: + self.size = self.type.GetNumberOfFields() - 1 + else: + self.size = self.type.GetNumberOfFields() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + if name.isdigit(): + return int(name) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if self.is_variant: + field = self.type.GetFieldAtIndex(index + 1) + else: + field = self.type.GetFieldAtIndex(index) + element = self.valobj.GetChildMemberWithName(field.name) + return self.valobj.CreateValueFromData(str(index), element.GetData(), element.GetType()) + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +class StdVecSyntheticProvider: + """Pretty-printer for alloc::vec::Vec + + struct Vec { buf: RawVec, len: usize } + struct RawVec { ptr: Unique, cap: usize, ... } + rust 1.31.1: struct Unique { pointer: NonZero<*const T>, ... } + rust 1.33.0: struct Unique { pointer: *const T, ... } + struct NonZero(T) + """ + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdVecSyntheticProvider + # logger = Logger.Logger() + # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.length + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + index * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.buf = self.valobj.GetChildMemberWithName("buf") + + self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +class StdSliceSyntheticProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.length + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + index * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.length = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + self.data_ptr = self.valobj.GetChildMemberWithName("data_ptr") + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +class StdVecDequeSyntheticProvider: + """Pretty-printer for alloc::collections::vec_deque::VecDeque + + struct VecDeque { tail: usize, head: usize, buf: RawVec } + """ + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdVecDequeSyntheticProvider + # logger = Logger.Logger() + # logger >> "[StdVecDequeSyntheticProvider] for " + str(valobj.GetName()) + self.valobj = valobj + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit() and self.tail <= index and (self.tail + index) % self.cap < self.head: + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + start = self.data_ptr.GetValueAsUnsigned() + address = start + ((index + self.tail) % self.cap) * self.element_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.element_type) + return element + + def update(self): + # type: () -> None + self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() + self.tail = self.valobj.GetChildMemberWithName("tail").GetValueAsUnsigned() + self.buf = self.valobj.GetChildMemberWithName("buf") + self.cap = self.buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + if self.head >= self.tail: + self.size = self.head - self.tail + else: + self.size = self.cap + self.head - self.tail + + self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) + + self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type_size = self.element_type.GetByteSize() + + def has_children(self): + # type: () -> bool + return True + + +# BACKCOMPAT: rust 1.35 +class StdOldHashMapSyntheticProvider: + """Pretty-printer for std::collections::hash::map::HashMap + + struct HashMap {..., table: RawTable, ... } + struct RawTable { capacity_mask: usize, size: usize, hashes: TaggedHashUintPtr, ... } + """ + + def __init__(self, valobj, dict, show_values=True): + # type: (SBValue, dict, bool) -> StdOldHashMapSyntheticProvider + self.valobj = valobj + self.show_values = show_values + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + # logger = Logger.Logger() + start = self.data_ptr.GetValueAsUnsigned() & ~1 + + # See `libstd/collections/hash/table.rs:raw_bucket_at + hashes = self.hash_uint_size * self.capacity + align = self.pair_type_size + # See `libcore/alloc.rs:padding_needed_for` + len_rounded_up = (((((hashes + align) % self.modulo - 1) % self.modulo) & ~( + (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo + # len_rounded_up = ((hashes + align - 1) & ~(align - 1)) - hashes + + pairs_offset = hashes + len_rounded_up + pairs_start = start + pairs_offset + + table_index = self.valid_indices[index] + idx = table_index & self.capacity_mask + address = pairs_start + idx * self.pair_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type) + if self.show_values: + return element + else: + key = element.GetChildAtIndex(0) + return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType()) + + def update(self): + # type: () -> None + # logger = Logger.Logger() + + self.table = self.valobj.GetChildMemberWithName("table") # type: SBValue + self.size = self.table.GetChildMemberWithName("size").GetValueAsUnsigned() + self.hashes = self.table.GetChildMemberWithName("hashes") + self.hash_uint_type = self.hashes.GetType() + self.hash_uint_size = self.hashes.GetType().GetByteSize() + self.modulo = 2 ** self.hash_uint_size + self.data_ptr = self.hashes.GetChildAtIndex(0).GetChildAtIndex(0) + + self.capacity_mask = self.table.GetChildMemberWithName("capacity_mask").GetValueAsUnsigned() + self.capacity = (self.capacity_mask + 1) % self.modulo + + marker = self.table.GetChildMemberWithName("marker").GetType() # type: SBType + self.pair_type = marker.template_args[0] + self.pair_type_size = self.pair_type.GetByteSize() + + self.valid_indices = [] + for idx in range(self.capacity): + address = self.data_ptr.GetValueAsUnsigned() + idx * self.hash_uint_size + hash_uint = self.data_ptr.CreateValueFromAddress("[%s]" % idx, address, + self.hash_uint_type) + hash_ptr = hash_uint.GetChildAtIndex(0).GetChildAtIndex(0) + if hash_ptr.GetValueAsUnsigned() != 0: + self.valid_indices.append(idx) + + # logger >> "Valid indices: {}".format(str(self.valid_indices)) + + def has_children(self): + # type: () -> bool + return True + + +class StdHashMapSyntheticProvider: + """Pretty-printer for hashbrown's HashMap""" + + def __init__(self, valobj, dict, show_values=True): + # type: (SBValue, dict, bool) -> StdHashMapSyntheticProvider + self.valobj = valobj + self.show_values = show_values + self.update() + + def num_children(self): + # type: () -> int + return self.size + + def get_child_index(self, name): + # type: (str) -> int + index = name.lstrip('[').rstrip(']') + if index.isdigit(): + return int(index) + else: + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + pairs_start = self.data_ptr.GetValueAsUnsigned() + idx = self.valid_indices[index] + address = pairs_start + idx * self.pair_type_size + element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type) + if self.show_values: + return element + else: + key = element.GetChildAtIndex(0) + return self.valobj.CreateValueFromData("[%s]" % index, key.GetData(), key.GetType()) + + def update(self): + # type: () -> None + table = self.valobj.GetChildMemberWithName("base").GetChildMemberWithName("table") + capacity = table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 + ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) + + self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned() + self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0) + self.pair_type = self.data_ptr.Dereference().GetType() + self.pair_type_size = self.pair_type.GetByteSize() + + u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar) + u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize() + + self.valid_indices = [] + for idx in range(capacity): + address = ctrl.GetValueAsUnsigned() + idx * u8_type_size + value = ctrl.CreateValueFromAddress("ctrl[%s]" % idx, address, + u8_type).GetValueAsUnsigned() + is_present = value & 128 == 0 + if is_present: + self.valid_indices.append(idx) + + def has_children(self): + # type: () -> bool + return True + + +def StdRcSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + strong = valobj.GetChildMemberWithName("strong").GetValueAsUnsigned() + weak = valobj.GetChildMemberWithName("weak").GetValueAsUnsigned() + return "strong={}, weak={}".format(strong, weak) + + +class StdRcSyntheticProvider: + """Pretty-printer for alloc::rc::Rc and alloc::sync::Arc + + struct Rc { ptr: NonNull>, ... } + rust 1.31.1: struct NonNull { pointer: NonZero<*const T> } + rust 1.33.0: struct NonNull { pointer: *const T } + struct NonZero(T) + struct RcBox { strong: Cell, weak: Cell, value: T } + struct Cell { value: UnsafeCell } + struct UnsafeCell { value: T } + + struct Arc { ptr: NonNull>, ... } + struct ArcInner { strong: atomic::AtomicUsize, weak: atomic::AtomicUsize, data: T } + struct AtomicUsize { v: UnsafeCell } + """ + + def __init__(self, valobj, dict, is_atomic=False): + # type: (SBValue, dict, bool) -> StdRcSyntheticProvider + self.valobj = valobj + + self.ptr = unwrap_unique_or_non_null(self.valobj.GetChildMemberWithName("ptr")) + + self.value = self.ptr.GetChildMemberWithName("data" if is_atomic else "value") + + self.strong = self.ptr.GetChildMemberWithName("strong").GetChildAtIndex( + 0).GetChildMemberWithName("value") + self.weak = self.ptr.GetChildMemberWithName("weak").GetChildAtIndex( + 0).GetChildMemberWithName("value") + + self.value_builder = ValueBuilder(valobj) + + self.update() + + def num_children(self): + # type: () -> int + # Actually there are 3 children, but only the `value` should be shown as a child + return 1 + + def get_child_index(self, name): + # type: (str) -> int + if name == "value": + return 0 + if name == "strong": + return 1 + if name == "weak": + return 2 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + if index == 1: + return self.value_builder.from_uint("strong", self.strong_count) + if index == 2: + return self.value_builder.from_uint("weak", self.weak_count) + + return None + + def update(self): + # type: () -> None + self.strong_count = self.strong.GetValueAsUnsigned() + self.weak_count = self.weak.GetValueAsUnsigned() - 1 + + def has_children(self): + # type: () -> bool + return True + + +class StdCellSyntheticProvider: + """Pretty-printer for std::cell::Cell""" + + def __init__(self, valobj, dict): + # type: (SBValue, dict) -> StdCellSyntheticProvider + self.valobj = valobj + self.value = valobj.GetChildMemberWithName("value").GetChildAtIndex(0) + + def num_children(self): + # type: () -> int + return 1 + + def get_child_index(self, name): + # type: (str) -> int + if name == "value": + return 0 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + return None + + def update(self): + # type: () -> None + pass + + def has_children(self): + # type: () -> bool + return True + + +def StdRefSummaryProvider(valobj, dict): + # type: (SBValue, dict) -> str + borrow = valobj.GetChildMemberWithName("borrow").GetValueAsSigned() + return "borrow={}".format(borrow) if borrow >= 0 else "borrow_mut={}".format(-borrow) + + +class StdRefSyntheticProvider: + """Pretty-printer for std::cell::Ref, std::cell::RefMut, and std::cell::RefCell""" + + def __init__(self, valobj, dict, is_cell=False): + # type: (SBValue, dict, bool) -> StdRefSyntheticProvider + self.valobj = valobj + + borrow = valobj.GetChildMemberWithName("borrow") + value = valobj.GetChildMemberWithName("value") + if is_cell: + self.borrow = borrow.GetChildMemberWithName("value").GetChildMemberWithName("value") + self.value = value.GetChildMemberWithName("value") + else: + self.borrow = borrow.GetChildMemberWithName("borrow").GetChildMemberWithName( + "value").GetChildMemberWithName("value") + self.value = value.Dereference() + + self.value_builder = ValueBuilder(valobj) + + self.update() + + def num_children(self): + # type: () -> int + # Actually there are 2 children, but only the `value` should be shown as a child + return 1 + + def get_child_index(self, name): + if name == "value": + return 0 + if name == "borrow": + return 1 + return -1 + + def get_child_at_index(self, index): + # type: (int) -> SBValue + if index == 0: + return self.value + if index == 1: + return self.value_builder.from_int("borrow", self.borrow_count) + return None + + def update(self): + # type: () -> None + self.borrow_count = self.borrow.GetValueAsSigned() + + def has_children(self): + # type: () -> bool + return True diff --git a/src/etc/lldb_rust_formatters.py b/src/etc/lldb_rust_formatters.py deleted file mode 100644 index 0c4021b36fb6f..0000000000000 --- a/src/etc/lldb_rust_formatters.py +++ /dev/null @@ -1,305 +0,0 @@ -import lldb -import debugger_pretty_printers_common as rustpp - -# =============================================================================== -# LLDB Pretty Printing Module for Rust -# =============================================================================== - - -class LldbType(rustpp.Type): - - def __init__(self, ty): - super(LldbType, self).__init__() - self.ty = ty - self.fields = None - - def get_unqualified_type_name(self): - qualified_name = self.ty.GetName() - - if qualified_name is None: - return qualified_name - - return rustpp.extract_type_name(qualified_name).replace("&'static ", "&") - - def get_dwarf_type_kind(self): - type_class = self.ty.GetTypeClass() - - if type_class == lldb.eTypeClassStruct: - return rustpp.DWARF_TYPE_CODE_STRUCT - - if type_class == lldb.eTypeClassUnion: - return rustpp.DWARF_TYPE_CODE_UNION - - if type_class == lldb.eTypeClassPointer: - return rustpp.DWARF_TYPE_CODE_PTR - - if type_class == lldb.eTypeClassArray: - return rustpp.DWARF_TYPE_CODE_ARRAY - - if type_class == lldb.eTypeClassEnumeration: - return rustpp.DWARF_TYPE_CODE_ENUM - - return None - - def get_fields(self): - assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or - (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION)) - if self.fields is None: - self.fields = list(self.ty.fields) - return self.fields - - def get_wrapped_value(self): - return self.ty - - -class LldbValue(rustpp.Value): - def __init__(self, lldb_val): - ty = lldb_val.type - wty = LldbType(ty) - super(LldbValue, self).__init__(wty) - self.lldb_val = lldb_val - self.children = {} - - def get_child_at_index(self, index): - child = self.children.get(index) - if child is None: - lldb_field = self.lldb_val.GetChildAtIndex(index) - child = LldbValue(lldb_field) - self.children[index] = child - return child - - def as_integer(self): - return self.lldb_val.GetValueAsUnsigned() - - def get_wrapped_value(self): - return self.lldb_val - - -def print_val(lldb_val, internal_dict): - val = LldbValue(lldb_val) - type_kind = val.type.get_type_kind() - - if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or - type_kind == rustpp.TYPE_KIND_REGULAR_UNION or - type_kind == rustpp.TYPE_KIND_EMPTY): - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT: - return print_struct_val(val, - internal_dict, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=False) - - if type_kind == rustpp.TYPE_KIND_SLICE: - return print_vec_slice_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STR_SLICE: - return print_str_slice_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STD_VEC: - return print_std_vec_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_STD_STRING: - return print_std_string_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_TUPLE: - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=True, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT: - return print_struct_val(val, - internal_dict, - omit_first_field=False, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT: - return val.type.get_unqualified_type_name() - - if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT: - return print_struct_val(val, - internal_dict, - omit_first_field=True, - omit_type_name=False, - is_tuple_like=True) - - if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM: - return print_val(lldb_val.GetChildAtIndex(0), internal_dict) - - if type_kind == rustpp.TYPE_KIND_PTR: - return print_pointer_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_FIXED_SIZE_VEC: - return print_fixed_size_vec_val(val, internal_dict) - - if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM: - # This is a regular enum, extract the discriminant - discriminant_val = rustpp.get_discriminant_value_as_integer(val) - return print_val(lldb_val.GetChildAtIndex(discriminant_val), internal_dict) - - if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM: - encoded_enum_info = rustpp.EncodedEnumInfo(val) - if encoded_enum_info.is_null_variant(): - return encoded_enum_info.get_null_variant_name() - - non_null_val = encoded_enum_info.get_non_null_variant_val() - return print_val(non_null_val.get_wrapped_value(), internal_dict) - - # No pretty printer has been found - return lldb_val.GetValue() - - -# =--------------------------------------------------------------------------------------- -# Type-Specialized Printing Functions -# =--------------------------------------------------------------------------------------- - -def print_struct_val(val, internal_dict, omit_first_field, omit_type_name, is_tuple_like): - """ - Prints a struct, tuple, or tuple struct value with Rust syntax. - Ignores any fields before field_start_index. - """ - assert (val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT or - val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION) - - if omit_type_name: - type_name = "" - else: - type_name = val.type.get_unqualified_type_name() - - if is_tuple_like: - template = "%(type_name)s(%(body)s)" - separator = ", " - else: - template = "%(type_name)s {\n%(body)s\n}" - separator = ", \n" - - fields = val.type.get_fields() - - def render_child(child_index): - this = "" - if not is_tuple_like: - field_name = fields[child_index].name - this += field_name + ": " - - field_val = val.get_child_at_index(child_index) - - if not field_val.get_wrapped_value().IsValid(): - field = fields[child_index] - # LLDB is not good at handling zero-sized values, so we have to help - # it a little - if field.GetType().GetByteSize() == 0: - return this + rustpp.extract_type_name(field.GetType().GetName()) - else: - return this + "" - - return this + print_val(field_val.get_wrapped_value(), internal_dict) - - if omit_first_field: - field_start_index = 1 - else: - field_start_index = 0 - - body = separator.join([render_child(idx) for idx in range(field_start_index, len(fields))]) - - return template % {"type_name": type_name, - "body": body} - - -def print_pointer_val(val, internal_dict): - """Prints a pointer value with Rust syntax""" - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - sigil = "&" - type_name = val.type.get_unqualified_type_name() - if type_name and type_name[0:1] in ["&", "*"]: - sigil = type_name[0:1] - - return sigil + hex(val.as_integer()) - - -def print_fixed_size_vec_val(val, internal_dict): - assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ARRAY - lldb_val = val.get_wrapped_value() - - output = "[" - - for i in range(lldb_val.num_children): - output += print_val(lldb_val.GetChildAtIndex(i), internal_dict) - if i != lldb_val.num_children - 1: - output += ", " - - output += "]" - return output - - -def print_vec_slice_val(val, internal_dict): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val) - return "&[%s]" % print_array_of_values(val.get_wrapped_value().GetName(), - data_ptr, - length, - internal_dict) - - -def print_std_vec_val(val, internal_dict): - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(val) - return "vec![%s]" % print_array_of_values(val.get_wrapped_value().GetName(), - data_ptr, - length, - internal_dict) - - -def print_str_slice_val(val, internal_dict): - (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val) - return read_utf8_string(data_ptr, length) - - -def print_std_string_val(val, internal_dict): - vec = val.get_child_at_index(0) - (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) - return read_utf8_string(data_ptr, length) - -# =----------------------------------------------------------------------- -# Helper Functions -# =----------------------------------------------------------------------- - - -def print_array_of_values(array_name, data_ptr_val, length, internal_dict): - """Prints a contiguous memory range, interpreting it as values of the - pointee-type of data_ptr_val.""" - - data_ptr_type = data_ptr_val.type - assert data_ptr_type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR - - element_type = data_ptr_type.get_wrapped_value().GetPointeeType() - element_type_size = element_type.GetByteSize() - - start_address = data_ptr_val.as_integer() - raw_value = data_ptr_val.get_wrapped_value() - - def render_element(i): - address = start_address + i * element_type_size - element_val = raw_value.CreateValueFromAddress(array_name + ("[%s]" % i), - address, - element_type) - return print_val(element_val, internal_dict) - - return ', '.join([render_element(i) for i in range(length)]) - - -def read_utf8_string(ptr_val, byte_count): - if byte_count == 0: - return '""' - error = lldb.SBError() - process = ptr_val.get_wrapped_value().GetProcess() - data = process.ReadMemory(ptr_val.as_integer(), byte_count, error) - if error.Success(): - return '"%s"' % data.decode(encoding='UTF-8') - else: - return '' % error.GetCString() diff --git a/src/etc/rust-lldb b/src/etc/rust-lldb index 7b9b40e6b4a0a..28b32ef1ad532 100755 --- a/src/etc/rust-lldb +++ b/src/etc/rust-lldb @@ -30,13 +30,5 @@ EOF fi fi -# Prepare commands that will be loaded before any file on the command line has been loaded -script_import="command script import \"$RUSTC_SYSROOT/lib/rustlib/etc/lldb_rust_formatters.py\"" -category_definition="type summary add --no-value --python-function lldb_rust_formatters.print_val -x \".*\" --category Rust" -category_enable="type category enable Rust" - # Call LLDB with the commands added to the argument list -exec "$lldb" --one-line-before-file "$script_import" \ - --one-line-before-file "$category_definition" \ - --one-line-before-file "$category_enable" \ - "$@" +exec "$lldb" --source-before-file ./lldb_commands "$@" diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py new file mode 100644 index 0000000000000..b49fd19ed4cbb --- /dev/null +++ b/src/etc/rust_types.py @@ -0,0 +1,113 @@ +import re + + +class RustType(object): + OTHER = "Other" + STRUCT = "Struct" + TUPLE = "Tuple" + CSTYLE_VARIANT = "CStyleVariant" + TUPLE_VARIANT = "TupleVariant" + STRUCT_VARIANT = "StructVariant" + ENUM = "Enum" + EMPTY = "Empty" + SINGLETON_ENUM = "SingletonEnum" + REGULAR_ENUM = "RegularEnum" + COMPRESSED_ENUM = "CompressedEnum" + REGULAR_UNION = "RegularUnion" + + STD_STRING = "StdString" + STD_OS_STRING = "StdOsString" + STD_STR = "StdStr" + STD_SLICE = "StdSlice" + STD_VEC = "StdVec" + STD_VEC_DEQUE = "StdVecDeque" + STD_BTREE_SET = "StdBTreeSet" + STD_BTREE_MAP = "StdBTreeMap" + STD_HASH_MAP = "StdHashMap" + STD_HASH_SET = "StdHashSet" + STD_RC = "StdRc" + STD_ARC = "StdArc" + STD_CELL = "StdCell" + STD_REF = "StdRef" + STD_REF_MUT = "StdRefMut" + STD_REF_CELL = "StdRefCell" + + +STD_STRING_REGEX = re.compile(r"^(alloc::(\w+::)+)String$") +STD_STR_REGEX = re.compile(r"^&str$") +STD_SLICE_REGEX = re.compile(r"^&\[.+\]$") +STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::(\w+::)+)OsString$") +STD_VEC_REGEX = re.compile(r"^(alloc::(\w+::)+)Vec<.+>$") +STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::(\w+::)+)VecDeque<.+>$") +STD_BTREE_SET_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeSet<.+>$") +STD_BTREE_MAP_REGEX = re.compile(r"^(alloc::(\w+::)+)BTreeMap<.+>$") +STD_HASH_MAP_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashMap<.+>$") +STD_HASH_SET_REGEX = re.compile(r"^(std::collections::(\w+::)+)HashSet<.+>$") +STD_RC_REGEX = re.compile(r"^(alloc::(\w+::)+)Rc<.+>$") +STD_ARC_REGEX = re.compile(r"^(alloc::(\w+::)+)Arc<.+>$") +STD_CELL_REGEX = re.compile(r"^(core::(\w+::)+)Cell<.+>$") +STD_REF_REGEX = re.compile(r"^(core::(\w+::)+)Ref<.+>$") +STD_REF_MUT_REGEX = re.compile(r"^(core::(\w+::)+)RefMut<.+>$") +STD_REF_CELL_REGEX = re.compile(r"^(core::(\w+::)+)RefCell<.+>$") + +TUPLE_ITEM_REGEX = re.compile(r"__\d+$") + +ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" +ENUM_DISR_FIELD_NAME = "<>" + +STD_TYPE_TO_REGEX = { + RustType.STD_STRING: STD_STRING_REGEX, + RustType.STD_OS_STRING: STD_OS_STRING_REGEX, + RustType.STD_STR: STD_STR_REGEX, + RustType.STD_SLICE: STD_SLICE_REGEX, + RustType.STD_VEC: STD_VEC_REGEX, + RustType.STD_VEC_DEQUE: STD_VEC_DEQUE_REGEX, + RustType.STD_HASH_MAP: STD_HASH_MAP_REGEX, + RustType.STD_HASH_SET: STD_HASH_SET_REGEX, + RustType.STD_BTREE_SET: STD_BTREE_SET_REGEX, + RustType.STD_BTREE_MAP: STD_BTREE_MAP_REGEX, + RustType.STD_RC: STD_RC_REGEX, + RustType.STD_ARC: STD_ARC_REGEX, + RustType.STD_REF: STD_REF_REGEX, + RustType.STD_REF_MUT: STD_REF_MUT_REGEX, + RustType.STD_REF_CELL: STD_REF_CELL_REGEX, + RustType.STD_CELL: STD_CELL_REGEX, +} + +def is_tuple_fields(fields): + # type: (list) -> bool + return all(TUPLE_ITEM_REGEX.match(str(field.name)) for field in fields) + + +def classify_struct(name, fields): + if len(fields) == 0: + return RustType.EMPTY + + for ty, regex in STD_TYPE_TO_REGEX.items(): + if regex.match(name): + return ty + + if fields[0].name == ENUM_DISR_FIELD_NAME: + return RustType.ENUM + + if is_tuple_fields(fields): + return RustType.TUPLE + + return RustType.STRUCT + + +def classify_union(fields): + if len(fields) == 0: + return RustType.EMPTY + + first_variant_name = fields[0].name + if first_variant_name is None: + if len(fields) == 1: + return RustType.SINGLETON_ENUM + else: + return RustType.REGULAR_ENUM + elif first_variant_name.startswith(ENCODED_ENUM_PREFIX): + assert len(fields) == 1 + return RustType.COMPRESSED_ENUM + else: + return RustType.REGULAR_UNION diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 925bc7d3c024e..0327a9f9a96e5 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -2034,7 +2034,7 @@ trait RcBoxPtr { // The reference count will never be zero when this is called; // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. - if strong == 0 || strong == usize::max_value() { + if strong == 0 || strong == usize::MAX { abort(); } self.inner().strong.set(strong + 1); @@ -2058,7 +2058,7 @@ trait RcBoxPtr { // The reference count will never be zero when this is called; // nevertheless, we insert an abort here to hint LLVM at // an otherwise missed optimization. - if weak == 0 || weak == usize::max_value() { + if weak == 0 || weak == usize::MAX { abort(); } self.inner().weak.set(weak + 1); diff --git a/src/liballoc/rc/tests.rs b/src/liballoc/rc/tests.rs index 56788bb56d550..e88385faf4fd4 100644 --- a/src/liballoc/rc/tests.rs +++ b/src/liballoc/rc/tests.rs @@ -407,14 +407,14 @@ fn test_from_vec() { fn test_downcast() { use std::any::Any; - let r1: Rc = Rc::new(i32::max_value()); + let r1: Rc = Rc::new(i32::MAX); let r2: Rc = Rc::new("abc"); assert!(r1.clone().downcast::().is_err()); let r1i32 = r1.downcast::(); assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Rc::new(i32::max_value())); + assert_eq!(r1i32.unwrap(), Rc::new(i32::MAX)); assert!(r2.clone().downcast::().is_err()); diff --git a/src/liballoc/sync/tests.rs b/src/liballoc/sync/tests.rs index a2bb651e2b778..6f08cd7f123be 100644 --- a/src/liballoc/sync/tests.rs +++ b/src/liballoc/sync/tests.rs @@ -465,14 +465,14 @@ fn test_from_vec() { fn test_downcast() { use std::any::Any; - let r1: Arc = Arc::new(i32::max_value()); + let r1: Arc = Arc::new(i32::MAX); let r2: Arc = Arc::new("abc"); assert!(r1.clone().downcast::().is_err()); let r1i32 = r1.downcast::(); assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); + assert_eq!(r1i32.unwrap(), Arc::new(i32::MAX)); assert!(r2.clone().downcast::().is_err()); diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index b703df6f3cb7d..eee98d4534042 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -566,13 +566,13 @@ mod slice_index { data: "hello"; // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - bad: data[0..=usize::max_value()]; + bad: data[0..=usize::MAX]; message: "maximum usize"; } in mod rangetoinclusive { data: "hello"; - bad: data[..=usize::max_value()]; + bad: data[..=usize::MAX]; message: "maximum usize"; } } diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index b73fd95ab6a86..a9813a8704f30 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -68,7 +68,7 @@ fn test_reserve() { #[test] fn test_zst_capacity() { - assert_eq!(Vec::<()>::new().capacity(), usize::max_value()); + assert_eq!(Vec::<()>::new().capacity(), usize::MAX); } #[test] @@ -563,19 +563,19 @@ fn test_drain_inclusive_range() { #[test] fn test_drain_max_vec_size() { - let mut v = Vec::<()>::with_capacity(usize::max_value()); + let mut v = Vec::<()>::with_capacity(usize::MAX); unsafe { - v.set_len(usize::max_value()); + v.set_len(usize::MAX); } - for _ in v.drain(usize::max_value() - 1..) {} - assert_eq!(v.len(), usize::max_value() - 1); + for _ in v.drain(usize::MAX - 1..) {} + assert_eq!(v.len(), usize::MAX - 1); - let mut v = Vec::<()>::with_capacity(usize::max_value()); + let mut v = Vec::<()>::with_capacity(usize::MAX); unsafe { - v.set_len(usize::max_value()); + v.set_len(usize::MAX); } - for _ in v.drain(usize::max_value() - 1..=usize::max_value() - 1) {} - assert_eq!(v.len(), usize::max_value() - 1); + for _ in v.drain(usize::MAX - 1..=usize::MAX - 1) {} + assert_eq!(v.len(), usize::MAX - 1); } #[test] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2226737757bc5..06462fd96d9a9 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2779,19 +2779,25 @@ impl<'a, T> Drain<'a, T> { /// # Examples /// /// ``` - /// # #![feature(vec_drain_as_slice)] /// let mut vec = vec!['a', 'b', 'c']; /// let mut drain = vec.drain(..); /// assert_eq!(drain.as_slice(), &['a', 'b', 'c']); /// let _ = drain.next().unwrap(); /// assert_eq!(drain.as_slice(), &['b', 'c']); /// ``` - #[unstable(feature = "vec_drain_as_slice", reason = "recently added", issue = "58957")] + #[stable(feature = "vec_drain_as_slice", since = "1.46.0")] pub fn as_slice(&self) -> &[T] { self.iter.as_slice() } } +#[stable(feature = "vec_drain_as_slice", since = "1.46.0")] +impl<'a, T> AsRef<[T]> for Drain<'a, T> { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + #[stable(feature = "drain", since = "1.6.0")] unsafe impl Sync for Drain<'_, T> {} #[stable(feature = "drain", since = "1.6.0")] diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index c4c1d2824b098..c4293ed7bcfe2 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -778,18 +778,13 @@ impl RefCell { /// /// An example of panic: /// - /// ``` + /// ```should_panic /// use std::cell::RefCell; - /// use std::thread; - /// - /// let result = thread::spawn(move || { - /// let c = RefCell::new(5); - /// let m = c.borrow_mut(); /// - /// let b = c.borrow(); // this causes a panic - /// }).join(); + /// let c = RefCell::new(5); /// - /// assert!(result.is_err()); + /// let m = c.borrow_mut(); + /// let b = c.borrow(); // this causes a panic /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -858,18 +853,13 @@ impl RefCell { /// /// An example of panic: /// - /// ``` + /// ```should_panic /// use std::cell::RefCell; - /// use std::thread; - /// - /// let result = thread::spawn(move || { - /// let c = RefCell::new(5); - /// let m = c.borrow(); /// - /// let b = c.borrow_mut(); // this causes a panic - /// }).join(); + /// let c = RefCell::new(5); + /// let m = c.borrow(); /// - /// assert!(result.is_err()); + /// let b = c.borrow_mut(); // this causes a panic /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1163,8 +1153,8 @@ impl<'b> BorrowRef<'b> { // Incrementing borrow can result in a non-reading value (<= 0) in these cases: // 1. It was < 0, i.e. there are writing borrows, so we can't allow a read borrow // due to Rust's reference aliasing rules - // 2. It was isize::max_value() (the max amount of reading borrows) and it overflowed - // into isize::min_value() (the max amount of writing borrows) so we can't allow + // 2. It was isize::MAX (the max amount of reading borrows) and it overflowed + // into isize::MIN (the max amount of writing borrows) so we can't allow // an additional read borrow because isize can't represent so many read borrows // (this can only happen if you mem::forget more than a small constant amount of // `Ref`s, which is not good practice) @@ -1172,7 +1162,7 @@ impl<'b> BorrowRef<'b> { } else { // Incrementing borrow can result in a reading value (> 0) in these cases: // 1. It was = 0, i.e. it wasn't borrowed, and we are taking the first read borrow - // 2. It was > 0 and < isize::max_value(), i.e. there were read borrows, and isize + // 2. It was > 0 and < isize::MAX, i.e. there were read borrows, and isize // is large enough to represent having one more read borrow borrow.set(b); Some(BorrowRef { borrow }) @@ -1198,7 +1188,7 @@ impl Clone for BorrowRef<'_> { debug_assert!(is_reading(borrow)); // Prevent the borrow counter from overflowing into // a writing borrow. - assert!(borrow != isize::max_value()); + assert!(borrow != isize::MAX); self.borrow.set(borrow + 1); BorrowRef { borrow: self.borrow } } @@ -1489,7 +1479,7 @@ impl<'b> BorrowRefMut<'b> { let borrow = self.borrow.get(); debug_assert!(is_writing(borrow)); // Prevent the borrow counter from underflowing. - assert!(borrow != isize::min_value()); + assert!(borrow != isize::MIN); self.borrow.set(borrow - 1); BorrowRefMut { borrow: self.borrow } } diff --git a/src/libcore/char/convert.rs b/src/libcore/char/convert.rs index 87c56c4b0a105..d7e39946148ed 100644 --- a/src/libcore/char/convert.rs +++ b/src/libcore/char/convert.rs @@ -278,16 +278,11 @@ impl fmt::Display for CharTryFromError { /// /// Passing a large radix, causing a panic: /// -/// ``` -/// use std::thread; +/// ```should_panic /// use std::char; /// -/// let result = thread::spawn(|| { -/// // this panics -/// let c = char::from_digit(1, 37); -/// }).join(); -/// -/// assert!(result.is_err()); +/// // this panics +/// let c = char::from_digit(1, 37); /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index bf09b28ff693e..dd2f01c679f73 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -229,16 +229,11 @@ impl char { /// /// Passing a large radix, causing a panic: /// - /// ``` - /// use std::thread; + /// ```should_panic /// use std::char; /// - /// let result = thread::spawn(|| { - /// // this panics - /// let c = char::from_digit(1, 37); - /// }).join(); - /// - /// assert!(result.is_err()); + /// // this panics + /// char::from_digit(1, 37); /// ``` #[unstable(feature = "assoc_char_funcs", reason = "recently added", issue = "71763")] #[inline] @@ -282,15 +277,9 @@ impl char { /// /// Passing a large radix, causing a panic: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// // this panics - /// '1'.is_digit(37); - /// }).join(); - /// - /// assert!(result.is_err()); + /// ```should_panic + /// // this panics + /// '1'.is_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -337,14 +326,9 @@ impl char { /// /// Passing a large radix, causing a panic: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// '1'.to_digit(37); - /// }).join(); - /// - /// assert!(result.is_err()); + /// ```should_panic + /// // this panics + /// '1'.to_digit(37); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -646,17 +630,11 @@ impl char { /// /// A buffer that's too small: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// let mut b = [0; 1]; - /// - /// // this panics - /// 'ß'.encode_utf8(&mut b); - /// }).join(); + /// ```should_panic + /// let mut b = [0; 1]; /// - /// assert!(result.is_err()); + /// // this panics + /// 'ß'.encode_utf8(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] @@ -687,17 +665,11 @@ impl char { /// /// A buffer that's too small: /// - /// ``` - /// use std::thread; - /// - /// let result = thread::spawn(|| { - /// let mut b = [0; 1]; - /// - /// // this panics - /// '𝕊'.encode_utf16(&mut b); - /// }).join(); + /// ```should_panic + /// let mut b = [0; 1]; /// - /// assert!(result.is_err()); + /// // this panics + /// '𝕊'.encode_utf16(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] diff --git a/src/libcore/convert/num.rs b/src/libcore/convert/num.rs index 6dd0522f7f610..5ff52a9a11b5a 100644 --- a/src/libcore/convert/num.rs +++ b/src/libcore/convert/num.rs @@ -217,7 +217,7 @@ macro_rules! try_from_upper_bounded { /// is outside of the range of the target type. #[inline] fn try_from(u: $source) -> Result { - if u > (Self::max_value() as $source) { + if u > (Self::MAX as $source) { Err(TryFromIntError(())) } else { Ok(u as Self) @@ -239,8 +239,8 @@ macro_rules! try_from_both_bounded { /// is outside of the range of the target type. #[inline] fn try_from(u: $source) -> Result { - let min = Self::min_value() as $source; - let max = Self::max_value() as $source; + let min = Self::MIN as $source; + let max = Self::MAX as $source; if u < min || u > max { Err(TryFromIntError(())) } else { diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs index 6f6009b47e672..9dbc23f5c04c5 100644 --- a/src/libcore/future/mod.rs +++ b/src/libcore/future/mod.rs @@ -56,6 +56,7 @@ pub const fn from_generator(gen: T) -> impl Future where T: Generator, { + #[rustc_diagnostic_item = "gen_future"] struct GenFuture>(T); // We rely on the fact that async/await futures are immovable in order to create diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index a10b34d931d10..fbfcdc3c1a9ea 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -2717,12 +2717,12 @@ pub trait Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// let v_cloned: Vec<_> = a.iter().copied().collect(); + /// let v_copied: Vec<_> = a.iter().copied().collect(); /// /// // copied is the same as .map(|&x| x) /// let v_map: Vec<_> = a.iter().map(|&x| x).collect(); /// - /// assert_eq!(v_cloned, vec![1, 2, 3]); + /// assert_eq!(v_copied, vec![1, 2, 3]); /// assert_eq!(v_map, vec![1, 2, 3]); /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 7d21f9a9a66d0..fe05e914e6d44 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -145,7 +145,6 @@ #![feature(associated_type_bounds)] #![feature(const_type_id)] #![feature(const_caller_location)] -#![feature(option_zip)] #![feature(no_niche)] // rust-lang/rust#68303 #[prelude_import] diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index c164e893b4fbf..b1317bc2121f6 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -750,9 +750,9 @@ $EndFeature, " } doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs, assuming overflow + concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::max_value()` or `self + rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -792,9 +792,9 @@ $EndFeature, " } doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs, assuming overflow + concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::max_value()` or `self - rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -834,9 +834,9 @@ $EndFeature, " } doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs, assuming overflow + concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::max_value()` or `self * rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -871,7 +871,7 @@ $EndFeature, " without modifying the original"] #[inline] pub const fn checked_div(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -900,7 +900,7 @@ assert_eq!((1", stringify!($SelfT), ").checked_div_euclid(0), None); without modifying the original"] #[inline] pub const fn checked_div_euclid(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { Some(self.div_euclid(rhs)) @@ -929,7 +929,7 @@ $EndFeature, " without modifying the original"] #[inline] pub const fn checked_rem(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { // SAFETY: div by zero and by INT_MIN have been checked above @@ -957,7 +957,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.checked_rem_euclid(-1), None); without modifying the original"] #[inline] pub const fn checked_rem_euclid(self, rhs: Self) -> Option { - if rhs == 0 || (self == Self::min_value() && rhs == -1) { + if rhs == 0 || (self == Self::MIN && rhs == -1) { None } else { Some(self.rem_euclid(rhs)) @@ -1236,9 +1236,9 @@ $EndFeature, " match self.checked_mul(rhs) { Some(x) => x, None => if (self < 0) == (rhs < 0) { - Self::max_value() + Self::MAX } else { - Self::min_value() + Self::MIN } } } @@ -1267,8 +1267,8 @@ $EndFeature, " pub const fn saturating_pow(self, exp: u32) -> Self { match self.checked_pow(exp) { Some(x) => x, - None if self < 0 && exp % 2 == 1 => Self::min_value(), - None => Self::max_value(), + None if self < 0 && exp % 2 == 1 => Self::MIN, + None => Self::MAX, } } } @@ -1738,7 +1738,7 @@ $EndFeature, " #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (self, true) } else { (self / rhs, false) @@ -1771,7 +1771,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_div_euclid(-1), (", stringi #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_div_euclid(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (self, true) } else { (self.div_euclid(rhs), false) @@ -1805,7 +1805,7 @@ $EndFeature, " #[must_use = "this returns the result of the operation, \ without modifying the original"] pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (0, true) } else { (self % rhs, false) @@ -1838,7 +1838,7 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_rem_euclid(-1), (0, true)); without modifying the original"] #[inline] pub const fn overflowing_rem_euclid(self, rhs: Self) -> (Self, bool) { - if self == Self::min_value() && rhs == -1 { + if self == Self::MIN && rhs == -1 { (0, true) } else { (self.rem_euclid(rhs), false) @@ -1869,8 +1869,8 @@ assert_eq!(", stringify!($SelfT), "::MIN.overflowing_neg(), (", stringify!($Self #[allow(unused_attributes)] #[allow_internal_unstable(const_if_match)] pub const fn overflowing_neg(self) -> (Self, bool) { - if self == Self::min_value() { - (Self::min_value(), true) + if self == Self::MIN { + (Self::MIN, true) } else { (-self, false) } @@ -1952,7 +1952,7 @@ $EndFeature, " #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] #[inline] pub const fn overflowing_abs(self) -> (Self, bool) { - (self.wrapping_abs(), self == Self::min_value()) + (self.wrapping_abs(), self == Self::MIN) } } @@ -2986,9 +2986,9 @@ assert_eq!((", stringify!($SelfT), "::MAX - 2).checked_add(3), None);", $EndFeat } doc_comment! { - concat!("Unchecked integer addition. Computes `self + rhs, assuming overflow + concat!("Unchecked integer addition. Computes `self + rhs`, assuming overflow cannot occur. This results in undefined behavior when `self + rhs > ", stringify!($SelfT), -"::max_value()` or `self + rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self + rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -3026,9 +3026,9 @@ assert_eq!(0", stringify!($SelfT), ".checked_sub(1), None);", $EndFeature, " } doc_comment! { - concat!("Unchecked integer subtraction. Computes `self - rhs, assuming overflow + concat!("Unchecked integer subtraction. Computes `self - rhs`, assuming overflow cannot occur. This results in undefined behavior when `self - rhs > ", stringify!($SelfT), -"::max_value()` or `self - rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self - rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -3066,9 +3066,9 @@ assert_eq!(", stringify!($SelfT), "::MAX.checked_mul(2), None);", $EndFeature, " } doc_comment! { - concat!("Unchecked integer multiplication. Computes `self * rhs, assuming overflow + concat!("Unchecked integer multiplication. Computes `self * rhs`, assuming overflow cannot occur. This results in undefined behavior when `self * rhs > ", stringify!($SelfT), -"::max_value()` or `self * rhs < ", stringify!($SelfT), "::min_value()`."), +"::MAX` or `self * rhs < ", stringify!($SelfT), "::MIN`."), #[unstable( feature = "unchecked_math", reason = "niche optimization path", @@ -3309,7 +3309,8 @@ Basic usage: ``` ", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101); -assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, " +assert_eq!(", stringify!($SelfT), "::MAX.saturating_add(127), ", stringify!($SelfT), "::MAX);", +$EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] @@ -3366,7 +3367,7 @@ assert_eq!((", stringify!($SelfT), "::MAX).saturating_mul(10), ", stringify!($Se pub const fn saturating_mul(self, rhs: Self) -> Self { match self.checked_mul(rhs) { Some(x) => x, - None => Self::max_value(), + None => Self::MAX, } } } @@ -3393,7 +3394,7 @@ $EndFeature, " pub const fn saturating_pow(self, exp: u32) -> Self { match self.checked_pow(exp) { Some(x) => x, - None => Self::max_value(), + None => Self::MAX, } } } @@ -4080,7 +4081,7 @@ Basic usage: } } - doc_comment! { + doc_comment! { concat!("Performs Euclidean division. Since, for the positive integers, all common @@ -4178,7 +4179,7 @@ assert!(!10", stringify!($SelfT), ".is_power_of_two());", $EndFeature, " // (such as intel pre-haswell) have more efficient ctlz // intrinsics when the argument is non-zero. let z = unsafe { intrinsics::ctlz_nonzero(p) }; - <$SelfT>::max_value() >> z + <$SelfT>::MAX >> z } doc_comment! { @@ -5160,9 +5161,9 @@ trait FromStrRadixHelper: PartialOrd + Copy { macro_rules! doit { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { #[inline] - fn min_value() -> Self { Self::min_value() } + fn min_value() -> Self { Self::MIN } #[inline] - fn max_value() -> Self { Self::max_value() } + fn max_value() -> Self { Self::MAX } #[inline] fn from_u32(u: u32) -> Self { u as Self } #[inline] diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index bb648ba8c25de..f6acb8f8b9a92 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -694,7 +694,7 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -let n = Wrapping(", stringify!($t), "::max_value()) >> 2; +let n = Wrapping(", stringify!($t), "::MAX) >> 2; assert_eq!(n.leading_zeros(), 3); ```"), @@ -723,8 +723,7 @@ use std::num::Wrapping; assert_eq!(Wrapping(100", stringify!($t), ").abs(), Wrapping(100)); assert_eq!(Wrapping(-100", stringify!($t), ").abs(), Wrapping(100)); -assert_eq!(Wrapping(", stringify!($t), "::min_value()).abs(), Wrapping(", stringify!($t), -"::min_value())); +assert_eq!(Wrapping(", stringify!($t), "::MIN).abs(), Wrapping(", stringify!($t), "::MIN)); assert_eq!(Wrapping(-128i8).abs().0 as u8, 128u8); ```"), #[inline] @@ -823,7 +822,7 @@ Basic usage: #![feature(wrapping_int_impl)] use std::num::Wrapping; -let n = Wrapping(", stringify!($t), "::max_value()) >> 2; +let n = Wrapping(", stringify!($t), "::MAX) >> 2; assert_eq!(n.leading_zeros(), 2); ```"), diff --git a/src/libcore/option.rs b/src/libcore/option.rs index e8483875c97e5..5f0a12678ff43 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -926,7 +926,6 @@ impl Option { /// # Examples /// /// ``` - /// #![feature(option_zip)] /// let x = Some(1); /// let y = Some("hi"); /// let z = None::; @@ -934,9 +933,12 @@ impl Option { /// assert_eq!(x.zip(y), Some((1, "hi"))); /// assert_eq!(x.zip(z), None); /// ``` - #[unstable(feature = "option_zip", issue = "70086")] + #[stable(feature = "option_zip_option", since = "1.46.0")] pub fn zip(self, other: Option) -> Option<(T, U)> { - self.zip_with(other, |a, b| (a, b)) + match (self, other) { + (Some(a), Some(b)) => Some((a, b)), + _ => None, + } } /// Zips `self` and another `Option` with function `f`. diff --git a/src/libcore/ptr/const_ptr.rs b/src/libcore/ptr/const_ptr.rs index 835183d171a79..e39d18d7733a2 100644 --- a/src/libcore/ptr/const_ptr.rs +++ b/src/libcore/ptr/const_ptr.rs @@ -291,7 +291,7 @@ impl *const T { T: Sized, { let pointee_size = mem::size_of::(); - assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); + assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); intrinsics::ptr_offset_from(self, origin) } @@ -336,7 +336,7 @@ impl *const T { T: Sized, { let pointee_size = mem::size_of::(); - assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize); + assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); let d = isize::wrapping_sub(self as _, origin as _); d.wrapping_div(pointee_size as _) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index ecc70adda4111..1be05d5effff3 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -1128,7 +1128,7 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { // // Note, that we use wrapping operations here intentionally – the original formula // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod - // usize::max_value()` instead, because we take the result `mod n` at the end + // usize::MAX` instead, because we take the result `mod n` at the end // anyway. inverse = inverse.wrapping_mul(2usize.wrapping_sub(x.wrapping_mul(inverse))); if going_mod >= m { @@ -1193,7 +1193,7 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { } // Cannot be aligned at all. - usize::max_value() + usize::MAX } /// Compares raw pointers for equality. @@ -1345,14 +1345,24 @@ macro_rules! fnptr_impls_safety_abi { #[stable(feature = "fnptr_impls", since = "1.4.0")] impl fmt::Pointer for $FnTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(*self as *const ()), f) + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) } } #[stable(feature = "fnptr_impls", since = "1.4.0")] impl fmt::Debug for $FnTy { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Pointer::fmt(&(*self as *const ()), f) + // HACK: The intermediate cast as usize is required for AVR + // so that the address space of the source function pointer + // is preserved in the final function pointer. + // + // https://github.com/avr-rust/rust/issues/143 + fmt::Pointer::fmt(&(*self as usize as *const ()), f) } } } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 4efb1db7a1a68..21ba2b5abcfb6 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -3043,16 +3043,12 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::max_value() { - None - } else { - (*self.start()..self.end() + 1).get(slice) - } + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get_mut(slice) @@ -3071,7 +3067,7 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index(self, slice: &[T]) -> &[T] { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { slice_index_overflow_fail(); } (*self.start()..self.end() + 1).index(slice) @@ -3079,7 +3075,7 @@ impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn index_mut(self, slice: &mut [T]) -> &mut [T] { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { slice_index_overflow_fail(); } (*self.start()..self.end() + 1).index_mut(slice) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 316c2cd55acea..6c4b28499a60b 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1651,7 +1651,7 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // Ascii case, try to skip forward quickly. // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. - if align != usize::max_value() && align.wrapping_sub(index) % usize_bytes == 0 { + if align != usize::MAX && align.wrapping_sub(index) % usize_bytes == 0 { let ptr = v.as_ptr(); while index < blocks_end { // SAFETY: since `align - index` and `ascii_block_size` are @@ -2083,7 +2083,7 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) @@ -2091,7 +2091,7 @@ mod traits { } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get_mut(slice) @@ -2107,14 +2107,14 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { str_index_overflow_fail(); } (*self.start()..self.end() + 1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if *self.end() == usize::max_value() { + if *self.end() == usize::MAX { str_index_overflow_fail(); } (*self.start()..self.end() + 1).index_mut(slice) @@ -2140,11 +2140,11 @@ mod traits { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end == usize::max_value() { None } else { (..self.end + 1).get(slice) } + if self.end == usize::MAX { None } else { (..self.end + 1).get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end == usize::max_value() { None } else { (..self.end + 1).get_mut(slice) } + if self.end == usize::MAX { None } else { (..self.end + 1).get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: &str) -> &Self::Output { @@ -2156,14 +2156,14 @@ mod traits { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if self.end == usize::max_value() { + if self.end == usize::MAX { str_index_overflow_fail(); } (..self.end + 1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if self.end == usize::max_value() { + if self.end == usize::MAX { str_index_overflow_fail(); } (..self.end + 1).index_mut(slice) diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index 1a2b612b2f95c..14f1f293d40d6 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -60,6 +60,43 @@ use crate::slice::memchr; /// The trait itself acts as a builder for an associated /// `Searcher` type, which does the actual work of finding /// occurrences of the pattern in a string. +/// +/// Depending on the type of the pattern, the behaviour of methods like +/// [`str::find`] and [`str::contains`] can change. The table below describes +/// some of those behaviours. +/// +/// | Pattern type | Match condition | +/// |--------------------------|-------------------------------------------| +/// | `&str` | is substring | +/// | `char` | is contained in string | +/// | `&[char]` | any char in slice is contained in string | +/// | `F: FnMut(char) -> bool` | `F` returns `true` for a char in string | +/// | `&&str` | is substring | +/// | `&String` | is substring | +/// +/// # Examples +/// ``` +/// // &str +/// assert_eq!("abaaa".find("ba"), Some(1)); +/// assert_eq!("abaaa".find("bac"), None); +/// +/// // char +/// assert_eq!("abaaa".find('a'), Some(0)); +/// assert_eq!("abaaa".find('b'), Some(1)); +/// assert_eq!("abaaa".find('c'), None); +/// +/// // &[char] +/// assert_eq!("ab".find(&['b', 'a'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['a', 'z'][..]), Some(0)); +/// assert_eq!("abaaa".find(&['c', 'd'][..]), None); +/// +/// // FnMut(char) -> bool +/// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); +/// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); +/// ``` +/// +/// [`str::find`]: ../../../std/primitive.str.html#method.find +/// [`str::contains`]: ../../../std/primitive.str.html#method.contains pub trait Pattern<'a>: Sized { /// Associated searcher for this pattern type Searcher: Searcher<'a>; @@ -80,6 +117,15 @@ pub trait Pattern<'a>: Sized { matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) } + /// Checks whether the pattern matches at the back of the haystack + #[inline] + fn is_suffix_of(self, haystack: &'a str) -> bool + where + Self::Searcher: ReverseSearcher<'a>, + { + matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) + } + /// Removes the pattern from the front of haystack, if it matches. #[inline] fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { @@ -96,15 +142,6 @@ pub trait Pattern<'a>: Sized { } } - /// Checks whether the pattern matches at the back of the haystack - #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool - where - Self::Searcher: ReverseSearcher<'a>, - { - matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) - } - /// Removes the pattern from the back of haystack, if it matches. #[inline] fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 477cb24d6be67..1cd68f2881b7c 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -2623,15 +2623,7 @@ unsafe fn atomic_umin(dst: *mut T, val: T, order: Ordering) -> T { /// [`Relaxed`]: enum.Ordering.html#variant.Relaxed #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(target_arch = "wasm32", allow(unused_variables))] pub fn fence(order: Ordering) { - // On wasm32 it looks like fences aren't implemented in LLVM yet in that - // they will cause LLVM to abort. The wasm instruction set doesn't have - // fences right now. There's discussion online about the best way for tools - // to conventionally implement fences at - // https://github.com/WebAssembly/tool-conventions/issues/59. We should - // follow that discussion and implement a solution when one comes about! - #[cfg(not(target_arch = "wasm32"))] // SAFETY: using an atomic fence is safe. unsafe { match order { diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index 181bbb8e18784..939f1325c8499 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -140,8 +140,8 @@ macro_rules! test_impl_from { ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] fn $fn_name() { - let small_max = <$Small>::max_value(); - let small_min = <$Small>::min_value(); + let small_max = <$Small>::MAX; + let small_min = <$Small>::MIN; let large_max: $Large = small_max.into(); let large_min: $Large = small_min.into(); assert_eq!(large_max as $Small, small_max); @@ -248,8 +248,8 @@ macro_rules! test_impl_try_from_always_ok { ($fn_name:ident, $source:ty, $target: ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target); assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target); @@ -361,8 +361,8 @@ macro_rules! test_impl_try_from_signed_to_unsigned_upper_ok { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; let neg_one: $source = -1; assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), max as $target); @@ -426,8 +426,8 @@ macro_rules! test_impl_try_from_unsigned_to_signed_upper_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), min as $target); @@ -487,11 +487,11 @@ macro_rules! test_impl_try_from_same_sign_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; - let t_max = <$target>::max_value(); - let t_min = <$target>::min_value(); + let t_max = <$target>::MAX; + let t_min = <$target>::MIN; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); if min != 0 { assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); @@ -576,11 +576,11 @@ macro_rules! test_impl_try_from_signed_to_unsigned_err { ($fn_name:ident, $source:ty, $target:ty) => { #[test] fn $fn_name() { - let max = <$source>::max_value(); - let min = <$source>::min_value(); + let max = <$source>::MAX; + let min = <$source>::MIN; let zero: $source = 0; - let t_max = <$target>::max_value(); - let t_min = <$target>::min_value(); + let t_max = <$target>::MAX; + let t_min = <$target>::MIN; assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), zero as $target); diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs index a008b3319f39a..9fea34d668fcc 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -357,7 +357,7 @@ fn align_offset_weird_strides() { unsafe fn test_weird_stride(ptr: *const T, align: usize) -> bool { let numptr = ptr as usize; - let mut expected = usize::max_value(); + let mut expected = usize::MAX; // Naive but definitely correct way to find the *first* aligned element of stride::. for el in 0..align { if (numptr + el * ::std::mem::size_of::()) % align == 0 { diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 54a585415bce2..cd46117f76322 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1691,8 +1691,8 @@ fn test_copy_within_panics_src_inverted() { #[should_panic(expected = "attempted to index slice up to maximum usize")] fn test_copy_within_panics_src_out_of_bounds() { let mut bytes = *b"Hello, World!"; - // an inclusive range ending at usize::max_value() would make src_end overflow - bytes.copy_within(usize::max_value()..=usize::max_value(), 0); + // an inclusive range ending at usize::MAX would make src_end overflow + bytes.copy_within(usize::MAX..=usize::MAX, 0); } #[test] diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 09a069ab3136d..ba3adc4a135cb 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -133,9 +133,9 @@ impl Neg for Round { pub type ExpInt = i16; // \c ilogb error results. -pub const IEK_INF: ExpInt = ExpInt::max_value(); -pub const IEK_NAN: ExpInt = ExpInt::min_value(); -pub const IEK_ZERO: ExpInt = ExpInt::min_value() + 1; +pub const IEK_INF: ExpInt = ExpInt::MAX; +pub const IEK_NAN: ExpInt = ExpInt::MIN; +pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct ParseError(pub &'static str); diff --git a/src/librustc_apfloat/tests/ieee.rs b/src/librustc_apfloat/tests/ieee.rs index e5b06cf225d16..2d8bb7d1e8e03 100644 --- a/src/librustc_apfloat/tests/ieee.rs +++ b/src/librustc_apfloat/tests/ieee.rs @@ -2997,8 +2997,8 @@ fn scalbn() { assert!(smallest_f64.scalbn(2099).is_infinite()); // Test for integer overflows when adding to exponent. - assert!(smallest_f64.scalbn(-ExpInt::max_value()).is_pos_zero()); - assert!(largest_f64.scalbn(ExpInt::max_value()).is_infinite()); + assert!(smallest_f64.scalbn(-ExpInt::MAX).is_pos_zero()); + assert!(largest_f64.scalbn(ExpInt::MAX).is_infinite()); assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),)); assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),)); diff --git a/src/librustc_arena/lib.rs b/src/librustc_arena/lib.rs index 4da336f8e288d..4a2a0de0e211f 100644 --- a/src/librustc_arena/lib.rs +++ b/src/librustc_arena/lib.rs @@ -602,7 +602,7 @@ macro_rules! which_arena_for_type { #[macro_export] macro_rules! declare_arena { - ([], [$($a:tt $name:ident: $ty:ty,)*], $tcx:lifetime) => { + ([], [$($a:tt $name:ident: $ty:ty, $gen_ty:ty;)*], $tcx:lifetime) => { #[derive(Default)] pub struct Arena<$tcx> { pub dropless: $crate::DroplessArena, @@ -611,17 +611,17 @@ macro_rules! declare_arena { } #[marker] - pub trait ArenaAllocatable {} + pub trait ArenaAllocatable<'tcx> {} - impl ArenaAllocatable for T {} + impl<'tcx, T: Copy> ArenaAllocatable<'tcx> for T {} - unsafe trait ArenaField<'tcx>: Sized { + unsafe trait ArenaField<'tcx>: Sized + ArenaAllocatable<'tcx> { /// Returns a specific arena to allocate from. /// If `None` is returned, the `DropArena` will be used. fn arena<'a>(arena: &'a Arena<'tcx>) -> Option<&'a $crate::TypedArena>; } - unsafe impl<'tcx, T> ArenaField<'tcx> for T { + unsafe impl<'tcx, T: ArenaAllocatable<'tcx>> ArenaField<'tcx> for T { #[inline] default fn arena<'a>(_: &'a Arena<'tcx>) -> Option<&'a $crate::TypedArena> { panic!() @@ -630,18 +630,27 @@ macro_rules! declare_arena { $( #[allow(unused_lifetimes)] - impl<$tcx> ArenaAllocatable for $ty {} - unsafe impl<$tcx> ArenaField<$tcx> for $ty { + impl<$tcx> ArenaAllocatable<$tcx> for $ty {} + unsafe impl<$tcx, '_x, '_y, '_z, '_w> ArenaField<$tcx> for $gen_ty where Self: ArenaAllocatable<$tcx> { #[inline] fn arena<'a>(_arena: &'a Arena<$tcx>) -> Option<&'a $crate::TypedArena> { - $crate::which_arena_for_type!($a[&_arena.$name]) + // SAFETY: We only implement `ArenaAllocatable<$tcx>` for + // `$ty`, so `$ty` and Self are the same type + unsafe { + ::std::mem::transmute::< + Option<&'a $crate::TypedArena<$ty>>, + Option<&'a $crate::TypedArena>, + >( + $crate::which_arena_for_type!($a[&_arena.$name]) + ) + } } } )* impl<'tcx> Arena<'tcx> { #[inline] - pub fn alloc(&self, value: T) -> &mut T { + pub fn alloc>(&self, value: T) -> &mut T { if !::std::mem::needs_drop::() { return self.dropless.alloc(value); } @@ -659,7 +668,7 @@ macro_rules! declare_arena { self.dropless.alloc_slice(value) } - pub fn alloc_from_iter<'a, T: ArenaAllocatable>( + pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx>>( &'a self, iter: impl ::std::iter::IntoIterator, ) -> &'a mut [T] { diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index efcf95ec706b8..62406552e318f 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -362,7 +362,11 @@ impl Default for Generics { fn default() -> Generics { Generics { params: Vec::new(), - where_clause: WhereClause { predicates: Vec::new(), span: DUMMY_SP }, + where_clause: WhereClause { + has_where_token: false, + predicates: Vec::new(), + span: DUMMY_SP, + }, span: DUMMY_SP, } } @@ -371,6 +375,11 @@ impl Default for Generics { /// A where-clause in a definition. #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] pub struct WhereClause { + /// `true` if we ate a `where` token: this can happen + /// if we parsed no predicates (e.g. `struct Foo where {} + /// This allows us to accurately pretty-print + /// in `nt_to_tokenstream` + pub has_where_token: bool, pub predicates: Vec, pub span: Span, } @@ -1165,7 +1174,9 @@ pub enum ExprKind { /// and the remaining elements are the rest of the arguments. /// Thus, `x.foo::(a, b, c, d)` is represented as /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`. - MethodCall(PathSegment, Vec>), + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + MethodCall(PathSegment, Vec>, Span), /// A tuple (e.g., `(a, b, c, d)`). Tup(Vec>), /// A binary operation (e.g., `a + b`, `a * b`). @@ -1849,15 +1860,6 @@ impl TyKind { pub fn is_unit(&self) -> bool { if let TyKind::Tup(ref tys) = *self { tys.is_empty() } else { false } } - - /// HACK(type_alias_impl_trait, Centril): A temporary crutch used - /// in lowering to avoid making larger changes there and beyond. - pub fn opaque_top_hack(&self) -> Option<&GenericBounds> { - match self { - Self::ImplTrait(_, bounds) => Some(bounds), - _ => None, - } - } } /// Syntax used to declare a trait object. diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs index 7ececb814a6a3..2ffef9d48c181 100644 --- a/src/librustc_ast/mut_visit.rs +++ b/src/librustc_ast/mut_visit.rs @@ -786,7 +786,7 @@ pub fn noop_visit_generics(generics: &mut Generics, vis: &mut T) } pub fn noop_visit_where_clause(wc: &mut WhereClause, vis: &mut T) { - let WhereClause { predicates, span } = wc; + let WhereClause { has_where_token: _, predicates, span } = wc; visit_vec(predicates, |predicate| vis.visit_where_predicate(predicate)); vis.visit_span(span); } @@ -1111,11 +1111,12 @@ pub fn noop_visit_expr( vis.visit_expr(f); visit_exprs(args, vis); } - ExprKind::MethodCall(PathSegment { ident, id, args }, exprs) => { + ExprKind::MethodCall(PathSegment { ident, id, args }, exprs, span) => { vis.visit_ident(ident); vis.visit_id(id); visit_opt(args, |args| vis.visit_generic_args(args)); visit_exprs(exprs, vis); + vis.visit_span(span); } ExprKind::Binary(_binop, lhs, rhs) => { vis.visit_expr(lhs); diff --git a/src/librustc_ast/tokenstream.rs b/src/librustc_ast/tokenstream.rs index 075aaa7e5bc01..15ae12ebf10e3 100644 --- a/src/librustc_ast/tokenstream.rs +++ b/src/librustc_ast/tokenstream.rs @@ -392,7 +392,7 @@ impl TokenStream { break; } } - token_trees = out.into_iter().map(|t| TokenTree::Token(t)).collect(); + token_trees = out.into_iter().map(TokenTree::Token).collect(); if token_trees.len() != 1 { debug!("break_tokens: broke {:?} to {:?}", tree, token_trees); } diff --git a/src/librustc_ast/util/parser.rs b/src/librustc_ast/util/parser.rs index b98cc96b3c647..d8b44a22f2c92 100644 --- a/src/librustc_ast/util/parser.rs +++ b/src/librustc_ast/util/parser.rs @@ -394,7 +394,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { contains_exterior_struct_lit(&x) } - ast::ExprKind::MethodCall(.., ref exprs) => { + ast::ExprKind::MethodCall(.., ref exprs, _) => { // X { y: 1 }.bar(...) contains_exterior_struct_lit(&exprs[0]) } diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs index 41c02734442a4..ccab46703dffe 100644 --- a/src/librustc_ast/visit.rs +++ b/src/librustc_ast/visit.rs @@ -726,7 +726,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(callee_expression); walk_list!(visitor, visit_expr, arguments); } - ExprKind::MethodCall(ref segment, ref arguments) => { + ExprKind::MethodCall(ref segment, ref arguments, _span) => { visitor.visit_path_segment(expression.span, segment); walk_list!(visitor, visit_expr, arguments); } diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs index c9037da377ebb..e59cacfffc926 100644 --- a/src/librustc_ast_lowering/expr.rs +++ b/src/librustc_ast_lowering/expr.rs @@ -9,7 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; +use rustc_span::source_map::{respan, DesugaringKind, ForLoopLoc, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_target::asm; use std::collections::hash_map::Entry; @@ -25,6 +25,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { + let mut span = e.span; ensure_sufficient_stack(|| { let kind = match e.kind { ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), @@ -39,7 +40,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let f = self.lower_expr(f); hir::ExprKind::Call(f, self.lower_exprs(args)) } - ExprKind::MethodCall(ref seg, ref args) => { + ExprKind::MethodCall(ref seg, ref args, span) => { let hir_seg = self.arena.alloc(self.lower_path_segment( e.span, seg, @@ -50,9 +51,10 @@ impl<'hir> LoweringContext<'_, 'hir> { None, )); let args = self.lower_exprs(args); - hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args) + hir::ExprKind::MethodCall(hir_seg, seg.ident.span, args, span) } ExprKind::Binary(binop, ref lhs, ref rhs) => { + span = self.mark_span_with_reason(DesugaringKind::Operator, e.span, None); let binop = self.lower_binop(binop); let lhs = self.lower_expr(lhs); let rhs = self.lower_expr(rhs); @@ -222,7 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::Expr { hir_id: self.lower_node_id(e.id), kind, - span: e.span, + span, attrs: e.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), } }) @@ -237,6 +239,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_binop(&mut self, b: BinOp) -> hir::BinOp { + let span = self.mark_span_with_reason(DesugaringKind::Operator, b.span, None); Spanned { node: match b.node { BinOpKind::Add => hir::BinOpKind::Add, @@ -258,7 +261,7 @@ impl<'hir> LoweringContext<'_, 'hir> { BinOpKind::Ge => hir::BinOpKind::Ge, BinOpKind::Gt => hir::BinOpKind::Gt, }, - span: b.span, + span, } } @@ -1237,10 +1240,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ) => { assert!(!*late); let out_op_sp = if input { op_sp2 } else { op_sp }; - let msg = &format!( - "use `lateout` instead of \ - `out` to avoid conflict" - ); + let msg = "use `lateout` instead of \ + `out` to avoid conflict"; err.span_help(out_op_sp, msg); } _ => {} @@ -1362,9 +1363,14 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Block, opt_label: Option