# The Rust Code of Conduct
-A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
-
-## Conduct
-
-**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
-
-* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
-* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
-* Please be kind and courteous. There's no need to be mean or rude.
-* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
-* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
-* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
-* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
-* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
-
-## Moderation
-
-
-These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team].
-
-1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
-2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
-3. Moderators will first respond to such remarks with a warning.
-4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
-5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
-6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
-7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
-8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
-
-In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
-
-And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
-
-The enforcement policies listed above apply to all official Rust venues; including all communication channels (Rust Discord server, Rust Zulip server); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
-
-*Adapted from the [Node.js Policy on Trolling](https://blog.izs.me/2012/08/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
-
-[mod_team]: https://www.rust-lang.org/team.html#Moderation-team
+The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).
"winapi 0.3.6",
]
+[[package]]
+name = "autocfg"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
+
[[package]]
name = "backtrace"
version = "0.3.37"
"crossbeam-utils 0.6.5",
]
+[[package]]
+name = "crossbeam-deque"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
+dependencies = [
+ "crossbeam-epoch 0.7.2",
+ "crossbeam-utils 0.6.5",
+]
+
[[package]]
name = "crossbeam-epoch"
version = "0.3.1"
"proc-macro2 0.4.30",
"quote 0.6.12",
"syn 0.15.35",
- "synstructure",
+ "synstructure 0.10.2",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df044dd42cdb7e32f28557b661406fc0f2494be75199779998810dbc35030e0d"
dependencies = [
- "hashbrown",
+ "hashbrown 0.5.0",
"lazy_static 1.3.0",
"log",
"pest",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353"
dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6587d09be37fb98a11cb08b9000a3f592451c1b1b613ca69d949160e313a430a"
+dependencies = [
+ "autocfg",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
- "serde",
]
[[package]]
[[package]]
name = "rayon"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4"
+checksum = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123"
dependencies = [
- "crossbeam-deque 0.6.3",
+ "crossbeam-deque 0.7.1",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
-version = "1.5.0"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2"
+checksum = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b"
dependencies = [
- "crossbeam-deque 0.6.3",
+ "crossbeam-deque 0.7.1",
"crossbeam-queue",
"crossbeam-utils 0.6.5",
"lazy_static 1.3.0",
"num_cpus",
"parking_lot 0.9.0",
"polonius-engine",
- "rustc-rayon",
- "rustc-rayon-core",
+ "rustc-rayon 0.3.0",
+ "rustc-rayon-core 0.3.0",
"rustc_apfloat",
"rustc_data_structures",
"rustc_errors",
"rustc-ap-graphviz",
"rustc-ap-serialize",
"rustc-hash",
- "rustc-rayon",
- "rustc-rayon-core",
+ "rustc-rayon 0.2.0",
+ "rustc-rayon-core 0.2.0",
"smallvec",
"stable_deref_trait",
]
"proc-macro2 0.4.30",
"quote 0.6.12",
"syn 0.15.35",
- "synstructure",
+ "synstructure 0.10.2",
]
[[package]]
dependencies = [
"crossbeam-deque 0.2.0",
"either",
- "rustc-rayon-core",
+ "rustc-rayon-core 0.2.0",
+]
+
+[[package]]
+name = "rustc-rayon"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f32767f90d938f1b7199a174ef249ae1924f6e5bbdb9d112fea141e016f25b3a"
+dependencies = [
+ "crossbeam-deque 0.7.1",
+ "either",
+ "rustc-rayon-core 0.3.0",
]
[[package]]
"num_cpus",
]
+[[package]]
+name = "rustc-rayon-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2427831f0053ea3ea73559c8eabd893133a51b251d142bacee53c62a288cb3"
+dependencies = [
+ "crossbeam-deque 0.7.1",
+ "crossbeam-queue",
+ "crossbeam-utils 0.6.5",
+ "lazy_static 1.3.0",
+ "num_cpus",
+]
+
[[package]]
name = "rustc-serialize"
version = "0.3.24"
"log",
"parking_lot 0.9.0",
"rustc-hash",
- "rustc-rayon",
- "rustc-rayon-core",
+ "rustc-rayon 0.3.0",
+ "rustc-rayon-core 0.3.0",
"rustc_index",
"serialize",
"smallvec",
"log",
"once_cell",
"rustc",
- "rustc-rayon",
+ "rustc-rayon 0.3.0",
"rustc_codegen_ssa",
"rustc_codegen_utils",
"rustc_data_structures",
version = "0.1.0"
dependencies = [
"itertools 0.8.0",
- "proc-macro2 0.4.30",
- "quote 0.6.12",
- "syn 0.15.35",
- "synstructure",
+ "proc-macro2 1.0.3",
+ "quote 1.0.2",
+ "syn 1.0.5",
+ "synstructure 0.12.1",
]
[[package]]
"rustc",
"rustc_data_structures",
"rustc_errors",
+ "rustc_index",
+ "rustc_target",
"syntax",
"syntax_pos",
]
dependencies = [
"minifier",
"pulldown-cmark 0.5.3",
- "rustc-rayon",
+ "rustc-rayon 0.3.0",
"tempfile",
]
"core",
"dlmalloc",
"fortanix-sgx-abi",
- "hashbrown",
+ "hashbrown 0.6.1",
"libc",
"panic_abort",
"panic_unwind",
"unicode-xid 0.1.0",
]
+[[package]]
+name = "synstructure"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203"
+dependencies = [
+ "proc-macro2 1.0.3",
+ "quote 1.0.2",
+ "syn 1.0.5",
+ "unicode-xid 0.2.0",
+]
+
[[package]]
name = "syntax"
version = "0.0.0"
# but you can also optionally enable the "emscripten" backend for asm.js or
# make this an empty array (but that probably won't get too far in the
# bootstrap)
-# FIXME: remove the obsolete emscripten backend option.
#codegen-backends = ["llvm"]
# This is the name of the directory in which codegen backends will get installed
let mut rustflags = Rustflags::new(&target);
if stage != 0 {
+ if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
+ cargo.args(s.split_whitespace());
+ }
rustflags.env("RUSTFLAGS_NOT_BOOTSTRAP");
} else {
+ if let Ok(s) = env::var("CARGOFLAGS_BOOTSTRAP") {
+ cargo.args(s.split_whitespace());
+ }
rustflags.env("RUSTFLAGS_BOOTSTRAP");
rustflags.arg("--cfg=bootstrap");
}
+ if let Ok(s) = env::var("CARGOFLAGS") {
+ cargo.args(s.split_whitespace());
+ }
+
match mode {
Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {},
Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
Some("-Wl,-rpath,@loader_path/../lib")
} else if !target.contains("windows") &&
!target.contains("wasm32") &&
- !target.contains("emscripten") &&
!target.contains("fuchsia") {
Some("-Wl,-rpath,$ORIGIN/../lib")
} else {
// Also provide `rust_test_helpers` for the host.
builder.ensure(native::TestHelpers { target: compiler.host });
- // As well as the target, except for plain wasm32, which can't build it
- if !target.contains("wasm32") || target.contains("emscripten") {
+ // wasm32 can't build the test helpers
+ if !target.contains("wasm32") {
builder.ensure(native::TestHelpers { target });
}
-
builder.ensure(RemoteCopyLibs { compiler, target });
let mut cmd = builder.tool_cmd(Tool::Compiletest);
cmake \
sudo \
gdb \
- xz-utils \
- bzip2
+ xz-utils
COPY scripts/emscripten.sh /scripts/
RUN bash /scripts/emscripten.sh
RUN sh /scripts/sccache.sh
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 PATH=$PATH:/emsdk-portable/clang/e1.38.15_64bit/
+ENV PATH=$PATH:/emsdk-portable/emscripten/1.38.15/
+ENV PATH=$PATH:/emsdk-portable/node/8.9.1_64bit/bin/
+ENV EMSCRIPTEN=/emsdk-portable/emscripten/1.38.15/
+ENV BINARYEN_ROOT=/emsdk-portable/clang/e1.38.15_64bit/binaryen/
ENV EM_CONFIG=/emsdk-portable/.emscripten
ENV TARGETS=asmjs-unknown-emscripten
-ENV SCRIPT python2.7 ../x.py test --target $TARGETS
+ENV RUST_CONFIGURE_ARGS --enable-emscripten --disable-optimize-tests
-# This is almost identical to the wasm32-unknown-emscripten target, so
-# running with assertions again is not useful
-ENV NO_DEBUG_ASSERTIONS=1
+ENV SCRIPT python2.7 ../x.py test --target $TARGETS \
+ src/test/ui \
+ src/test/run-fail \
+ src/libstd \
+ src/liballoc \
+ src/libcore
+
+# Debug assertions in rustc are largely covered by other builders, and LLVM
+# assertions cause this builder to slow down by quite a large amount and don't
+# buy us a huge amount over other builders (not sure if we've ever seen an
+# asmjs-specific backend assertion trip), so disable assertions for these
+# tests.
ENV NO_LLVM_ASSERTIONS=1
+ENV NO_DEBUG_ASSERTIONS=1
--- /dev/null
+FROM ubuntu:16.04
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ make \
+ file \
+ curl \
+ ca-certificates \
+ python \
+ git \
+ cmake \
+ sudo \
+ gdb \
+ xz-utils \
+ jq \
+ bzip2
+
+# emscripten
+COPY scripts/emscripten-wasm.sh /scripts/
+COPY wasm32-exp/node.sh /usr/local/bin/node
+RUN bash /scripts/emscripten-wasm.sh
+
+# cache
+COPY scripts/sccache.sh /scripts/
+RUN sh /scripts/sccache.sh
+
+# env
+ENV PATH=/wasm-install/emscripten:/wasm-install/bin:$PATH
+ENV EM_CONFIG=/root/.emscripten
+
+ENV TARGETS=wasm32-experimental-emscripten
+
+ENV RUST_CONFIGURE_ARGS --experimental-targets=WebAssembly
+
+ENV SCRIPT python2.7 ../x.py test --target $TARGETS
--- /dev/null
+#!/usr/bin/env bash
+
+path="$(dirname $1)"
+file="$(basename $1)"
+
+shift
+
+cd "$path"
+exec /node-v8.0.0-linux-x64/bin/node "$file" "$@"
cmake \
sudo \
gdb \
- xz-utils \
- bzip2
+ xz-utils
+# emscripten
COPY scripts/emscripten.sh /scripts/
RUN bash /scripts/emscripten.sh
RUN sh /scripts/sccache.sh
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 PATH=$PATH:/emsdk-portable/clang/e1.38.15_64bit/
+ENV PATH=$PATH:/emsdk-portable/emscripten/1.38.15/
+ENV PATH=$PATH:/emsdk-portable/node/8.9.1_64bit/bin/
+ENV EMSCRIPTEN=/emsdk-portable/emscripten/1.38.15/
+ENV BINARYEN_ROOT=/emsdk-portable/clang/e1.38.15_64bit/binaryen/
ENV EM_CONFIG=/emsdk-portable/.emscripten
ENV TARGETS=wasm32-unknown-emscripten
-
-# FIXME: Re-enable these tests once Cargo stops trying to execute wasms
-ENV SCRIPT python2.7 ../x.py test --target $TARGETS \
- --exclude src/libcore \
- --exclude src/liballoc \
- --exclude src/libproc_macro \
- --exclude src/libstd \
- --exclude src/libterm \
- --exclude src/libtest
+ENV SCRIPT python2.7 ../x.py test --target $TARGETS
--- /dev/null
+set -ex
+
+hide_output() {
+ set +x
+ on_err="
+echo ERROR: An error was encountered with the build.
+cat /tmp/build.log
+exit 1
+"
+ trap "$on_err" ERR
+ bash -c "while true; do sleep 30; echo \$(date) - building ...; done" &
+ PING_LOOP_PID=$!
+ $@ &> /tmp/build.log
+ trap - ERR
+ kill $PING_LOOP_PID
+ rm -f /tmp/build.log
+ set -x
+}
+
+# Download last known good emscripten from WebAssembly waterfall
+BUILD=$(curl -fL https://storage.googleapis.com/wasm-llvm/builds/linux/lkgr.json | \
+ jq '.build | tonumber')
+curl -sL https://storage.googleapis.com/wasm-llvm/builds/linux/$BUILD/wasm-binaries.tbz2 | \
+ hide_output tar xvkj
+
+# node 8 is required to run wasm
+cd /
+curl -sL https://nodejs.org/dist/v8.0.0/node-v8.0.0-linux-x64.tar.xz | \
+ tar -xJ
+
+# Make emscripten use wasm-ready node and LLVM tools
+echo "EMSCRIPTEN_ROOT = '/wasm-install/emscripten'" >> /root/.emscripten
+echo "NODE_JS='/usr/local/bin/node'" >> /root/.emscripten
+echo "LLVM_ROOT='/wasm-install/bin'" >> /root/.emscripten
+echo "BINARYEN_ROOT = '/wasm-install'" >> /root/.emscripten
+echo "COMPILER_ENGINE = NODE_JS" >> /root/.emscripten
+echo "JS_ENGINES = [NODE_JS]" >> /root/.emscripten
set -x
}
-git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable
+cd /
+curl -fL https://mozilla-games.s3.amazonaws.com/emscripten/releases/emsdk-portable.tar.gz | \
+ tar -xz
+
cd /emsdk-portable
-hide_output ./emsdk install 1.38.46-upstream
-./emsdk activate 1.38.46-upstream
+./emsdk update
+hide_output ./emsdk install sdk-1.38.15-64bit
+./emsdk activate sdk-1.38.15-64bit
# Compile and cache libc
source ./emsdk_env.sh
echo "main(){}" > a.c
HOME=/emsdk-portable/ emcc a.c
+HOME=/emsdk-portable/ emcc -s BINARYEN=1 a.c
rm -f a.*
# Make emsdk usable by any user
-Subproject commit 320d232b206edecb67489316f71a14e31dbc6c08
+Subproject commit 5b9d2fcefadfc32fceafacfc0dd9441d9b57dd94
--- /dev/null
+# `track_caller`
+
+The tracking issue for this feature is: [#47809](https://github.com/rust-lang/rust/issues/47809).
+
+------------------------
fn nth(&mut self, n: usize) -> Option<I::Item> {
(**self).nth(n)
}
+ fn last(self) -> Option<I::Item> {
+ BoxIter::last(self)
+ }
+}
+
+trait BoxIter {
+ type Item;
+ fn last(self) -> Option<Self::Item>;
+}
+
+impl<I: Iterator + ?Sized> BoxIter for Box<I> {
+ type Item = I::Item;
+ default fn last(self) -> Option<I::Item> {
+ #[inline]
+ fn some<T>(_: Option<T>, x: T) -> Option<T> {
+ Some(x)
+ }
+
+ self.fold(None, some)
+ }
}
+/// Specialization for sized `I`s that uses `I`s implementation of `last()`
+/// instead of the default.
#[stable(feature = "rust1", since = "1.0.0")]
-impl<I: Iterator + Sized> Iterator for Box<I> {
- fn last(self) -> Option<I::Item> where I: Sized {
+impl<I: Iterator> BoxIter for Box<I> {
+ fn last(self) -> Option<I::Item> {
(*self).last()
}
}
+use std::cmp;
use std::collections::BinaryHeap;
use std::collections::binary_heap::{Drain, PeekMut};
+use std::panic::{self, AssertUnwindSafe};
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+use rand::{thread_rng, seq::SliceRandom};
#[test]
fn test_iterator() {
// even if the order may not be correct.
//
// Destructors must be called exactly once per element.
-// FIXME: re-enable emscripten once it can unwind again
#[test]
-#[cfg(not(any(miri, target_os = "emscripten")))] // Miri does not support catching panics
+#[cfg(not(miri))] // Miri does not support catching panics
fn panic_safe() {
- use std::cmp;
- use std::panic::{self, AssertUnwindSafe};
- use std::sync::atomic::{AtomicUsize, Ordering};
- use rand::{thread_rng, seq::SliceRandom};
-
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
#[derive(Eq, PartialEq, Ord, Clone, Debug)]
}
#[test]
- #[cfg(not(target_os = "emscripten"))] // hits an OOM
+ #[cfg(not(target_arch = "asmjs"))] // hits an OOM
#[cfg(not(miri))] // Miri is too slow
fn simple_big() {
fn a_million_letter_x() -> String {
+++ /dev/null
-diff a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs (rejected hunks)
-@@ -483,7 +483,7 @@ mod slice_index {
- }
-
- #[test]
-- #[cfg(not(target_arch = "asmjs"))] // hits an OOM
-+ #[cfg(not(target_arch = "js"))] // hits an OOM
- #[cfg(not(miri))] // Miri is too slow
- fn simple_big() {
- fn a_million_letter_x() -> String {
}
}
-// Miri does not support catching panics
-// FIXME: re-enable emscripten once it can unwind again
#[test]
-#[cfg(not(any(miri, target_os = "emscripten")))]
+#[cfg(not(miri))] // Miri does not support catching panics
fn drain_filter_consumed_panic() {
use std::rc::Rc;
use std::sync::Mutex;
}
}
-// FIXME: Re-enable emscripten once it can catch panics
#[test]
-#[cfg(not(any(miri, target_os = "emscripten")))] // Miri does not support catching panics
+#[cfg(not(miri))] // Miri does not support catching panics
fn drain_filter_unconsumed_panic() {
use std::rc::Rc;
use std::sync::Mutex;
value: UnsafeCell<T>,
}
-impl<T:Copy> Cell<T> {
- /// Returns a copy of the contained value.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::cell::Cell;
- ///
- /// let c = Cell::new(5);
- ///
- /// let five = c.get();
- /// ```
- #[inline]
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn get(&self) -> T {
- unsafe{ *self.value.get() }
- }
-
- /// Updates the contained value using a function and returns the new value.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(cell_update)]
- ///
- /// use std::cell::Cell;
- ///
- /// let c = Cell::new(5);
- /// let new = c.update(|x| x + 1);
- ///
- /// assert_eq!(new, 6);
- /// assert_eq!(c.get(), 6);
- /// ```
- #[inline]
- #[unstable(feature = "cell_update", issue = "50186")]
- pub fn update<F>(&self, f: F) -> T
- where
- F: FnOnce(T) -> T,
- {
- let old = self.get();
- let new = f(old);
- self.set(new);
- new
- }
-}
-
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized> Send for Cell<T> where T: Send {}
}
}
+impl<T:Copy> Cell<T> {
+ /// Returns a copy of the contained value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::cell::Cell;
+ ///
+ /// let c = Cell::new(5);
+ ///
+ /// let five = c.get();
+ /// ```
+ #[inline]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn get(&self) -> T {
+ unsafe{ *self.value.get() }
+ }
+
+ /// Updates the contained value using a function and returns the new value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(cell_update)]
+ ///
+ /// use std::cell::Cell;
+ ///
+ /// let c = Cell::new(5);
+ /// let new = c.update(|x| x + 1);
+ ///
+ /// assert_eq!(new, 6);
+ /// assert_eq!(c.get(), 6);
+ /// ```
+ #[inline]
+ #[unstable(feature = "cell_update", issue = "50186")]
+ pub fn update<F>(&self, f: F) -> T
+ where
+ F: FnOnce(T) -> T,
+ {
+ let old = self.get();
+ let new = f(old);
+ self.set(new);
+ new
+ }
+}
+
impl<T: ?Sized> Cell<T> {
/// Returns a raw pointer to the underlying data in this cell.
///
/// Basic implementation of a `va_list`.
// The name is WIP, using `VaListImpl` for now.
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
- not(target_arch = "x86_64")),
+ not(target_arch = "x86_64"), not(target_arch = "asmjs")),
all(target_arch = "aarch64", target_os = "ios"),
- target_arch = "wasm32",
- target_arch = "asmjs",
windows))]
#[repr(transparent)]
#[unstable(feature = "c_variadic",
}
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
- not(target_arch = "x86_64")),
+ not(target_arch = "x86_64"), not(target_arch = "asmjs")),
all(target_arch = "aarch64", target_os = "ios"),
- target_arch = "wasm32",
- target_arch = "asmjs",
windows))]
#[unstable(feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
_marker: PhantomData<&'f mut &'f c_void>,
}
+/// asm.js ABI implementation of a `va_list`.
+// asm.js uses the PNaCl ABI, which specifies that a `va_list` is
+// an array of 4 32-bit integers, according to the old PNaCl docs at
+// https://web.archive.org/web/20130518054430/https://www.chromium.org/nativeclient/pnacl/bitcode-abi#TOC-Derived-Types
+// and clang does the same in `CreatePNaClABIBuiltinVaListDecl` from `lib/AST/ASTContext.cpp`
+#[cfg(all(target_arch = "asmjs", not(windows)))]
+#[repr(C)]
+#[unstable(feature = "c_variadic",
+ reason = "the `c_variadic` feature has not been properly tested on \
+ all supported platforms",
+ issue = "44930")]
+#[lang = "va_list"]
+pub struct VaListImpl<'f> {
+ inner: [crate::mem::MaybeUninit<i32>; 4],
+ _marker: PhantomData<&'f mut &'f c_void>,
+}
+
+#[cfg(all(target_arch = "asmjs", not(windows)))]
+#[unstable(feature = "c_variadic",
+ reason = "the `c_variadic` feature has not been properly tested on \
+ all supported platforms",
+ issue = "44930")]
+impl<'f> fmt::Debug for VaListImpl<'f> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ unsafe {
+ write!(f, "va_list* [{:#x}, {:#x}, {:#x}, {:#x}]",
+ self.inner[0].read(), self.inner[1].read(),
+ self.inner[2].read(), self.inner[3].read())
+ }
+ }
+}
+
/// A wrapper for a `va_list`
#[repr(transparent)]
#[derive(Debug)]
issue = "44930")]
pub struct VaList<'a, 'f: 'a> {
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
- not(target_arch = "x86_64")),
+ not(target_arch = "x86_64"), not(target_arch = "asmjs")),
all(target_arch = "aarch64", target_os = "ios"),
- target_arch = "wasm32",
- target_arch = "asmjs",
windows))]
inner: VaListImpl<'f>,
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
- target_arch = "x86_64"),
+ target_arch = "x86_64", target_arch = "asmjs"),
any(not(target_arch = "aarch64"), not(target_os = "ios")),
- not(target_arch = "wasm32"),
- not(target_arch = "asmjs"),
not(windows)))]
inner: &'a mut VaListImpl<'f>,
}
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
- not(target_arch = "x86_64")),
+ not(target_arch = "x86_64"), not(target_arch = "asmjs")),
all(target_arch = "aarch64", target_os = "ios"),
- target_arch = "wasm32",
- target_arch = "asmjs",
windows))]
#[unstable(feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
}
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc",
- target_arch = "x86_64"),
+ target_arch = "x86_64", target_arch = "asmjs"),
any(not(target_arch = "aarch64"), not(target_os = "ios")),
- not(target_arch = "wasm32"),
- not(target_arch = "asmjs"),
not(windows)))]
#[unstable(feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
// this. LLVM's intepretation of inline assembly is that it's, well, a black
// box. This isn't the greatest implementation since it probably deoptimizes
// more than we want, but it's so far good enough.
+ #[cfg(not(any(
+ target_arch = "asmjs",
+ all(
+ target_arch = "wasm32",
+ target_os = "emscripten"
+ )
+ )))]
unsafe {
asm!("" : : "r"(&dummy));
return dummy;
}
+
+ // Not all platforms support inline assembly so try to do something without
+ // inline assembly which in theory still hinders at least some optimizations
+ // on those targets. This is the "best effort" scenario.
+ unsafe {
+ let ret = crate::ptr::read_volatile(&dummy);
+ crate::mem::forget(dummy);
+ ret
+ }
}
});
}
-/// Indicates unfinished code.
+/// Indicates unfinished code by panicking with a message of "not yet implemented".
///
-/// This can be useful if you are prototyping and are just looking to have your
-/// code type-check, or if you're implementing a trait that requires multiple
-/// methods, and you're only planning on using one of them.
+/// This allows the your code to type-check, which is useful if you are prototyping or
+/// implementing a trait that requires multiple methods which you don't plan of using all of.
///
/// There is no difference between `unimplemented!` and `todo!` apart from the
/// name.
///
/// # Panics
///
-/// This will always [panic!](macro.panic.html)
+/// This will always [panic!](macro.panic.html) because `unimplemented!` is just a
+/// shorthand for `panic!` with a fixed, specific message.
+///
+/// Like `panic!`, this macro has a second form for displaying custom values.
///
/// # Examples
///
///
/// ```
/// trait Foo {
-/// fn bar(&self);
+/// fn bar(&self) -> u8;
/// fn baz(&self);
+/// fn qux(&self) -> Result<u64, ()>;
/// }
/// ```
///
-/// We want to implement `Foo` on one of our types, but we also want to work on
-/// just `bar()` first. In order for our code to compile, we need to implement
-/// `baz()`, so we can use `unimplemented!`:
+/// We want to implement `Foo` for 'MyStruct', but so far we only know how to
+/// implement the `bar()` function. `baz()` and `qux()` will still need to be defined
+/// in our implementation of `Foo`, but we can use `unimplemented!` in their definitions
+/// to allow our code to compile.
+///
+/// In the meantime, we want to have our program stop running once these
+/// unimplemented functions are reached.
///
/// ```
/// # trait Foo {
-/// # fn bar(&self);
+/// # fn bar(&self) -> u8;
/// # fn baz(&self);
+/// # fn qux(&self) -> Result<u64, ()>;
/// # }
/// struct MyStruct;
///
/// impl Foo for MyStruct {
-/// fn bar(&self) {
-/// // implementation goes here
+/// fn bar(&self) -> u8 {
+/// 1 + 1
/// }
///
/// fn baz(&self) {
-/// // let's not worry about implementing baz() for now
+/// // We aren't sure how to even start writing baz yet,
+/// // so we have no logic here at all.
+/// // This will display "thread 'main' panicked at 'not yet implemented'".
/// unimplemented!();
/// }
+///
+/// fn qux(&self) -> Result<u64, ()> {
+/// let n = self.bar();
+/// // We have some logic here,
+/// // so we can use unimplemented! to display what we have so far.
+/// // This will display:
+/// // "thread 'main' panicked at 'not yet implemented: we need to divide by 2'".
+/// unimplemented!("we need to divide by {}", n);
+/// }
/// }
///
/// fn main() {
/// let s = MyStruct;
/// s.bar();
-///
-/// // we aren't even using baz() yet, so this is fine.
/// }
/// ```
#[macro_export]
}
}
-#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
impl<T: Deref> Option<T> {
/// Converts from `Option<T>` (or `&Option<T>`) to `Option<&T::Target>`.
///
/// # Examples
///
/// ```
- /// #![feature(inner_deref)]
- ///
/// let x: Option<String> = Some("hey".to_owned());
/// assert_eq!(x.as_deref(), Some("hey"));
///
/// let x: Option<String> = None;
/// assert_eq!(x.as_deref(), None);
/// ```
+ #[stable(feature = "option_deref", since = "1.40.0")]
pub fn as_deref(&self) -> Option<&T::Target> {
self.as_ref().map(|t| t.deref())
}
}
-#[unstable(feature = "inner_deref", reason = "newly added", issue = "50264")]
impl<T: DerefMut> Option<T> {
/// Converts from `Option<T>` (or `&mut Option<T>`) to `Option<&mut T::Target>`.
///
/// # Examples
///
/// ```
- /// #![feature(inner_deref)]
- ///
/// let mut x: Option<String> = Some("hey".to_owned());
/// assert_eq!(x.as_deref_mut().map(|x| {
/// x.make_ascii_uppercase();
/// x
/// }), Some("HEY".to_owned().as_mut_str()));
/// ```
+ #[stable(feature = "option_deref", since = "1.40.0")]
pub fn as_deref_mut(&mut self) -> Option<&mut T::Target> {
self.as_mut().map(|t| t.deref_mut())
}
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
// SAFETY: const sound because we transmute out the length field as a usize (which it must be)
+ #[allow(unused_attributes)]
#[allow_internal_unstable(const_fn_union)]
pub const fn len(&self) -> usize {
unsafe {
#[stable(feature = "rust1", since = "1.0.0")]
#[inline(always)]
// SAFETY: const sound because we transmute two types with the same layout
+ #[allow(unused_attributes)]
#[allow_internal_unstable(const_fn_union)]
pub const fn as_bytes(&self) -> &[u8] {
#[repr(C)]
num_cpus = "1.0"
scoped-tls = "1.0"
log = { version = "0.4", features = ["release_max_level_info", "std"] }
-rustc-rayon = "0.2.0"
-rustc-rayon-core = "0.2.0"
+rustc-rayon = "0.3.0"
+rustc-rayon-core = "0.3.0"
polonius-engine = "0.10.0"
rustc_apfloat = { path = "../librustc_apfloat" }
rustc_target = { path = "../librustc_target" }
use std::collections::hash_map::Entry;
use std::mem;
use crate::ty::{self, TyCtxt};
-use crate::util::common::{ProfileQueriesMsg, profq_msg};
use parking_lot::{Mutex, Condvar};
use crate::ich::{StableHashingContext, StableHashingContextProvider, Fingerprint};
previous_work_products: FxHashMap<WorkProductId, WorkProduct>,
dep_node_debug: Lock<FxHashMap<DepNode, String>>,
-
- // Used for testing, only populated when -Zquery-dep-graph is specified.
- loaded_from_cache: Lock<FxHashMap<DepNodeIndex, bool>>,
}
pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Option<Fingerprint>
emitting_diagnostics_cond_var: Condvar::new(),
previous: prev_graph,
colors: DepNodeColorMap::new(prev_graph_node_count),
- loaded_from_cache: Default::default(),
})),
}
}
// - we can get an idea of the runtime cost.
let mut hcx = cx.get_stable_hashing_context();
- if cfg!(debug_assertions) {
- profq_msg(hcx.sess(), ProfileQueriesMsg::TaskBegin(key.clone()))
- };
-
let result = if no_tcx {
task(cx, arg)
} else {
})
};
- if cfg!(debug_assertions) {
- profq_msg(hcx.sess(), ProfileQueriesMsg::TaskEnd)
- };
-
let current_fingerprint = hash_result(&mut hcx, &result);
let dep_node_index = finish_task_and_alloc_depnode(
// This method will only load queries that will end up in the disk cache.
// Other queries will not be executed.
pub fn exec_cache_promotions(&self, tcx: TyCtxt<'_>) {
+ let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion");
+
let data = self.data.as_ref().unwrap();
for prev_index in data.colors.values.indices() {
match data.colors.get(prev_index) {
}
}
}
-
- pub fn mark_loaded_from_cache(&self, dep_node_index: DepNodeIndex, state: bool) {
- debug!("mark_loaded_from_cache({:?}, {})",
- self.data.as_ref().unwrap().current.borrow().data[dep_node_index].node,
- state);
-
- self.data
- .as_ref()
- .unwrap()
- .loaded_from_cache
- .borrow_mut()
- .insert(dep_node_index, state);
- }
-
- pub fn was_loaded_from_cache(&self, dep_node: &DepNode) -> Option<bool> {
- let data = self.data.as_ref().unwrap();
- let dep_node_index = data.current.borrow().node_to_node_index[dep_node];
- data.loaded_from_cache.borrow().get(&dep_node_index).cloned()
- }
}
/// A "work product" is an intermediate result that we save into the
```
"##,
-// This shouldn't really ever trigger since the repeated value error comes first
-E0136: r##"
-A binary can only have one entry point, and by default that entry point is the
-function `main()`. If there are multiple such functions, please rename one.
-"##,
-
-E0137: r##"
-More than one function was declared with the `#[main]` attribute.
-
-Erroneous code example:
-
-```compile_fail,E0137
-#![feature(main)]
-
-#[main]
-fn foo() {}
-
-#[main]
-fn f() {} // error: multiple functions with a `#[main]` attribute
-```
-
-This error indicates that the compiler found multiple functions with the
-`#[main]` attribute. This is an error because there must be a unique entry
-point into a Rust program. Example:
-
-```
-#![feature(main)]
-
-#[main]
-fn f() {} // ok!
-```
-"##,
-
-E0138: r##"
-More than one function was declared with the `#[start]` attribute.
-
-Erroneous code example:
-
-```compile_fail,E0138
-#![feature(start)]
-
-#[start]
-fn foo(argc: isize, argv: *const *const u8) -> isize {}
-
-#[start]
-fn f(argc: isize, argv: *const *const u8) -> isize {}
-// error: multiple 'start' functions
-```
-
-This error indicates that the compiler found multiple functions with the
-`#[start]` attribute. This is an error because there must be a unique entry
-point into a Rust program. Example:
-
-```
-#![feature(start)]
-
-#[start]
-fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok!
-```
-"##,
-
E0139: r##"
#### Note: this error code is no longer emitted by the compiler.
```
"##,
+E0495: r##"
+A lifetime cannot be determined in the given situation.
+
+Erroneous code example:
+
+```compile_fail,E0495
+fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ match (&t,) { // error!
+ ((u,),) => u,
+ }
+}
+
+let y = Box::new((42,));
+let x = transmute_lifetime(&y);
+```
+
+In this code, you have two ways to solve this issue:
+ 1. Enforce that `'a` lives at least as long as `'b`.
+ 2. Use the same lifetime requirement for both input and output values.
+
+So for the first solution, you can do it by replacing `'a` with `'a: 'b`:
+
+```
+fn transmute_lifetime<'a: 'b, 'b, T>(t: &'a (T,)) -> &'b T {
+ match (&t,) { // ok!
+ ((u,),) => u,
+ }
+}
+```
+
+In the second you can do it by simply removing `'b` so they both use `'a`:
+
+```
+fn transmute_lifetime<'a, T>(t: &'a (T,)) -> &'a T {
+ match (&t,) { // ok!
+ ((u,),) => u,
+ }
+}
+```
+"##,
+
E0496: r##"
-A lifetime name is shadowing another lifetime name. Erroneous code example:
+A lifetime name is shadowing another lifetime name.
+
+Erroneous code example:
```compile_fail,E0496
struct Foo<'a> {
"##,
E0497: r##"
-A stability attribute was used outside of the standard library. Erroneous code
-example:
+#### Note: this error code is no longer emitted by the compiler.
+
+A stability attribute was used outside of the standard library.
+
+Erroneous code example:
```compile_fail
#[stable] // error: stability attributes may not be used outside of the
Also, for now, it is not possible to write deprecation messages either.
"##,
-E0512: r##"
-Transmute with two differently sized types was attempted. Erroneous code
-example:
-
-```compile_fail,E0512
-fn takes_u8(_: u8) {}
-
-fn main() {
- unsafe { takes_u8(::std::mem::transmute(0u16)); }
- // error: cannot transmute between types of different sizes,
- // or dependently-sized types
-}
-```
-
-Please use types with same size or use the expected type directly. Example:
-
-```
-fn takes_u8(_: u8) {}
-
-fn main() {
- unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
- // or:
- unsafe { takes_u8(0u8); } // ok!
-}
-```
-"##,
-
E0517: r##"
This error indicates that a `#[repr(..)]` attribute was placed on an
unsupported item.
https://doc.rust-lang.org/book/ch13-01-closures.html
"##,
+E0566: r##"
+Conflicting representation hints have been used on a same item.
+
+Erroneous code example:
+
+```
+#[repr(u32, u64)] // warning!
+enum Repr { A }
+```
+
+In most cases (if not all), using just one representation hint is more than
+enough. If you want to have a representation hint depending on the current
+architecture, use `cfg_attr`. Example:
+
+```
+#[cfg_attr(linux, repr(u32))]
+#[cfg_attr(not(linux), repr(u64))]
+enum Repr { A }
+```
+"##,
+
E0580: r##"
The `main` function was incorrectly declared.
[RFC 1522]: https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
"##,
-E0591: r##"
-Per [RFC 401][rfc401], if you have a function declaration `foo`:
-
-```
-// For the purposes of this explanation, all of these
-// different kinds of `fn` declarations are equivalent:
-struct S;
-fn foo(x: S) { /* ... */ }
-# #[cfg(for_demonstration_only)]
-extern "C" { fn foo(x: S); }
-# #[cfg(for_demonstration_only)]
-impl S { fn foo(self) { /* ... */ } }
-```
-
-the type of `foo` is **not** `fn(S)`, as one might expect.
-Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`.
-However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`,
-so you rarely notice this:
-
-```
-# struct S;
-# fn foo(_: S) {}
-let x: fn(S) = foo; // OK, coerces
-```
-
-The reason that this matter is that the type `fn(S)` is not specific to
-any particular function: it's a function _pointer_. So calling `x()` results
-in a virtual call, whereas `foo()` is statically dispatched, because the type
-of `foo` tells us precisely what function is being called.
-
-As noted above, coercions mean that most code doesn't have to be
-concerned with this distinction. However, you can tell the difference
-when using **transmute** to convert a fn item into a fn pointer.
-
-This is sometimes done as part of an FFI:
-
-```compile_fail,E0591
-extern "C" fn foo(userdata: Box<i32>) {
- /* ... */
-}
-
-# fn callback(_: extern "C" fn(*mut i32)) {}
-# use std::mem::transmute;
-# unsafe {
-let f: extern "C" fn(*mut i32) = transmute(foo);
-callback(f);
-# }
-```
-
-Here, transmute is being used to convert the types of the fn arguments.
-This pattern is incorrect because, because the type of `foo` is a function
-**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
-is a function pointer, which is not zero-sized.
-This pattern should be rewritten. There are a few possible ways to do this:
-
-- change the original fn declaration to match the expected signature,
- and do the cast in the fn body (the preferred option)
-- cast the fn item fo a fn pointer before calling transmute, as shown here:
-
- ```
- # extern "C" fn foo(_: Box<i32>) {}
- # use std::mem::transmute;
- # unsafe {
- let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
- let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too
- # }
- ```
-
-The same applies to transmutes to `*mut fn()`, which were observed in practice.
-Note though that use of this type is generally incorrect.
-The intention is typically to describe a function pointer, but just `fn()`
-alone suffices for that. `*mut fn()` is a pointer to a fn pointer.
-(Since these values are typically just passed to C code, however, this rarely
-makes a difference in practice.)
-
-[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
-"##,
-
E0593: r##"
You tried to supply an `Fn`-based type with an incorrect number of arguments
than what was expected.
```
"##,
-E0601: r##"
-No `main` function was found in a binary crate. To fix this error, add a
-`main` function. For example:
-
-```
-fn main() {
- // Your program will start here.
- println!("Hello world!");
-}
-```
-
-If you don't know the basics of Rust, you can go look to the Rust Book to get
-started: https://doc.rust-lang.org/book/
-"##,
-
E0602: r##"
An unknown lint was used on the command line.
rejected in your own crates.
"##,
+E0736: r##"
+#[track_caller] and #[naked] cannot be applied to the same function.
+
+Erroneous code example:
+
+```compile_fail,E0736
+#![feature(track_caller)]
+
+#[naked]
+#[track_caller]
+fn foo() {}
+```
+
+This is primarily due to ABI incompatibilities between the two attributes.
+See [RFC 2091] for details on this and other limitations.
+
+[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md
+"##,
+
;
// E0006, // merged with E0005
// E0101, // replaced with E0282
// E0272, // on_unimplemented #0
// E0273, // on_unimplemented #1
// E0274, // on_unimplemented #2
- E0278, // requirement is not satisfied
+// E0278, // requirement is not satisfied
E0279, // requirement is not satisfied
E0280, // requirement is not satisfied
// E0285, // overflow evaluation builtin bounds
E0488, // lifetime of variable does not enclose its declaration
E0489, // type/lifetime parameter not in scope here
E0490, // a value of type `..` is borrowed for too long
- E0495, // cannot infer an appropriate lifetime due to conflicting
- // requirements
- E0566, // conflicting representation hints
E0623, // lifetime mismatch where both parameters are anonymous regions
E0628, // generators cannot have explicit parameters
E0631, // type mismatch in closure arguments
E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax
E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders
E0697, // closures cannot be static
- E0707, // multiple elided lifetimes used in arguments of `async fn`
+// E0707, // multiple elided lifetimes used in arguments of `async fn`
E0708, // `async` non-`move` closures with parameters are not currently
// supported
- E0709, // multiple different lifetimes used in arguments of `async fn`
+// E0709, // multiple different lifetimes used in arguments of `async fn`
E0710, // an unknown tool name found in scoped lint
E0711, // a feature has been declared with conflicting stability attributes
// E0702, // replaced with a generic attribute input check
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
E0727, // `async` generators are not yet supported
E0728, // `await` must be in an `async` function or block
+ E0739, // invalid track_caller application/syntax
}
use crate::ty::query::Providers;
use std::fmt::{self, Display};
-use syntax::symbol::sym;
+use syntax::{attr, symbol::sym};
use syntax_pos::Span;
#[derive(Copy, Clone, PartialEq)]
self.check_marker(attr, item, target)
} else if attr.check_name(sym::target_feature) {
self.check_target_feature(attr, item, target)
+ } else if attr.check_name(sym::track_caller) {
+ self.check_track_caller(attr, &item, target)
} else {
true
};
}
}
+ /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
+ fn check_track_caller(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) -> bool {
+ if target != Target::Fn {
+ struct_span_err!(
+ self.tcx.sess,
+ attr.span,
+ E0739,
+ "attribute should be applied to function"
+ )
+ .span_label(item.span, "not a function")
+ .emit();
+ false
+ } else if attr::contains_name(&item.attrs, sym::naked) {
+ struct_span_err!(
+ self.tcx.sess,
+ attr.span,
+ E0736,
+ "cannot use `#[track_caller]` with `#[naked]`",
+ )
+ .emit();
+ false
+ } else {
+ true
+ }
+ }
+
/// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
fn check_non_exhaustive(
&self,
// incr. comp. yet.
dep_graph.assert_ignored();
+ let _prof_timer = sess.prof.generic_activity("hir_lowering");
+
LoweringContext {
crate_root: sess.parse_sess.injected_crate_name.try_get().copied(),
sess,
/// header, we convert it to an in-band lifetime.
fn collect_fresh_in_band_lifetime(&mut self, span: Span) -> ParamName {
assert!(self.is_collecting_in_band_lifetimes);
- let index = self.lifetimes_to_define.len();
+ let index = self.lifetimes_to_define.len() + self.in_scope_lifetimes.len();
let hir_name = ParamName::Fresh(index);
self.lifetimes_to_define.push((span, hir_name));
hir_name
forest: &'hir Forest,
definitions: &'hir Definitions)
-> Map<'hir> {
+ let _prof_timer = sess.prof.generic_activity("build_hir_map");
+
// Build the reverse mapping of `node_to_hir_id`.
let hir_to_node_id = definitions.node_to_hir_id.iter_enumerated()
.map(|(node_id, &hir_id)| (hir_id, node_id)).collect();
MutImmutable => MutImmutable,
}
}
+
+ pub fn invert(self) -> Self {
+ match self {
+ MutMutable => MutImmutable,
+ MutImmutable => MutMutable,
+ }
+ }
}
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)]
}
}
}
+
+ /// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps`
+ /// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically
+ /// silent, only signaling the ownership system. By doing this, suggestions that check the
+ /// `ExprKind` of any given `Expr` for presentation don't have to care about `DropTemps`
+ /// beyond remembering to call this function before doing analysis on it.
+ pub fn peel_drop_temps(&self) -> &Self {
+ let mut expr = self;
+ while let ExprKind::DropTemps(inner) = &expr.kind {
+ expr = inner;
+ }
+ expr
+ }
}
impl fmt::Debug for Expr {
/// probably isn't set when this is set, this is for foreign items while
/// `#[export_name]` is for Rust-defined functions.
pub link_name: Option<Symbol>,
+ /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an
+ /// imported function has in the dynamic library. Note that this must not
+ /// be set when `link_name` is set. This is for foreign items with the
+ /// "raw-dylib" kind.
+ pub link_ordinal: Option<usize>,
/// The `#[target_feature(enable = "...")]` attribute and the enabled
/// features (only enabled features are supported right now).
pub target_features: Vec<Symbol>,
const USED = 1 << 9;
/// #[ffi_returns_twice], indicates that an extern function can return
/// multiple times
- const FFI_RETURNS_TWICE = 1 << 10;
+ const FFI_RETURNS_TWICE = 1 << 10;
+ /// #[track_caller]: allow access to the caller location
+ const TRACK_CALLER = 1 << 11;
}
}
optimize: OptimizeAttr::None,
export_name: None,
link_name: None,
+ link_ordinal: None,
target_features: vec![],
linkage: None,
link_section: None,
.filter(|(a, b)| a == b)
.count();
let len = sub1.len() - common_default_params;
+ let consts_offset = len - sub1.consts().count();
// Only draw `<...>` if there're lifetime/type arguments.
if len > 0 {
// ^ elided type as this type argument was the same in both sides
let type_arguments = sub1.types().zip(sub2.types());
let regions_len = sub1.regions().count();
- for (i, (ta1, ta2)) in type_arguments.take(len).enumerate() {
+ let num_display_types = consts_offset - regions_len;
+ for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
let i = i + regions_len;
if ta1 == ta2 {
values.0.push_normal("_");
self.push_comma(&mut values.0, &mut values.1, len, i);
}
+ // Do the same for const arguments, if they are equal, do not highlight and
+ // elide them from the output.
+ let const_arguments = sub1.consts().zip(sub2.consts());
+ for (i, (ca1, ca2)) in const_arguments.enumerate() {
+ let i = i + consts_offset;
+ if ca1 == ca2 {
+ values.0.push_normal("_");
+ values.1.push_normal("_");
+ } else {
+ values.0.push_highlighted(ca1.to_string());
+ values.1.push_highlighted(ca2.to_string());
+ }
+ self.push_comma(&mut values.0, &mut values.1, len, i);
+ }
+
// Close the type argument bracket.
// Only draw `<...>` if there're lifetime/type arguments.
if len > 0 {
/// Executes `f` and commit the bindings.
pub fn commit_unconditionally<R, F>(&self, f: F) -> R
where
- F: FnOnce() -> R,
+ F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
{
- debug!("commit()");
+ debug!("commit_unconditionally()");
let snapshot = self.start_snapshot();
- let r = f();
+ let r = f(&snapshot);
self.commit_from(snapshot);
r
}
- /// Executes `f` and commit the bindings if closure `f` returns `Ok(_)`.
+ /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`.
pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E>
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>,
r
}
- /// Execute `f` in a snapshot, and commit the bindings it creates.
- pub fn in_snapshot<T, F>(&self, f: F) -> T
- where
- F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> T,
- {
- debug!("in_snapshot()");
- let snapshot = self.start_snapshot();
- let r = f(&snapshot);
- self.commit_from(snapshot);
- r
- }
-
- /// Executes `f` then unroll any bindings it creates.
+ /// Execute `f` then unroll any bindings it creates.
pub fn probe<R, F>(&self, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R,
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.
- for upvar_ty in substs.upvar_tys(def_id, self.tcx) {
+ for upvar_ty in substs.as_generator().upvar_tys(def_id, self.tcx) {
upvar_ty.visit_with(self);
}
- substs.return_ty(def_id, self.tcx).visit_with(self);
- substs.yield_ty(def_id, self.tcx).visit_with(self);
+ substs.as_generator().return_ty(def_id, self.tcx).visit_with(self);
+ substs.as_generator().yield_ty(def_id, self.tcx).visit_with(self);
}
_ => {
ty.super_visit_with(self);
ty::Generator(def_id, substs, movability) => {
let generics = self.tcx.generics_of(def_id);
let substs =
- self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| {
+ self.tcx.mk_substs(substs.iter().enumerate().map(|(index, &kind)| {
if index < generics.parent_count {
// Accommodate missing regions in the parent kinds...
self.fold_kind_mapping_missing_regions_to_empty(kind)
}
}));
- self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability)
+ self.tcx.mk_generator(def_id, substs, movability)
}
ty::Param(..) => {
/// retain the older (arguably incorrect) behavior of the
/// compiler.
///
- /// NB. The use of snapshot here is mostly an efficiency thing --
- /// we could search *all* region constraints, but that'd be a
- /// bigger set and the data structures are not setup for that. If
+ /// NB. Although `_snapshot` isn't used, it's passed in to prove
+ /// that we are in a snapshot, which guarantees that we can just
+ /// search the "undo log" for edges. This is mostly an efficiency
+ /// thing -- we could search *all* region constraints, but that'd be
+ /// a bigger set and the data structures are not setup for that. If
/// we wind up keeping some form of this check long term, it would
/// probably be better to remove the snapshot parameter and to
/// refactor the constraint set.
#![feature(const_transmute)]
#![feature(core_intrinsics)]
#![feature(drain_filter)]
-#![feature(inner_deref)]
#![cfg_attr(windows, feature(libc))]
#![feature(never_type)]
#![feature(exhaustive_patterns)]
pub mod middle {
pub mod expr_use_visitor;
pub mod cstore;
- pub mod dead;
pub mod dependency_format;
pub mod diagnostic_items;
- pub mod entry;
pub mod exported_symbols;
pub mod free_region;
- pub mod intrinsicck;
pub mod lib_features;
pub mod lang_items;
- pub mod liveness;
pub mod mem_categorization;
pub mod privacy;
pub mod reachable;
Allow,
"possible meta-variable misuse at macro definition"
}
+
+ declare_lint! {
+ pub INCOMPLETE_INCLUDE,
+ Deny,
+ "trailing content in included file"
+ }
}
declare_lint! {
use crate::hir;
use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::lint::builtin::parser::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE};
+use crate::lint::builtin::parser::INCOMPLETE_INCLUDE;
use crate::session::{Session, DiagnosticMessageId};
use crate::ty::TyCtxt;
use crate::ty::query::Providers;
match lint_id {
BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT,
BufferedEarlyLintId::MetaVariableMisuse => META_VARIABLE_MISUSE,
+ BufferedEarlyLintId::IncompleteInclude => INCOMPLETE_INCLUDE,
}
}
NativeStaticNobundle,
/// macOS-specific
NativeFramework,
+ /// Windows dynamic library without import library.
+ NativeRawDylib,
/// default way to specify a dynamic library
NativeUnknown,
}
+++ /dev/null
-// This implements the dead-code warning pass. It follows middle::reachable
-// closely. The idea is that all reachable symbols are live, codes called
-// from live codes are live, and everything else is dead.
-
-use crate::hir::Node;
-use crate::hir::{self, PatKind, TyKind};
-use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use crate::hir::itemlikevisit::ItemLikeVisitor;
-
-use crate::hir::def::{CtorOf, Res, DefKind};
-use crate::hir::CodegenFnAttrFlags;
-use crate::hir::def_id::{DefId, LOCAL_CRATE};
-use crate::lint;
-use crate::middle::privacy;
-use crate::ty::{self, DefIdTree, TyCtxt};
-use crate::util::nodemap::FxHashSet;
-
-use rustc_data_structures::fx::FxHashMap;
-
-use syntax::{ast, attr};
-use syntax::symbol::sym;
-use syntax_pos;
-
-// Any local node that may call something in its body block should be
-// explored. For example, if it's a live Node::Item that is a
-// function, then we should explore its block to check for codes that
-// may need to be marked as live.
-fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
- match tcx.hir().find(hir_id) {
- Some(Node::Item(..)) |
- Some(Node::ImplItem(..)) |
- Some(Node::ForeignItem(..)) |
- Some(Node::TraitItem(..)) |
- Some(Node::Variant(..)) |
- Some(Node::AnonConst(..)) |
- Some(Node::Pat(..)) => true,
- _ => false
- }
-}
-
-struct MarkSymbolVisitor<'a, 'tcx> {
- worklist: Vec<hir::HirId>,
- tcx: TyCtxt<'tcx>,
- tables: &'a ty::TypeckTables<'tcx>,
- live_symbols: FxHashSet<hir::HirId>,
- repr_has_repr_c: bool,
- in_pat: bool,
- inherited_pub_visibility: bool,
- ignore_variant_stack: Vec<DefId>,
- // maps from tuple struct constructors to tuple struct items
- struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
-}
-
-impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
- fn check_def_id(&mut self, def_id: DefId) {
- if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
- if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) {
- self.worklist.push(hir_id);
- }
- self.live_symbols.insert(hir_id);
- }
- }
-
- fn insert_def_id(&mut self, def_id: DefId) {
- if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
- debug_assert!(!should_explore(self.tcx, hir_id));
- self.live_symbols.insert(hir_id);
- }
- }
-
- fn handle_res(&mut self, res: Res) {
- match res {
- Res::Def(DefKind::Const, _)
- | Res::Def(DefKind::AssocConst, _)
- | Res::Def(DefKind::TyAlias, _) => {
- self.check_def_id(res.def_id());
- }
- _ if self.in_pat => {},
- Res::PrimTy(..) | Res::SelfCtor(..) |
- Res::Local(..) => {}
- Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
- let variant_id = self.tcx.parent(ctor_def_id).unwrap();
- let enum_id = self.tcx.parent(variant_id).unwrap();
- self.check_def_id(enum_id);
- if !self.ignore_variant_stack.contains(&ctor_def_id) {
- self.check_def_id(variant_id);
- }
- }
- Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
- self.check_def_id(enum_id);
- if !self.ignore_variant_stack.contains(&variant_id) {
- self.check_def_id(variant_id);
- }
- }
- Res::SelfTy(t, i) => {
- if let Some(t) = t {
- self.check_def_id(t);
- }
- if let Some(i) = i {
- self.check_def_id(i);
- }
- }
- Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
- _ => {
- self.check_def_id(res.def_id());
- }
- }
- }
-
- fn lookup_and_handle_method(&mut self, id: hir::HirId) {
- if let Some(def_id) = self.tables.type_dependent_def_id(id) {
- self.check_def_id(def_id);
- } else {
- bug!("no type-dependent def for method");
- }
- }
-
- fn handle_field_access(&mut self, lhs: &hir::Expr, hir_id: hir::HirId) {
- match self.tables.expr_ty_adjusted(lhs).kind {
- ty::Adt(def, _) => {
- let index = self.tcx.field_index(hir_id, self.tables);
- self.insert_def_id(def.non_enum_variant().fields[index].did);
- }
- ty::Tuple(..) => {}
- _ => span_bug!(lhs.span, "named field access on non-ADT"),
- }
- }
-
- fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, res: Res, pats: &[hir::FieldPat]) {
- let variant = match self.tables.node_type(lhs.hir_id).kind {
- ty::Adt(adt, _) => adt.variant_of_res(res),
- _ => span_bug!(lhs.span, "non-ADT in struct pattern")
- };
- for pat in pats {
- if let PatKind::Wild = pat.pat.kind {
- continue;
- }
- let index = self.tcx.field_index(pat.hir_id, self.tables);
- self.insert_def_id(variant.fields[index].did);
- }
- }
-
- fn mark_live_symbols(&mut self) {
- let mut scanned = FxHashSet::default();
- while let Some(id) = self.worklist.pop() {
- if !scanned.insert(id) {
- continue
- }
-
- // in the case of tuple struct constructors we want to check the item, not the generated
- // tuple struct constructor function
- let id = self.struct_constructors.get(&id).cloned().unwrap_or(id);
-
- if let Some(node) = self.tcx.hir().find(id) {
- self.live_symbols.insert(id);
- self.visit_node(node);
- }
- }
- }
-
- fn visit_node(&mut self, node: Node<'tcx>) {
- let had_repr_c = self.repr_has_repr_c;
- self.repr_has_repr_c = false;
- let had_inherited_pub_visibility = self.inherited_pub_visibility;
- self.inherited_pub_visibility = false;
- match node {
- Node::Item(item) => {
- match item.kind {
- hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
- let def_id = self.tcx.hir().local_def_id(item.hir_id);
- let def = self.tcx.adt_def(def_id);
- self.repr_has_repr_c = def.repr.c();
-
- intravisit::walk_item(self, &item);
- }
- hir::ItemKind::Enum(..) => {
- self.inherited_pub_visibility = item.vis.node.is_pub();
-
- intravisit::walk_item(self, &item);
- }
- hir::ItemKind::ForeignMod(..) => {}
- _ => {
- intravisit::walk_item(self, &item);
- }
- }
- }
- Node::TraitItem(trait_item) => {
- intravisit::walk_trait_item(self, trait_item);
- }
- Node::ImplItem(impl_item) => {
- intravisit::walk_impl_item(self, impl_item);
- }
- Node::ForeignItem(foreign_item) => {
- intravisit::walk_foreign_item(self, &foreign_item);
- }
- _ => {}
- }
- self.repr_has_repr_c = had_repr_c;
- self.inherited_pub_visibility = had_inherited_pub_visibility;
- }
-
- fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &hir::HirVec<hir::Field>) {
- if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
- for field in fields {
- let index = self.tcx.field_index(field.hir_id, self.tables);
- self.insert_def_id(adt.non_enum_variant().fields[index].did);
- }
- }
- }
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::None
- }
-
- fn visit_nested_body(&mut self, body: hir::BodyId) {
- let old_tables = self.tables;
- self.tables = self.tcx.body_tables(body);
- let body = self.tcx.hir().body(body);
- self.visit_body(body);
- self.tables = old_tables;
- }
-
- fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name,
- _: &hir::Generics, _: hir::HirId, _: syntax_pos::Span) {
- let has_repr_c = self.repr_has_repr_c;
- let inherited_pub_visibility = self.inherited_pub_visibility;
- let live_fields = def.fields().iter().filter(|f| {
- has_repr_c || inherited_pub_visibility || f.vis.node.is_pub()
- });
- self.live_symbols.extend(live_fields.map(|f| f.hir_id));
-
- intravisit::walk_struct_def(self, def);
- }
-
- fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
- match expr.kind {
- hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
- let res = self.tables.qpath_res(qpath, expr.hir_id);
- self.handle_res(res);
- }
- hir::ExprKind::MethodCall(..) => {
- self.lookup_and_handle_method(expr.hir_id);
- }
- hir::ExprKind::Field(ref lhs, ..) => {
- self.handle_field_access(&lhs, expr.hir_id);
- }
- hir::ExprKind::Struct(_, ref fields, _) => {
- if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind {
- self.mark_as_used_if_union(adt, fields);
- }
- }
- _ => ()
- }
-
- intravisit::walk_expr(self, expr);
- }
-
- fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
- // Inside the body, ignore constructions of variants
- // necessary for the pattern to match. Those construction sites
- // can't be reached unless the variant is constructed elsewhere.
- let len = self.ignore_variant_stack.len();
- self.ignore_variant_stack.extend(arm.pat.necessary_variants());
- intravisit::walk_arm(self, arm);
- self.ignore_variant_stack.truncate(len);
- }
-
- fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
- match pat.kind {
- PatKind::Struct(ref path, ref fields, _) => {
- let res = self.tables.qpath_res(path, pat.hir_id);
- self.handle_field_pattern_match(pat, res, fields);
- }
- PatKind::Path(ref qpath) => {
- let res = self.tables.qpath_res(qpath, pat.hir_id);
- self.handle_res(res);
- }
- _ => ()
- }
-
- self.in_pat = true;
- intravisit::walk_pat(self, pat);
- self.in_pat = false;
- }
-
- fn visit_path(&mut self, path: &'tcx hir::Path, _: hir::HirId) {
- self.handle_res(path.res);
- intravisit::walk_path(self, path);
- }
-
- fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
- match ty.kind {
- TyKind::Def(item_id, _) => {
- let item = self.tcx.hir().expect_item(item_id.id);
- intravisit::walk_item(self, item);
- }
- _ => ()
- }
- intravisit::walk_ty(self, ty);
- }
-
- fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
- self.live_symbols.insert(c.hir_id);
- intravisit::walk_anon_const(self, c);
- }
-}
-
-fn has_allow_dead_code_or_lang_attr(
- tcx: TyCtxt<'_>,
- id: hir::HirId,
- attrs: &[ast::Attribute],
-) -> bool {
- if attr::contains_name(attrs, sym::lang) {
- return true;
- }
-
- // Stable attribute for #[lang = "panic_impl"]
- if attr::contains_name(attrs, sym::panic_handler) {
- return true;
- }
-
- // (To be) stable attribute for #[lang = "oom"]
- if attr::contains_name(attrs, sym::alloc_error_handler) {
- return true;
- }
-
- let def_id = tcx.hir().local_def_id(id);
- let cg_attrs = tcx.codegen_fn_attrs(def_id);
-
- // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
- // forcefully, e.g., for placing it in a specific section.
- if cg_attrs.contains_extern_indicator() ||
- cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
- return true;
- }
-
- tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
-}
-
-// This visitor seeds items that
-// 1) We want to explicitly consider as live:
-// * Item annotated with #[allow(dead_code)]
-// - This is done so that if we want to suppress warnings for a
-// group of dead functions, we only have to annotate the "root".
-// For example, if both `f` and `g` are dead and `f` calls `g`,
-// then annotating `f` with `#[allow(dead_code)]` will suppress
-// warning for both `f` and `g`.
-// * Item annotated with #[lang=".."]
-// - This is because lang items are always callable from elsewhere.
-// or
-// 2) We are not sure to be live or not
-// * Implementation of a trait method
-struct LifeSeeder<'k, 'tcx> {
- worklist: Vec<hir::HirId>,
- krate: &'k hir::Crate,
- tcx: TyCtxt<'tcx>,
- // see `MarkSymbolVisitor::struct_constructors`
- struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
-}
-
-impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
- fn visit_item(&mut self, item: &hir::Item) {
- let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx,
- item.hir_id,
- &item.attrs);
- if allow_dead_code {
- self.worklist.push(item.hir_id);
- }
- match item.kind {
- hir::ItemKind::Enum(ref enum_def, _) => {
- if allow_dead_code {
- self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id));
- }
-
- for variant in &enum_def.variants {
- if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
- self.struct_constructors.insert(ctor_hir_id, variant.id);
- }
- }
- }
- hir::ItemKind::Trait(.., ref trait_item_refs) => {
- for trait_item_ref in trait_item_refs {
- let trait_item = self.krate.trait_item(trait_item_ref.id);
- match trait_item.kind {
- hir::TraitItemKind::Const(_, Some(_)) |
- hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
- if has_allow_dead_code_or_lang_attr(self.tcx,
- trait_item.hir_id,
- &trait_item.attrs) {
- self.worklist.push(trait_item.hir_id);
- }
- }
- _ => {}
- }
- }
- }
- hir::ItemKind::Impl(.., ref opt_trait, _, ref impl_item_refs) => {
- for impl_item_ref in impl_item_refs {
- let impl_item = self.krate.impl_item(impl_item_ref.id);
- if opt_trait.is_some() ||
- has_allow_dead_code_or_lang_attr(self.tcx,
- impl_item.hir_id,
- &impl_item.attrs) {
- self.worklist.push(impl_item_ref.id.hir_id);
- }
- }
- }
- hir::ItemKind::Struct(ref variant_data, _) => {
- if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
- self.struct_constructors.insert(ctor_hir_id, item.hir_id);
- }
- }
- _ => ()
- }
- }
-
- fn visit_trait_item(&mut self, _item: &hir::TraitItem) {
- // ignore: we are handling this in `visit_item` above
- }
-
- fn visit_impl_item(&mut self, _item: &hir::ImplItem) {
- // ignore: we are handling this in `visit_item` above
- }
-}
-
-fn create_and_seed_worklist<'tcx>(
- tcx: TyCtxt<'tcx>,
- access_levels: &privacy::AccessLevels,
- krate: &hir::Crate,
-) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) {
- let worklist = access_levels.map.iter().filter_map(|(&id, level)| {
- if level >= &privacy::AccessLevel::Reachable {
- Some(id)
- } else {
- None
- }
- }).chain(
- // Seed entry point
- tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id).unwrap())
- ).collect::<Vec<_>>();
-
- // Seed implemented trait items
- let mut life_seeder = LifeSeeder {
- worklist,
- krate,
- tcx,
- struct_constructors: Default::default(),
- };
- krate.visit_all_item_likes(&mut life_seeder);
-
- (life_seeder.worklist, life_seeder.struct_constructors)
-}
-
-fn find_live<'tcx>(
- tcx: TyCtxt<'tcx>,
- access_levels: &privacy::AccessLevels,
- krate: &hir::Crate,
-) -> FxHashSet<hir::HirId> {
- let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate);
- let mut symbol_visitor = MarkSymbolVisitor {
- worklist,
- tcx,
- tables: &ty::TypeckTables::empty(None),
- live_symbols: Default::default(),
- repr_has_repr_c: false,
- in_pat: false,
- inherited_pub_visibility: false,
- ignore_variant_stack: vec![],
- struct_constructors,
- };
- symbol_visitor.mark_live_symbols();
- symbol_visitor.live_symbols
-}
-
-struct DeadVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- live_symbols: FxHashSet<hir::HirId>,
-}
-
-impl DeadVisitor<'tcx> {
- fn should_warn_about_item(&mut self, item: &hir::Item) -> bool {
- let should_warn = match item.kind {
- hir::ItemKind::Static(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Fn(..)
- | hir::ItemKind::TyAlias(..)
- | hir::ItemKind::Enum(..)
- | hir::ItemKind::Struct(..)
- | hir::ItemKind::Union(..) => true,
- _ => false
- };
- should_warn && !self.symbol_is_live(item.hir_id)
- }
-
- fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool {
- let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id));
- !field.is_positional()
- && !self.symbol_is_live(field.hir_id)
- && !field_type.is_phantom_data()
- && !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs)
- }
-
- fn should_warn_about_variant(&mut self, variant: &hir::Variant) -> bool {
- !self.symbol_is_live(variant.id)
- && !has_allow_dead_code_or_lang_attr(self.tcx,
- variant.id,
- &variant.attrs)
- }
-
- fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool {
- !self.symbol_is_live(fi.hir_id)
- && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id, &fi.attrs)
- }
-
- // id := HIR id of an item's definition.
- fn symbol_is_live(
- &mut self,
- id: hir::HirId,
- ) -> bool {
- if self.live_symbols.contains(&id) {
- return true;
- }
- // If it's a type whose items are live, then it's live, too.
- // This is done to handle the case where, for example, the static
- // method of a private type is used, but the type itself is never
- // called directly.
- let def_id = self.tcx.hir().local_def_id(id);
- let inherent_impls = self.tcx.inherent_impls(def_id);
- for &impl_did in inherent_impls.iter() {
- for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] {
- if let Some(item_hir_id) = self.tcx.hir().as_local_hir_id(item_did) {
- if self.live_symbols.contains(&item_hir_id) {
- return true;
- }
- }
- }
- }
- false
- }
-
- fn warn_dead_code(&mut self,
- id: hir::HirId,
- span: syntax_pos::Span,
- name: ast::Name,
- node_type: &str,
- participle: &str) {
- if !name.as_str().starts_with("_") {
- self.tcx
- .lint_hir(lint::builtin::DEAD_CODE,
- id,
- span,
- &format!("{} is never {}: `{}`",
- node_type, participle, name));
- }
- }
-}
-
-impl Visitor<'tcx> for DeadVisitor<'tcx> {
- /// Walk nested items in place so that we don't report dead-code
- /// on inner functions when the outer function is already getting
- /// an error. We could do this also by checking the parents, but
- /// this is how the code is setup and it seems harmless enough.
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::All(&self.tcx.hir())
- }
-
- fn visit_item(&mut self, item: &'tcx hir::Item) {
- if self.should_warn_about_item(item) {
- // For items that have a definition with a signature followed by a
- // block, point only at the signature.
- let span = match item.kind {
- hir::ItemKind::Fn(..) |
- hir::ItemKind::Mod(..) |
- hir::ItemKind::Enum(..) |
- hir::ItemKind::Struct(..) |
- hir::ItemKind::Union(..) |
- hir::ItemKind::Trait(..) |
- hir::ItemKind::Impl(..) => self.tcx.sess.source_map().def_span(item.span),
- _ => item.span,
- };
- let participle = match item.kind {
- hir::ItemKind::Struct(..) => "constructed", // Issue #52325
- _ => "used"
- };
- self.warn_dead_code(
- item.hir_id,
- span,
- item.ident.name,
- item.kind.descriptive_variant(),
- participle,
- );
- } else {
- // Only continue if we didn't warn
- intravisit::walk_item(self, item);
- }
- }
-
- fn visit_variant(&mut self,
- variant: &'tcx hir::Variant,
- g: &'tcx hir::Generics,
- id: hir::HirId) {
- if self.should_warn_about_variant(&variant) {
- self.warn_dead_code(variant.id, variant.span, variant.ident.name,
- "variant", "constructed");
- } else {
- intravisit::walk_variant(self, variant, g, id);
- }
- }
-
- fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) {
- if self.should_warn_about_foreign_item(fi) {
- self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name,
- fi.kind.descriptive_variant(), "used");
- }
- intravisit::walk_foreign_item(self, fi);
- }
-
- fn visit_struct_field(&mut self, field: &'tcx hir::StructField) {
- if self.should_warn_about_field(&field) {
- self.warn_dead_code(field.hir_id, field.span, field.ident.name, "field", "used");
- }
- intravisit::walk_struct_field(self, field);
- }
-
- fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
- match impl_item.kind {
- hir::ImplItemKind::Const(_, body_id) => {
- if !self.symbol_is_live(impl_item.hir_id) {
- self.warn_dead_code(impl_item.hir_id,
- impl_item.span,
- impl_item.ident.name,
- "associated const",
- "used");
- }
- self.visit_nested_body(body_id)
- }
- hir::ImplItemKind::Method(_, body_id) => {
- if !self.symbol_is_live(impl_item.hir_id) {
- let span = self.tcx.sess.source_map().def_span(impl_item.span);
- self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "method",
- "used");
- }
- self.visit_nested_body(body_id)
- }
- hir::ImplItemKind::OpaqueTy(..) |
- hir::ImplItemKind::TyAlias(..) => {}
- }
- }
-
- // Overwrite so that we don't warn the trait item itself.
- fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
- match trait_item.kind {
- hir::TraitItemKind::Const(_, Some(body_id)) |
- hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)) => {
- self.visit_nested_body(body_id)
- }
- hir::TraitItemKind::Const(_, None) |
- hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) |
- hir::TraitItemKind::Type(..) => {}
- }
- }
-}
-
-pub fn check_crate(tcx: TyCtxt<'_>) {
- let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
- let krate = tcx.hir().krate();
- let live_symbols = find_live(tcx, access_levels, krate);
- let mut visitor = DeadVisitor {
- tcx,
- live_symbols,
- };
- intravisit::walk_crate(&mut visitor, krate);
-}
+++ /dev/null
-use crate::hir::map as hir_map;
-use crate::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
-use crate::session::{config, Session};
-use crate::session::config::EntryFnType;
-use syntax::attr;
-use syntax::entry::EntryPointType;
-use syntax::symbol::sym;
-use syntax_pos::Span;
-use crate::hir::{HirId, Item, ItemKind, ImplItem, TraitItem};
-use crate::hir::itemlikevisit::ItemLikeVisitor;
-use crate::ty::TyCtxt;
-use crate::ty::query::Providers;
-
-struct EntryContext<'a, 'tcx> {
- session: &'a Session,
-
- map: &'a hir_map::Map<'tcx>,
-
- /// The top-level function called `main`.
- main_fn: Option<(HirId, Span)>,
-
- /// The function that has attribute named `main`.
- attr_main_fn: Option<(HirId, Span)>,
-
- /// The function that has the attribute 'start' on it.
- start_fn: Option<(HirId, Span)>,
-
- /// The functions that one might think are `main` but aren't, e.g.
- /// main functions not defined at the top level. For diagnostics.
- non_main_fns: Vec<(HirId, Span)> ,
-}
-
-impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
- fn visit_item(&mut self, item: &'tcx Item) {
- let def_id = self.map.local_def_id(item.hir_id);
- let def_key = self.map.def_key(def_id);
- let at_root = def_key.parent == Some(CRATE_DEF_INDEX);
- find_item(item, self, at_root);
- }
-
- fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem) {
- // Entry fn is never a trait item.
- }
-
- fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem) {
- // Entry fn is never a trait item.
- }
-}
-
-fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> {
- assert_eq!(cnum, LOCAL_CRATE);
-
- let any_exe = tcx.sess.crate_types.borrow().iter().any(|ty| {
- *ty == config::CrateType::Executable
- });
- if !any_exe {
- // No need to find a main function.
- return None;
- }
-
- // If the user wants no main function at all, then stop here.
- if attr::contains_name(&tcx.hir().krate().attrs, sym::no_main) {
- return None;
- }
-
- let mut ctxt = EntryContext {
- session: tcx.sess,
- map: tcx.hir(),
- main_fn: None,
- attr_main_fn: None,
- start_fn: None,
- non_main_fns: Vec::new(),
- };
-
- tcx.hir().krate().visit_all_item_likes(&mut ctxt);
-
- configure_main(tcx, &ctxt)
-}
-
-// Beware, this is duplicated in `libsyntax/entry.rs`, so make sure to keep
-// them in sync.
-fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType {
- match item.kind {
- ItemKind::Fn(..) => {
- if attr::contains_name(&item.attrs, sym::start) {
- EntryPointType::Start
- } else if attr::contains_name(&item.attrs, sym::main) {
- EntryPointType::MainAttr
- } else if item.ident.name == sym::main {
- if at_root {
- // This is a top-level function so can be `main`.
- EntryPointType::MainNamed
- } else {
- EntryPointType::OtherMain
- }
- } else {
- EntryPointType::None
- }
- }
- _ => EntryPointType::None,
- }
-}
-
-
-fn find_item(item: &Item, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
- match entry_point_type(item, at_root) {
- EntryPointType::MainNamed => {
- if ctxt.main_fn.is_none() {
- ctxt.main_fn = Some((item.hir_id, item.span));
- } else {
- span_err!(ctxt.session, item.span, E0136,
- "multiple `main` functions");
- }
- },
- EntryPointType::OtherMain => {
- ctxt.non_main_fns.push((item.hir_id, item.span));
- },
- EntryPointType::MainAttr => {
- if ctxt.attr_main_fn.is_none() {
- ctxt.attr_main_fn = Some((item.hir_id, item.span));
- } else {
- struct_span_err!(ctxt.session, item.span, E0137,
- "multiple functions with a `#[main]` attribute")
- .span_label(item.span, "additional `#[main]` function")
- .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function")
- .emit();
- }
- },
- EntryPointType::Start => {
- if ctxt.start_fn.is_none() {
- ctxt.start_fn = Some((item.hir_id, item.span));
- } else {
- struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions")
- .span_label(ctxt.start_fn.unwrap().1, "previous `start` function here")
- .span_label(item.span, "multiple `start` functions")
- .emit();
- }
- }
- EntryPointType::None => (),
- }
-}
-
-fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> {
- if let Some((hir_id, _)) = visitor.start_fn {
- Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start))
- } else if let Some((hir_id, _)) = visitor.attr_main_fn {
- Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
- } else if let Some((hir_id, _)) = visitor.main_fn {
- Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
- } else {
- no_main_err(tcx, visitor);
- None
- }
-}
-
-fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
- // There is no main function.
- let mut err = struct_err!(tcx.sess, E0601,
- "`main` function not found in crate `{}`", tcx.crate_name(LOCAL_CRATE));
- let filename = &tcx.sess.local_crate_source_file;
- let note = if !visitor.non_main_fns.is_empty() {
- for &(_, span) in &visitor.non_main_fns {
- err.span_note(span, "here is a function named `main`");
- }
- err.note("you have one or more functions named `main` not defined at the crate level");
- err.help("either move the `main` function definitions or attach the `#[main]` attribute \
- to one of them");
- // There were some functions named `main` though. Try to give the user a hint.
- format!("the main function must be defined at the crate level{}",
- filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default())
- } else if let Some(filename) = filename {
- format!("consider adding a `main` function to `{}`", filename.display())
- } else {
- String::from("consider adding a `main` function at the crate level")
- };
- let sp = tcx.hir().krate().span;
- // The file may be empty, which leads to the diagnostic machinery not emitting this
- // note. This is a relatively simple way to detect that case and emit a span-less
- // note instead.
- if let Ok(_) = tcx.sess.source_map().lookup_line(sp.lo()) {
- err.set_span(sp);
- err.span_label(sp, ¬e);
- } else {
- err.note(¬e);
- }
- if tcx.sess.teach(&err.get_code().unwrap()) {
- err.note("If you don't know the basics of Rust, you can go look to the Rust Book \
- to get started: https://doc.rust-lang.org/book/");
- }
- err.emit();
-}
-
-pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> {
- tcx.entry_fn(LOCAL_CRATE)
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
- *providers = Providers {
- entry_fn,
- ..*providers
- };
-}
+++ /dev/null
-use crate::hir::def::{Res, DefKind};
-use crate::hir::def_id::DefId;
-use crate::ty::{self, Ty, TyCtxt};
-use crate::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx};
-use crate::ty::query::Providers;
-
-use rustc_target::spec::abi::Abi::RustIntrinsic;
-use rustc_index::vec::Idx;
-use syntax_pos::{Span, sym};
-use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use crate::hir;
-
-fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) {
- tcx.hir().visit_item_likes_in_module(
- module_def_id,
- &mut ItemVisitor { tcx }.as_deep_visitor()
- );
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
- *providers = Providers {
- check_mod_intrinsics,
- ..*providers
- };
-}
-
-struct ItemVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
-
-struct ExprVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- tables: &'tcx ty::TypeckTables<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
-}
-
-/// If the type is `Option<T>`, it will return `T`, otherwise
-/// the type itself. Works on most `Option`-like types.
-fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
- let (def, substs) = match ty.kind {
- ty::Adt(def, substs) => (def, substs),
- _ => return ty
- };
-
- if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() {
- let data_idx;
-
- let one = VariantIdx::new(1);
- let zero = VariantIdx::new(0);
-
- if def.variants[zero].fields.is_empty() {
- data_idx = one;
- } else if def.variants[one].fields.is_empty() {
- data_idx = zero;
- } else {
- return ty;
- }
-
- if def.variants[data_idx].fields.len() == 1 {
- return def.variants[data_idx].fields[0].ty(tcx, substs);
- }
- }
-
- ty
-}
-
-impl ExprVisitor<'tcx> {
- fn def_id_is_transmute(&self, def_id: DefId) -> bool {
- self.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
- self.tcx.item_name(def_id) == sym::transmute
- }
-
- fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
- let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
- let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
-
- // Check for same size using the skeletons.
- if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
- if sk_from.same_size(sk_to) {
- return;
- }
-
- // Special-case transmutting from `typeof(function)` and
- // `Option<typeof(function)>` to present a clearer error.
- let from = unpack_option_like(self.tcx, from);
- if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.kind, sk_to) {
- if size_to == Pointer.size(&self.tcx) {
- struct_span_err!(self.tcx.sess, span, E0591,
- "can't transmute zero-sized type")
- .note(&format!("source type: {}", from))
- .note(&format!("target type: {}", to))
- .help("cast with `as` to a pointer instead")
- .emit();
- return;
- }
- }
- }
-
- // Try to display a sensible error with as much information as possible.
- let skeleton_string = |ty: Ty<'tcx>, sk| {
- match sk {
- Ok(SizeSkeleton::Known(size)) => {
- format!("{} bits", size.bits())
- }
- Ok(SizeSkeleton::Pointer { tail, .. }) => {
- format!("pointer to `{}`", tail)
- }
- Err(LayoutError::Unknown(bad)) => {
- if bad == ty {
- "this type does not have a fixed size".to_owned()
- } else {
- format!("size can vary because of {}", bad)
- }
- }
- Err(err) => err.to_string()
- }
- };
-
- let mut err = struct_span_err!(self.tcx.sess, span, E0512,
- "cannot transmute between types of different sizes, \
- or dependently-sized types");
- if from == to {
- err.note(&format!("`{}` does not have a fixed size", from));
- } else {
- err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
- .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
- }
- err.emit()
- }
-}
-
-impl Visitor<'tcx> for ItemVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::None
- }
-
- fn visit_nested_body(&mut self, body_id: hir::BodyId) {
- let owner_def_id = self.tcx.hir().body_owner_def_id(body_id);
- let body = self.tcx.hir().body(body_id);
- let param_env = self.tcx.param_env(owner_def_id);
- let tables = self.tcx.typeck_tables_of(owner_def_id);
- ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body);
- self.visit_body(body);
- }
-}
-
-impl Visitor<'tcx> for ExprVisitor<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
- let res = if let hir::ExprKind::Path(ref qpath) = expr.kind {
- self.tables.qpath_res(qpath, expr.hir_id)
- } else {
- Res::Err
- };
- if let Res::Def(DefKind::Fn, did) = res {
- if self.def_id_is_transmute(did) {
- let typ = self.tables.node_type(expr.hir_id);
- let sig = typ.fn_sig(self.tcx);
- let from = sig.inputs().skip_binder()[0];
- let to = *sig.output().skip_binder();
- self.check_transmute(expr.span, from, to);
- }
- }
-
- intravisit::walk_expr(self, expr);
- }
-}
+++ /dev/null
-//! A classic liveness analysis based on dataflow over the AST. Computes,
-//! for each local variable in a function, whether that variable is live
-//! at a given point. Program execution points are identified by their
-//! IDs.
-//!
-//! # Basic idea
-//!
-//! The basic model is that each local variable is assigned an index. We
-//! represent sets of local variables using a vector indexed by this
-//! index. The value in the vector is either 0, indicating the variable
-//! is dead, or the ID of an expression that uses the variable.
-//!
-//! We conceptually walk over the AST in reverse execution order. If we
-//! find a use of a variable, we add it to the set of live variables. If
-//! we find an assignment to a variable, we remove it from the set of live
-//! variables. When we have to merge two flows, we take the union of
-//! those two flows -- if the variable is live on both paths, we simply
-//! pick one ID. In the event of loops, we continue doing this until a
-//! fixed point is reached.
-//!
-//! ## Checking initialization
-//!
-//! At the function entry point, all variables must be dead. If this is
-//! not the case, we can report an error using the ID found in the set of
-//! live variables, which identifies a use of the variable which is not
-//! dominated by an assignment.
-//!
-//! ## Checking moves
-//!
-//! After each explicit move, the variable must be dead.
-//!
-//! ## Computing last uses
-//!
-//! Any use of the variable where the variable is dead afterwards is a
-//! last use.
-//!
-//! # Implementation details
-//!
-//! The actual implementation contains two (nested) walks over the AST.
-//! The outer walk has the job of building up the ir_maps instance for the
-//! enclosing function. On the way down the tree, it identifies those AST
-//! nodes and variable IDs that will be needed for the liveness analysis
-//! and assigns them contiguous IDs. The liveness ID for an AST node is
-//! called a `live_node` (it's a newtype'd `u32`) and the ID for a variable
-//! is called a `variable` (another newtype'd `u32`).
-//!
-//! On the way back up the tree, as we are about to exit from a function
-//! declaration we allocate a `liveness` instance. Now that we know
-//! precisely how many nodes and variables we need, we can allocate all
-//! the various arrays that we will need to precisely the right size. We then
-//! perform the actual propagation on the `liveness` instance.
-//!
-//! This propagation is encoded in the various `propagate_through_*()`
-//! methods. It effectively does a reverse walk of the AST; whenever we
-//! reach a loop node, we iterate until a fixed point is reached.
-//!
-//! ## The `RWU` struct
-//!
-//! At each live node `N`, we track three pieces of information for each
-//! variable `V` (these are encapsulated in the `RWU` struct):
-//!
-//! - `reader`: the `LiveNode` ID of some node which will read the value
-//! that `V` holds on entry to `N`. Formally: a node `M` such
-//! that there exists a path `P` from `N` to `M` where `P` does not
-//! write `V`. If the `reader` is `invalid_node()`, then the current
-//! value will never be read (the variable is dead, essentially).
-//!
-//! - `writer`: the `LiveNode` ID of some node which will write the
-//! variable `V` and which is reachable from `N`. Formally: a node `M`
-//! such that there exists a path `P` from `N` to `M` and `M` writes
-//! `V`. If the `writer` is `invalid_node()`, then there is no writer
-//! of `V` that follows `N`.
-//!
-//! - `used`: a boolean value indicating whether `V` is *used*. We
-//! distinguish a *read* from a *use* in that a *use* is some read that
-//! is not just used to generate a new value. For example, `x += 1` is
-//! a read but not a use. This is used to generate better warnings.
-//!
-//! ## Special Variables
-//!
-//! We generate various special variables for various, well, special purposes.
-//! These are described in the `specials` struct:
-//!
-//! - `exit_ln`: a live node that is generated to represent every 'exit' from
-//! the function, whether it be by explicit return, panic, or other means.
-//!
-//! - `fallthrough_ln`: a live node that represents a fallthrough
-//!
-//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
-//! fallthrough node. It is only live if the function could converge
-//! via means other than an explicit `return` expression. That is, it is
-//! only dead if the end of the function's block can never be reached.
-//! It is the responsibility of typeck to ensure that there are no
-//! `return` expressions in a function declared as diverging.
-
-use self::LiveNodeKind::*;
-use self::VarKind::*;
-
-use crate::hir;
-use crate::hir::{Expr, HirId};
-use crate::hir::def::*;
-use crate::hir::def_id::DefId;
-use crate::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
-use crate::hir::Node;
-use crate::hir::ptr::P;
-use crate::ty::{self, TyCtxt};
-use crate::ty::query::Providers;
-use crate::lint;
-use crate::util::nodemap::{HirIdMap, HirIdSet};
-
-use errors::Applicability;
-use rustc_data_structures::fx::FxIndexMap;
-use std::collections::VecDeque;
-use std::{fmt, u32};
-use std::io::prelude::*;
-use std::io;
-use std::rc::Rc;
-use syntax::ast;
-use syntax::symbol::sym;
-use syntax_pos::Span;
-
-#[derive(Copy, Clone, PartialEq)]
-struct Variable(u32);
-
-#[derive(Copy, Clone, PartialEq)]
-struct LiveNode(u32);
-
-impl Variable {
- fn get(&self) -> usize { self.0 as usize }
-}
-
-impl LiveNode {
- fn get(&self) -> usize { self.0 as usize }
-}
-
-#[derive(Copy, Clone, PartialEq, Debug)]
-enum LiveNodeKind {
- UpvarNode(Span),
- ExprNode(Span),
- VarDefNode(Span),
- ExitNode
-}
-
-fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
- let cm = tcx.sess.source_map();
- match lnk {
- UpvarNode(s) => {
- format!("Upvar node [{}]", cm.span_to_string(s))
- }
- ExprNode(s) => {
- format!("Expr node [{}]", cm.span_to_string(s))
- }
- VarDefNode(s) => {
- format!("Var def node [{}]", cm.span_to_string(s))
- }
- ExitNode => "Exit node".to_owned(),
- }
-}
-
-impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::OnlyBodies(&self.tcx.hir())
- }
-
- fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl,
- b: hir::BodyId, s: Span, id: HirId) {
- visit_fn(self, fk, fd, b, s, id);
- }
-
- fn visit_local(&mut self, l: &'tcx hir::Local) { visit_local(self, l); }
- fn visit_expr(&mut self, ex: &'tcx Expr) { visit_expr(self, ex); }
- fn visit_arm(&mut self, a: &'tcx hir::Arm) { visit_arm(self, a); }
-}
-
-fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: DefId) {
- tcx.hir().visit_item_likes_in_module(
- module_def_id,
- &mut IrMaps::new(tcx, module_def_id).as_deep_visitor(),
- );
-}
-
-pub fn provide(providers: &mut Providers<'_>) {
- *providers = Providers {
- check_mod_liveness,
- ..*providers
- };
-}
-
-impl fmt::Debug for LiveNode {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ln({})", self.get())
- }
-}
-
-impl fmt::Debug for Variable {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "v({})", self.get())
- }
-}
-
-// ______________________________________________________________________
-// Creating ir_maps
-//
-// This is the first pass and the one that drives the main
-// computation. It walks up and down the IR once. On the way down,
-// we count for each function the number of variables as well as
-// liveness nodes. A liveness node is basically an expression or
-// capture clause that does something of interest: either it has
-// interesting control flow or it uses/defines a local variable.
-//
-// On the way back up, at each function node we create liveness sets
-// (we now know precisely how big to make our various vectors and so
-// forth) and then do the data-flow propagation to compute the set
-// of live variables at each program point.
-//
-// Finally, we run back over the IR one last time and, using the
-// computed liveness, check various safety conditions. For example,
-// there must be no live nodes at the definition site for a variable
-// unless it has an initializer. Similarly, each non-mutable local
-// variable must not be assigned if there is some successor
-// assignment. And so forth.
-
-impl LiveNode {
- fn is_valid(&self) -> bool {
- self.0 != u32::MAX
- }
-}
-
-fn invalid_node() -> LiveNode { LiveNode(u32::MAX) }
-
-struct CaptureInfo {
- ln: LiveNode,
- var_hid: HirId
-}
-
-#[derive(Copy, Clone, Debug)]
-struct LocalInfo {
- id: HirId,
- name: ast::Name,
- is_shorthand: bool,
-}
-
-#[derive(Copy, Clone, Debug)]
-enum VarKind {
- Param(HirId, ast::Name),
- Local(LocalInfo),
- CleanExit
-}
-
-struct IrMaps<'tcx> {
- tcx: TyCtxt<'tcx>,
- body_owner: DefId,
- num_live_nodes: usize,
- num_vars: usize,
- live_node_map: HirIdMap<LiveNode>,
- variable_map: HirIdMap<Variable>,
- capture_info_map: HirIdMap<Rc<Vec<CaptureInfo>>>,
- var_kinds: Vec<VarKind>,
- lnks: Vec<LiveNodeKind>,
-}
-
-impl IrMaps<'tcx> {
- fn new(tcx: TyCtxt<'tcx>, body_owner: DefId) -> IrMaps<'tcx> {
- IrMaps {
- tcx,
- body_owner,
- num_live_nodes: 0,
- num_vars: 0,
- live_node_map: HirIdMap::default(),
- variable_map: HirIdMap::default(),
- capture_info_map: Default::default(),
- var_kinds: Vec::new(),
- lnks: Vec::new(),
- }
- }
-
- fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode {
- let ln = LiveNode(self.num_live_nodes as u32);
- self.lnks.push(lnk);
- self.num_live_nodes += 1;
-
- debug!("{:?} is of kind {}", ln,
- live_node_kind_to_string(lnk, self.tcx));
-
- ln
- }
-
- fn add_live_node_for_node(&mut self, hir_id: HirId, lnk: LiveNodeKind) {
- let ln = self.add_live_node(lnk);
- self.live_node_map.insert(hir_id, ln);
-
- debug!("{:?} is node {:?}", ln, hir_id);
- }
-
- fn add_variable(&mut self, vk: VarKind) -> Variable {
- let v = Variable(self.num_vars as u32);
- self.var_kinds.push(vk);
- self.num_vars += 1;
-
- match vk {
- Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
- self.variable_map.insert(node_id, v);
- },
- CleanExit => {}
- }
-
- debug!("{:?} is {:?}", v, vk);
-
- v
- }
-
- fn variable(&self, hir_id: HirId, span: Span) -> Variable {
- match self.variable_map.get(&hir_id) {
- Some(&var) => var,
- None => {
- span_bug!(span, "no variable registered for id {:?}", hir_id);
- }
- }
- }
-
- fn variable_name(&self, var: Variable) -> String {
- match self.var_kinds[var.get()] {
- Local(LocalInfo { name, .. }) | Param(_, name) => {
- name.to_string()
- },
- CleanExit => "<clean-exit>".to_owned()
- }
- }
-
- fn variable_is_shorthand(&self, var: Variable) -> bool {
- match self.var_kinds[var.get()] {
- Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
- Param(..) | CleanExit => false
- }
- }
-
- fn set_captures(&mut self, hir_id: HirId, cs: Vec<CaptureInfo>) {
- self.capture_info_map.insert(hir_id, Rc::new(cs));
- }
-
- fn lnk(&self, ln: LiveNode) -> LiveNodeKind {
- self.lnks[ln.get()]
- }
-}
-
-fn visit_fn<'tcx>(
- ir: &mut IrMaps<'tcx>,
- fk: FnKind<'tcx>,
- decl: &'tcx hir::FnDecl,
- body_id: hir::BodyId,
- sp: Span,
- id: hir::HirId,
-) {
- debug!("visit_fn");
-
- // swap in a new set of IR maps for this function body:
- let def_id = ir.tcx.hir().local_def_id(id);
- let mut fn_maps = IrMaps::new(ir.tcx, def_id);
-
- // Don't run unused pass for #[derive()]
- if let FnKind::Method(..) = fk {
- let parent = ir.tcx.hir().get_parent_item(id);
- if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) {
- if i.attrs.iter().any(|a| a.check_name(sym::automatically_derived)) {
- return;
- }
- }
- }
-
- debug!("creating fn_maps: {:p}", &fn_maps);
-
- let body = ir.tcx.hir().body(body_id);
-
- for param in &body.params {
- let is_shorthand = match param.pat.kind {
- crate::hir::PatKind::Struct(..) => true,
- _ => false,
- };
- param.pat.each_binding(|_bm, hir_id, _x, ident| {
- debug!("adding parameters {:?}", hir_id);
- let var = if is_shorthand {
- Local(LocalInfo {
- id: hir_id,
- name: ident.name,
- is_shorthand: true,
- })
- } else {
- Param(hir_id, ident.name)
- };
- fn_maps.add_variable(var);
- })
- };
-
- // gather up the various local variables, significant expressions,
- // and so forth:
- intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id);
-
- // compute liveness
- let mut lsets = Liveness::new(&mut fn_maps, body_id);
- let entry_ln = lsets.compute(&body.value);
-
- // check for various error conditions
- lsets.visit_body(body);
- lsets.warn_about_unused_args(body, entry_ln);
-}
-
-fn add_from_pat(ir: &mut IrMaps<'_>, pat: &P<hir::Pat>) {
- // For struct patterns, take note of which fields used shorthand
- // (`x` rather than `x: x`).
- let mut shorthand_field_ids = HirIdSet::default();
- let mut pats = VecDeque::new();
- pats.push_back(pat);
- while let Some(pat) = pats.pop_front() {
- use crate::hir::PatKind::*;
- match &pat.kind {
- Binding(.., inner_pat) => {
- pats.extend(inner_pat.iter());
- }
- Struct(_, fields, _) => {
- let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id);
- shorthand_field_ids.extend(ids);
- }
- Ref(inner_pat, _) | Box(inner_pat) => {
- pats.push_back(inner_pat);
- }
- TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => {
- pats.extend(inner_pats.iter());
- }
- Slice(pre_pats, inner_pat, post_pats) => {
- pats.extend(pre_pats.iter());
- pats.extend(inner_pat.iter());
- pats.extend(post_pats.iter());
- }
- _ => {}
- }
- }
-
- pat.each_binding(|_, hir_id, _, ident| {
- ir.add_live_node_for_node(hir_id, VarDefNode(ident.span));
- ir.add_variable(Local(LocalInfo {
- id: hir_id,
- name: ident.name,
- is_shorthand: shorthand_field_ids.contains(&hir_id)
- }));
- });
-}
-
-fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local) {
- add_from_pat(ir, &local.pat);
- intravisit::walk_local(ir, local);
-}
-
-fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm) {
- add_from_pat(ir, &arm.pat);
- intravisit::walk_arm(ir, arm);
-}
-
-fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr) {
- match expr.kind {
- // live nodes required for uses or definitions of variables:
- hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
- debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
- if let Res::Local(var_hir_id) = path.res {
- let upvars = ir.tcx.upvars(ir.body_owner);
- if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
- ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
- }
- }
- intravisit::walk_expr(ir, expr);
- }
- hir::ExprKind::Closure(..) => {
- // Interesting control flow (for loops can contain labeled
- // breaks or continues)
- ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
-
- // Make a live_node for each captured variable, with the span
- // being the location that the variable is used. This results
- // in better error messages than just pointing at the closure
- // construction site.
- let mut call_caps = Vec::new();
- let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
- if let Some(upvars) = ir.tcx.upvars(closure_def_id) {
- let parent_upvars = ir.tcx.upvars(ir.body_owner);
- call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| {
- let has_parent = parent_upvars
- .map_or(false, |upvars| upvars.contains_key(&var_id));
- if !has_parent {
- let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
- Some(CaptureInfo { ln: upvar_ln, var_hid: var_id })
- } else {
- None
- }
- }));
- }
- ir.set_captures(expr.hir_id, call_caps);
- let old_body_owner = ir.body_owner;
- ir.body_owner = closure_def_id;
- intravisit::walk_expr(ir, expr);
- ir.body_owner = old_body_owner;
- }
-
- // live nodes required for interesting control flow:
- hir::ExprKind::Match(..) |
- hir::ExprKind::Loop(..) => {
- ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
- intravisit::walk_expr(ir, expr);
- }
- hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => {
- ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
- intravisit::walk_expr(ir, expr);
- }
-
- // otherwise, live nodes are not required:
- hir::ExprKind::Index(..) |
- hir::ExprKind::Field(..) |
- hir::ExprKind::Array(..) |
- hir::ExprKind::Call(..) |
- hir::ExprKind::MethodCall(..) |
- hir::ExprKind::Tup(..) |
- hir::ExprKind::Binary(..) |
- hir::ExprKind::AddrOf(..) |
- hir::ExprKind::Cast(..) |
- hir::ExprKind::DropTemps(..) |
- hir::ExprKind::Unary(..) |
- hir::ExprKind::Break(..) |
- hir::ExprKind::Continue(_) |
- hir::ExprKind::Lit(_) |
- hir::ExprKind::Ret(..) |
- hir::ExprKind::Block(..) |
- hir::ExprKind::Assign(..) |
- hir::ExprKind::AssignOp(..) |
- hir::ExprKind::Struct(..) |
- hir::ExprKind::Repeat(..) |
- hir::ExprKind::InlineAsm(..) |
- hir::ExprKind::Box(..) |
- hir::ExprKind::Yield(..) |
- hir::ExprKind::Type(..) |
- hir::ExprKind::Err |
- hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => {
- intravisit::walk_expr(ir, expr);
- }
- }
-}
-
-// ______________________________________________________________________
-// Computing liveness sets
-//
-// Actually we compute just a bit more than just liveness, but we use
-// the same basic propagation framework in all cases.
-
-#[derive(Clone, Copy)]
-struct RWU {
- reader: LiveNode,
- writer: LiveNode,
- used: bool
-}
-
-/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
-/// very large, so it uses a more compact representation that takes advantage
-/// of the fact that when the number of `RWU`s is large, most of them have an
-/// invalid reader and an invalid writer.
-struct RWUTable {
- /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
- /// an index into `unpacked_rwus`. In the common cases, this compacts the
- /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
- /// in 96.
- ///
- /// More compact representations are possible -- e.g., use only 2 bits per
- /// packed `RWU` and make the secondary table a HashMap that maps from
- /// indices to `RWU`s -- but this one strikes a good balance between size
- /// and speed.
- packed_rwus: Vec<u32>,
- unpacked_rwus: Vec<RWU>,
-}
-
-// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`.
-const INV_INV_FALSE: u32 = u32::MAX;
-
-// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`.
-const INV_INV_TRUE: u32 = u32::MAX - 1;
-
-impl RWUTable {
- fn new(num_rwus: usize) -> RWUTable {
- Self {
- packed_rwus: vec![INV_INV_FALSE; num_rwus],
- unpacked_rwus: vec![],
- }
- }
-
- fn get(&self, idx: usize) -> RWU {
- let packed_rwu = self.packed_rwus[idx];
- match packed_rwu {
- INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false },
- INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true },
- _ => self.unpacked_rwus[packed_rwu as usize],
- }
- }
-
- fn get_reader(&self, idx: usize) -> LiveNode {
- let packed_rwu = self.packed_rwus[idx];
- match packed_rwu {
- INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
- _ => self.unpacked_rwus[packed_rwu as usize].reader,
- }
- }
-
- fn get_writer(&self, idx: usize) -> LiveNode {
- let packed_rwu = self.packed_rwus[idx];
- match packed_rwu {
- INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
- _ => self.unpacked_rwus[packed_rwu as usize].writer,
- }
- }
-
- fn get_used(&self, idx: usize) -> bool {
- let packed_rwu = self.packed_rwus[idx];
- match packed_rwu {
- INV_INV_FALSE => false,
- INV_INV_TRUE => true,
- _ => self.unpacked_rwus[packed_rwu as usize].used,
- }
- }
-
- #[inline]
- fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) {
- self.packed_rwus[dst_idx] = self.packed_rwus[src_idx];
- }
-
- fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
- if rwu.reader == invalid_node() && rwu.writer == invalid_node() {
- // When we overwrite an indexing entry in `self.packed_rwus` with
- // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
- // from `self.unpacked_rwus`; it's not worth the effort, and we
- // can't have entries shifting around anyway.
- self.packed_rwus[idx] = if rwu.used {
- INV_INV_TRUE
- } else {
- INV_INV_FALSE
- }
- } else {
- // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
- // point to it.
- self.packed_rwus[idx] = self.unpacked_rwus.len() as u32;
- self.unpacked_rwus.push(rwu);
- }
- }
-
- fn assign_inv_inv(&mut self, idx: usize) {
- self.packed_rwus[idx] = if self.get_used(idx) {
- INV_INV_TRUE
- } else {
- INV_INV_FALSE
- };
- }
-}
-
-#[derive(Copy, Clone)]
-struct Specials {
- exit_ln: LiveNode,
- fallthrough_ln: LiveNode,
- clean_exit_var: Variable
-}
-
-const ACC_READ: u32 = 1;
-const ACC_WRITE: u32 = 2;
-const ACC_USE: u32 = 4;
-
-struct Liveness<'a, 'tcx> {
- ir: &'a mut IrMaps<'tcx>,
- tables: &'a ty::TypeckTables<'tcx>,
- s: Specials,
- successors: Vec<LiveNode>,
- rwu_table: RWUTable,
-
- // mappings from loop node ID to LiveNode
- // ("break" label should map to loop node ID,
- // it probably doesn't now)
- break_ln: HirIdMap<LiveNode>,
- cont_ln: HirIdMap<LiveNode>,
-}
-
-impl<'a, 'tcx> Liveness<'a, 'tcx> {
- fn new(ir: &'a mut IrMaps<'tcx>, body: hir::BodyId) -> Liveness<'a, 'tcx> {
- // Special nodes and variables:
- // - exit_ln represents the end of the fn, either by return or panic
- // - implicit_ret_var is a pseudo-variable that represents
- // an implicit return
- let specials = Specials {
- exit_ln: ir.add_live_node(ExitNode),
- fallthrough_ln: ir.add_live_node(ExitNode),
- clean_exit_var: ir.add_variable(CleanExit)
- };
-
- let tables = ir.tcx.body_tables(body);
-
- let num_live_nodes = ir.num_live_nodes;
- let num_vars = ir.num_vars;
-
- Liveness {
- ir,
- tables,
- s: specials,
- successors: vec![invalid_node(); num_live_nodes],
- rwu_table: RWUTable::new(num_live_nodes * num_vars),
- break_ln: Default::default(),
- cont_ln: Default::default(),
- }
- }
-
- fn live_node(&self, hir_id: HirId, span: Span) -> LiveNode {
- match self.ir.live_node_map.get(&hir_id) {
- Some(&ln) => ln,
- None => {
- // This must be a mismatch between the ir_map construction
- // above and the propagation code below; the two sets of
- // code have to agree about which AST nodes are worth
- // creating liveness nodes for.
- span_bug!(
- span,
- "no live node registered for node {:?}",
- hir_id);
- }
- }
- }
-
- fn variable(&self, hir_id: HirId, span: Span) -> Variable {
- self.ir.variable(hir_id, span)
- }
-
- fn define_bindings_in_pat(&mut self, pat: &hir::Pat, mut succ: LiveNode) -> LiveNode {
- // In an or-pattern, only consider the first pattern; any later patterns
- // must have the same bindings, and we also consider the first pattern
- // to be the "authoritative" set of ids.
- pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
- let ln = self.live_node(hir_id, pat_sp);
- let var = self.variable(hir_id, ident.span);
- self.init_from_succ(ln, succ);
- self.define(ln, var);
- succ = ln;
- });
- succ
- }
-
- fn idx(&self, ln: LiveNode, var: Variable) -> usize {
- ln.get() * self.ir.num_vars + var.get()
- }
-
- fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
- assert!(ln.is_valid());
- let reader = self.rwu_table.get_reader(self.idx(ln, var));
- if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None }
- }
-
- // Is this variable live on entry to any of its successor nodes?
- fn live_on_exit(&self, ln: LiveNode, var: Variable)
- -> Option<LiveNodeKind> {
- let successor = self.successors[ln.get()];
- self.live_on_entry(successor, var)
- }
-
- fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
- assert!(ln.is_valid());
- self.rwu_table.get_used(self.idx(ln, var))
- }
-
- fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
- -> Option<LiveNodeKind> {
- assert!(ln.is_valid());
- let writer = self.rwu_table.get_writer(self.idx(ln, var));
- if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None }
- }
-
- fn assigned_on_exit(&self, ln: LiveNode, var: Variable)
- -> Option<LiveNodeKind> {
- let successor = self.successors[ln.get()];
- self.assigned_on_entry(successor, var)
- }
-
- fn indices2<F>(&mut self, ln: LiveNode, succ_ln: LiveNode, mut op: F) where
- F: FnMut(&mut Liveness<'a, 'tcx>, usize, usize),
- {
- let node_base_idx = self.idx(ln, Variable(0));
- let succ_base_idx = self.idx(succ_ln, Variable(0));
- for var_idx in 0..self.ir.num_vars {
- op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
- }
- }
-
- fn write_vars<F>(&self,
- wr: &mut dyn Write,
- ln: LiveNode,
- mut test: F)
- -> io::Result<()> where
- F: FnMut(usize) -> LiveNode,
- {
- let node_base_idx = self.idx(ln, Variable(0));
- for var_idx in 0..self.ir.num_vars {
- let idx = node_base_idx + var_idx;
- if test(idx).is_valid() {
- write!(wr, " {:?}", Variable(var_idx as u32))?;
- }
- }
- Ok(())
- }
-
-
- #[allow(unused_must_use)]
- fn ln_str(&self, ln: LiveNode) -> String {
- let mut wr = Vec::new();
- {
- let wr = &mut wr as &mut dyn Write;
- write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
- self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
- write!(wr, " writes");
- self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
- write!(wr, " precedes {:?}]", self.successors[ln.get()]);
- }
- String::from_utf8(wr).unwrap()
- }
-
- fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
- self.successors[ln.get()] = succ_ln;
-
- // It is not necessary to initialize the RWUs here because they are all
- // set to INV_INV_FALSE when they are created, and the sets only grow
- // during iterations.
- }
-
- fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
- // more efficient version of init_empty() / merge_from_succ()
- self.successors[ln.get()] = succ_ln;
-
- self.indices2(ln, succ_ln, |this, idx, succ_idx| {
- this.rwu_table.copy_packed(idx, succ_idx);
- });
- debug!("init_from_succ(ln={}, succ={})",
- self.ln_str(ln), self.ln_str(succ_ln));
- }
-
- fn merge_from_succ(&mut self,
- ln: LiveNode,
- succ_ln: LiveNode,
- first_merge: bool)
- -> bool {
- if ln == succ_ln { return false; }
-
- let mut changed = false;
- self.indices2(ln, succ_ln, |this, idx, succ_idx| {
- let mut rwu = this.rwu_table.get(idx);
- let succ_rwu = this.rwu_table.get(succ_idx);
- if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() {
- rwu.reader = succ_rwu.reader;
- changed = true
- }
-
- if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() {
- rwu.writer = succ_rwu.writer;
- changed = true
- }
-
- if succ_rwu.used && !rwu.used {
- rwu.used = true;
- changed = true;
- }
-
- if changed {
- this.rwu_table.assign_unpacked(idx, rwu);
- }
- });
-
- debug!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
- ln, self.ln_str(succ_ln), first_merge, changed);
- return changed;
- }
-
- // Indicates that a local variable was *defined*; we know that no
- // uses of the variable can precede the definition (resolve checks
- // this) so we just clear out all the data.
- fn define(&mut self, writer: LiveNode, var: Variable) {
- let idx = self.idx(writer, var);
- self.rwu_table.assign_inv_inv(idx);
-
- debug!("{:?} defines {:?} (idx={}): {}", writer, var,
- idx, self.ln_str(writer));
- }
-
- // Either read, write, or both depending on the acc bitset
- fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) {
- debug!("{:?} accesses[{:x}] {:?}: {}",
- ln, acc, var, self.ln_str(ln));
-
- let idx = self.idx(ln, var);
- let mut rwu = self.rwu_table.get(idx);
-
- if (acc & ACC_WRITE) != 0 {
- rwu.reader = invalid_node();
- rwu.writer = ln;
- }
-
- // Important: if we both read/write, must do read second
- // or else the write will override.
- if (acc & ACC_READ) != 0 {
- rwu.reader = ln;
- }
-
- if (acc & ACC_USE) != 0 {
- rwu.used = true;
- }
-
- self.rwu_table.assign_unpacked(idx, rwu);
- }
-
- fn compute(&mut self, body: &hir::Expr) -> LiveNode {
- debug!("compute: using id for body, {}",
- self.ir.tcx.hir().hir_to_pretty_string(body.hir_id));
-
- // the fallthrough exit is only for those cases where we do not
- // explicitly return:
- let s = self.s;
- self.init_from_succ(s.fallthrough_ln, s.exit_ln);
- self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
-
- let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
-
- // hack to skip the loop unless debug! is enabled:
- debug!("^^ liveness computation results for body {} (entry={:?})", {
- for ln_idx in 0..self.ir.num_live_nodes {
- debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
- }
- body.hir_id
- },
- entry_ln);
-
- entry_ln
- }
-
- fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
- -> LiveNode {
- if blk.targeted_by_break {
- self.break_ln.insert(blk.hir_id, succ);
- }
- let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
- blk.stmts.iter().rev().fold(succ, |succ, stmt| {
- self.propagate_through_stmt(stmt, succ)
- })
- }
-
- fn propagate_through_stmt(&mut self, stmt: &hir::Stmt, succ: LiveNode)
- -> LiveNode {
- match stmt.kind {
- hir::StmtKind::Local(ref local) => {
- // Note: we mark the variable as defined regardless of whether
- // there is an initializer. Initially I had thought to only mark
- // the live variable as defined if it was initialized, and then we
- // could check for uninit variables just by scanning what is live
- // at the start of the function. But that doesn't work so well for
- // immutable variables defined in a loop:
- // loop { let x; x = 5; }
- // because the "assignment" loops back around and generates an error.
- //
- // So now we just check that variables defined w/o an
- // initializer are not live at the point of their
- // initialization, which is mildly more complex than checking
- // once at the func header but otherwise equivalent.
-
- let succ = self.propagate_through_opt_expr(local.init.as_ref().map(|e| &**e), succ);
- self.define_bindings_in_pat(&local.pat, succ)
- }
- hir::StmtKind::Item(..) => succ,
- hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
- self.propagate_through_expr(&expr, succ)
- }
- }
- }
-
- fn propagate_through_exprs(&mut self, exprs: &[Expr], succ: LiveNode)
- -> LiveNode {
- exprs.iter().rev().fold(succ, |succ, expr| {
- self.propagate_through_expr(&expr, succ)
- })
- }
-
- fn propagate_through_opt_expr(&mut self,
- opt_expr: Option<&Expr>,
- succ: LiveNode)
- -> LiveNode {
- opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
- }
-
- fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
- -> LiveNode {
- debug!("propagate_through_expr: {}", self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id));
-
- match expr.kind {
- // Interesting cases with control flow or which gen/kill
- hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
- self.access_path(expr.hir_id, path, succ, ACC_READ | ACC_USE)
- }
-
- hir::ExprKind::Field(ref e, _) => {
- self.propagate_through_expr(&e, succ)
- }
-
- hir::ExprKind::Closure(..) => {
- debug!("{} is an ExprKind::Closure",
- self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id));
-
- // the construction of a closure itself is not important,
- // but we have to consider the closed over variables.
- let caps = self.ir.capture_info_map.get(&expr.hir_id).cloned().unwrap_or_else(||
- span_bug!(expr.span, "no registered caps"));
-
- caps.iter().rev().fold(succ, |succ, cap| {
- self.init_from_succ(cap.ln, succ);
- let var = self.variable(cap.var_hid, expr.span);
- self.acc(cap.ln, var, ACC_READ | ACC_USE);
- cap.ln
- })
- }
-
- // Note that labels have been resolved, so we don't need to look
- // at the label ident
- hir::ExprKind::Loop(ref blk, _, _) => {
- self.propagate_through_loop(expr, &blk, succ)
- }
-
- hir::ExprKind::Match(ref e, ref arms, _) => {
- //
- // (e)
- // |
- // v
- // (expr)
- // / | \
- // | | |
- // v v v
- // (..arms..)
- // | | |
- // v v v
- // ( succ )
- //
- //
- let ln = self.live_node(expr.hir_id, expr.span);
- self.init_empty(ln, succ);
- let mut first_merge = true;
- for arm in arms {
- let body_succ = self.propagate_through_expr(&arm.body, succ);
-
- let guard_succ = self.propagate_through_opt_expr(
- arm.guard.as_ref().map(|hir::Guard::If(e)| &**e),
- body_succ
- );
- let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
- self.merge_from_succ(ln, arm_succ, first_merge);
- first_merge = false;
- };
- self.propagate_through_expr(&e, ln)
- }
-
- hir::ExprKind::Ret(ref o_e) => {
- // ignore succ and subst exit_ln:
- let exit_ln = self.s.exit_ln;
- self.propagate_through_opt_expr(o_e.as_ref().map(|e| &**e), exit_ln)
- }
-
- hir::ExprKind::Break(label, ref opt_expr) => {
- // Find which label this break jumps to
- let target = match label.target_id {
- Ok(hir_id) => self.break_ln.get(&hir_id),
- Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
- }.cloned();
-
- // Now that we know the label we're going to,
- // look it up in the break loop nodes table
-
- match target {
- Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
- None => span_bug!(expr.span, "break to unknown label")
- }
- }
-
- hir::ExprKind::Continue(label) => {
- // Find which label this expr continues to
- let sc = label.target_id.unwrap_or_else(|err|
- span_bug!(expr.span, "loop scope error: {}", err));
-
- // Now that we know the label we're going to,
- // look it up in the continue loop nodes table
- self.cont_ln.get(&sc).cloned().unwrap_or_else(||
- span_bug!(expr.span, "continue to unknown label"))
- }
-
- hir::ExprKind::Assign(ref l, ref r) => {
- // see comment on places in
- // propagate_through_place_components()
- let succ = self.write_place(&l, succ, ACC_WRITE);
- let succ = self.propagate_through_place_components(&l, succ);
- self.propagate_through_expr(&r, succ)
- }
-
- hir::ExprKind::AssignOp(_, ref l, ref r) => {
- // an overloaded assign op is like a method call
- if self.tables.is_method_call(expr) {
- let succ = self.propagate_through_expr(&l, succ);
- self.propagate_through_expr(&r, succ)
- } else {
- // see comment on places in
- // propagate_through_place_components()
- let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ);
- let succ = self.propagate_through_expr(&r, succ);
- self.propagate_through_place_components(&l, succ)
- }
- }
-
- // Uninteresting cases: just propagate in rev exec order
-
- hir::ExprKind::Array(ref exprs) => {
- self.propagate_through_exprs(exprs, succ)
- }
-
- hir::ExprKind::Struct(_, ref fields, ref with_expr) => {
- let succ = self.propagate_through_opt_expr(with_expr.as_ref().map(|e| &**e), succ);
- fields.iter().rev().fold(succ, |succ, field| {
- self.propagate_through_expr(&field.expr, succ)
- })
- }
-
- hir::ExprKind::Call(ref f, ref args) => {
- let m = self.ir.tcx.hir().get_module_parent(expr.hir_id);
- let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
- self.s.exit_ln
- } else {
- succ
- };
- let succ = self.propagate_through_exprs(args, succ);
- self.propagate_through_expr(&f, succ)
- }
-
- hir::ExprKind::MethodCall(.., ref args) => {
- let m = self.ir.tcx.hir().get_module_parent(expr.hir_id);
- let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
- self.s.exit_ln
- } else {
- succ
- };
-
- self.propagate_through_exprs(args, succ)
- }
-
- hir::ExprKind::Tup(ref exprs) => {
- self.propagate_through_exprs(exprs, succ)
- }
-
- hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => {
- let r_succ = self.propagate_through_expr(&r, succ);
-
- let ln = self.live_node(expr.hir_id, expr.span);
- self.init_from_succ(ln, succ);
- self.merge_from_succ(ln, r_succ, false);
-
- self.propagate_through_expr(&l, ln)
- }
-
- hir::ExprKind::Index(ref l, ref r) |
- hir::ExprKind::Binary(_, ref l, ref r) => {
- let r_succ = self.propagate_through_expr(&r, succ);
- self.propagate_through_expr(&l, r_succ)
- }
-
- hir::ExprKind::Box(ref e) |
- hir::ExprKind::AddrOf(_, ref e) |
- hir::ExprKind::Cast(ref e, _) |
- hir::ExprKind::Type(ref e, _) |
- hir::ExprKind::DropTemps(ref e) |
- hir::ExprKind::Unary(_, ref e) |
- hir::ExprKind::Yield(ref e, _) |
- hir::ExprKind::Repeat(ref e, _) => {
- self.propagate_through_expr(&e, succ)
- }
-
- hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
- let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
- // see comment on places
- // in propagate_through_place_components()
- if o.is_indirect {
- self.propagate_through_expr(output, succ)
- } else {
- let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
- let succ = self.write_place(output, succ, acc);
- self.propagate_through_place_components(output, succ)
- }});
-
- // Inputs are executed first. Propagate last because of rev order
- self.propagate_through_exprs(inputs, succ)
- }
-
- hir::ExprKind::Lit(..) | hir::ExprKind::Err |
- hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => {
- succ
- }
-
- // Note that labels have been resolved, so we don't need to look
- // at the label ident
- hir::ExprKind::Block(ref blk, _) => {
- self.propagate_through_block(&blk, succ)
- }
- }
- }
-
- fn propagate_through_place_components(&mut self,
- expr: &Expr,
- succ: LiveNode)
- -> LiveNode {
- // # Places
- //
- // In general, the full flow graph structure for an
- // assignment/move/etc can be handled in one of two ways,
- // depending on whether what is being assigned is a "tracked
- // value" or not. A tracked value is basically a local
- // variable or argument.
- //
- // The two kinds of graphs are:
- //
- // Tracked place Untracked place
- // ----------------------++-----------------------
- // ||
- // | || |
- // v || v
- // (rvalue) || (rvalue)
- // | || |
- // v || v
- // (write of place) || (place components)
- // | || |
- // v || v
- // (succ) || (succ)
- // ||
- // ----------------------++-----------------------
- //
- // I will cover the two cases in turn:
- //
- // # Tracked places
- //
- // A tracked place is a local variable/argument `x`. In
- // these cases, the link_node where the write occurs is linked
- // to node id of `x`. The `write_place()` routine generates
- // the contents of this node. There are no subcomponents to
- // consider.
- //
- // # Non-tracked places
- //
- // These are places like `x[5]` or `x.f`. In that case, we
- // basically ignore the value which is written to but generate
- // reads for the components---`x` in these two examples. The
- // components reads are generated by
- // `propagate_through_place_components()` (this fn).
- //
- // # Illegal places
- //
- // It is still possible to observe assignments to non-places;
- // these errors are detected in the later pass borrowck. We
- // just ignore such cases and treat them as reads.
-
- match expr.kind {
- hir::ExprKind::Path(_) => succ,
- hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ),
- _ => self.propagate_through_expr(expr, succ)
- }
- }
-
- // see comment on propagate_through_place()
- fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32) -> LiveNode {
- match expr.kind {
- hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
- self.access_path(expr.hir_id, path, succ, acc)
- }
-
- // We do not track other places, so just propagate through
- // to their subcomponents. Also, it may happen that
- // non-places occur here, because those are detected in the
- // later pass borrowck.
- _ => succ
- }
- }
-
- fn access_var(&mut self, hir_id: HirId, var_hid: HirId, succ: LiveNode, acc: u32, span: Span)
- -> LiveNode {
- let ln = self.live_node(hir_id, span);
- if acc != 0 {
- self.init_from_succ(ln, succ);
- let var = self.variable(var_hid, span);
- self.acc(ln, var, acc);
- }
- ln
- }
-
- fn access_path(&mut self, hir_id: HirId, path: &hir::Path, succ: LiveNode, acc: u32)
- -> LiveNode {
- match path.res {
- Res::Local(hid) => {
- let upvars = self.ir.tcx.upvars(self.ir.body_owner);
- if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) {
- self.access_var(hir_id, hid, succ, acc, path.span)
- } else {
- succ
- }
- }
- _ => succ
- }
- }
-
- fn propagate_through_loop(
- &mut self,
- expr: &Expr,
- body: &hir::Block,
- succ: LiveNode
- ) -> LiveNode {
- /*
- We model control flow like this:
-
- (expr) <-+
- | |
- v |
- (body) --+
-
- Note that a `continue` expression targeting the `loop` will have a successor of `expr`.
- Meanwhile, a `break` expression will have a successor of `succ`.
- */
-
- // first iteration:
- let mut first_merge = true;
- let ln = self.live_node(expr.hir_id, expr.span);
- self.init_empty(ln, succ);
- debug!("propagate_through_loop: using id for loop body {} {}",
- expr.hir_id, self.ir.tcx.hir().hir_to_pretty_string(body.hir_id));
-
- self.break_ln.insert(expr.hir_id, succ);
-
- self.cont_ln.insert(expr.hir_id, ln);
-
- let body_ln = self.propagate_through_block(body, ln);
-
- // repeat until fixed point is reached:
- while self.merge_from_succ(ln, body_ln, first_merge) {
- first_merge = false;
- assert_eq!(body_ln, self.propagate_through_block(body, ln));
- }
-
- ln
- }
-}
-
-// _______________________________________________________________________
-// Checking for error conditions
-
-impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
- NestedVisitorMap::None
- }
-
- fn visit_local(&mut self, local: &'tcx hir::Local) {
- self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
- if local.init.is_some() {
- self.warn_about_dead_assign(spans, hir_id, ln, var);
- }
- });
-
- intravisit::walk_local(self, local);
- }
-
- fn visit_expr(&mut self, ex: &'tcx Expr) {
- check_expr(self, ex);
- }
-
- fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
- self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
- intravisit::walk_arm(self, arm);
- }
-}
-
-fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
- match expr.kind {
- hir::ExprKind::Assign(ref l, _) => {
- this.check_place(&l);
- }
-
- hir::ExprKind::AssignOp(_, ref l, _) => {
- if !this.tables.is_method_call(expr) {
- this.check_place(&l);
- }
- }
-
- hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
- for input in inputs {
- this.visit_expr(input);
- }
-
- // Output operands must be places
- for (o, output) in ia.outputs.iter().zip(outputs) {
- if !o.is_indirect {
- this.check_place(output);
- }
- this.visit_expr(output);
- }
- }
-
- // no correctness conditions related to liveness
- hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) |
- hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) |
- hir::ExprKind::Index(..) | hir::ExprKind::Field(..) |
- hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) |
- hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) |
- hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) |
- hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) |
- hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
- hir::ExprKind::Closure(..) | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) |
- hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {}
- }
-
- intravisit::walk_expr(this, expr);
-}
-
-impl<'tcx> Liveness<'_, 'tcx> {
- fn check_place(&mut self, expr: &'tcx Expr) {
- match expr.kind {
- hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
- if let Res::Local(var_hid) = path.res {
- let upvars = self.ir.tcx.upvars(self.ir.body_owner);
- if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) {
- // Assignment to an immutable variable or argument: only legal
- // if there is no later assignment. If this local is actually
- // mutable, then check for a reassignment to flag the mutability
- // as being used.
- let ln = self.live_node(expr.hir_id, expr.span);
- let var = self.variable(var_hid, expr.span);
- self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
- }
- }
- }
- _ => {
- // For other kinds of places, no checks are required,
- // and any embedded expressions are actually rvalues
- intravisit::walk_expr(self, expr);
- }
- }
- }
-
- fn should_warn(&self, var: Variable) -> Option<String> {
- let name = self.ir.variable_name(var);
- if name.is_empty() || name.as_bytes()[0] == b'_' {
- None
- } else {
- Some(name)
- }
- }
-
- fn warn_about_unused_args(&self, body: &hir::Body, entry_ln: LiveNode) {
- for p in &body.params {
- self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
- if self.live_on_entry(ln, var).is_none() {
- self.report_dead_assign(hir_id, spans, var, true);
- }
- });
- }
- }
-
- fn check_unused_vars_in_pat(
- &self,
- pat: &hir::Pat,
- entry_ln: Option<LiveNode>,
- on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
- ) {
- // In an or-pattern, only consider the variable; any later patterns must have the same
- // bindings, and we also consider the first pattern to be the "authoritative" set of ids.
- // However, we should take the spans of variables with the same name from the later
- // patterns so the suggestions to prefix with underscores will apply to those too.
- let mut vars: FxIndexMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = <_>::default();
-
- pat.each_binding(|_, hir_id, pat_sp, ident| {
- let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp));
- let var = self.variable(hir_id, ident.span);
- vars.entry(self.ir.variable_name(var))
- .and_modify(|(.., spans)| spans.push(ident.span))
- .or_insert_with(|| (ln, var, hir_id, vec![ident.span]));
- });
-
- for (_, (ln, var, id, spans)) in vars {
- if self.used_on_entry(ln, var) {
- on_used_on_entry(spans, id, ln, var);
- } else {
- self.report_unused(spans, id, ln, var);
- }
- }
- }
-
- fn report_unused(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
- if let Some(name) = self.should_warn(var).filter(|name| name != "self") {
- // annoying: for parameters in funcs like `fn(x: i32)
- // {ret}`, there is only one node, so asking about
- // assigned_on_exit() is not meaningful.
- let is_assigned = if ln == self.s.exit_ln {
- false
- } else {
- self.assigned_on_exit(ln, var).is_some()
- };
-
- if is_assigned {
- self.ir.tcx.lint_hir_note(
- lint::builtin::UNUSED_VARIABLES,
- hir_id,
- spans,
- &format!("variable `{}` is assigned to, but never used", name),
- &format!("consider using `_{}` instead", name),
- );
- } else {
- let mut err = self.ir.tcx.struct_span_lint_hir(
- lint::builtin::UNUSED_VARIABLES,
- hir_id,
- spans.clone(),
- &format!("unused variable: `{}`", name),
- );
-
- if self.ir.variable_is_shorthand(var) {
- if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
- // Handle `ref` and `ref mut`.
- let spans = spans.iter()
- .map(|_span| (pat.span, format!("{}: _", name)))
- .collect();
-
- err.multipart_suggestion(
- "try ignoring the field",
- spans,
- Applicability::MachineApplicable,
- );
- }
- } else {
- err.multipart_suggestion(
- "consider prefixing with an underscore",
- spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
- Applicability::MachineApplicable,
- );
- }
-
- err.emit()
- }
- }
- }
-
- fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
- if self.live_on_exit(ln, var).is_none() {
- self.report_dead_assign(hir_id, spans, var, false);
- }
- }
-
- fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
- if let Some(name) = self.should_warn(var) {
- if is_param {
- self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
- &format!("value passed to `{}` is never read", name))
- .help("maybe it is overwritten before being read?")
- .emit();
- } else {
- self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
- &format!("value assigned to `{}` is never read", name))
- .help("maybe it is overwritten before being read?")
- .emit();
- }
- }
- }
-}
}
}
+ #[inline(always)]
+ pub fn check_raw(data: u128, size: u8, target_size: Size) {
+ assert_eq!(target_size.bytes(), size as u64);
+ assert_ne!(size, 0, "you should never look at the bits of a ZST");
+ Scalar::check_data(data, size);
+ }
+
/// Do not call this method! Use either `assert_bits` or `force_bits`.
#[inline]
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
match self {
Scalar::Raw { data, size } => {
- assert_eq!(target_size.bytes(), size as u64);
- assert_ne!(size, 0, "you should never look at the bits of a ZST");
- Scalar::check_data(data, size);
+ Self::check_raw(data, size, target_size);
Ok(data)
}
Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes),
use crate::ty::print::{FmtPrinter, Printer};
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{
- self, AdtDef, CanonicalUserTypeAnnotations, GeneratorSubsts, Region, Ty, TyCtxt,
+ self, AdtDef, CanonicalUserTypeAnnotations, Region, Ty, TyCtxt,
UserTypeAnnotationIndex,
};
Adt(&'tcx AdtDef, VariantIdx, SubstsRef<'tcx>, Option<UserTypeAnnotationIndex>, Option<usize>),
Closure(DefId, SubstsRef<'tcx>),
- Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),
+ Generator(DefId, SubstsRef<'tcx>, hir::GeneratorMovability),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)]
let ty = place.ty(local_decls, tcx).ty;
match ty.kind {
ty::Adt(adt_def, _) => adt_def.repr.discr_type().to_ty(tcx),
- ty::Generator(_, substs, _) => substs.discr_ty(tcx),
+ ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx),
_ => {
// This can only be `0`, for now, so `u8` will suffice.
tcx.types.u8
use crate::ty::subst::SubstsRef;
-use crate::ty::{CanonicalUserTypeAnnotation, GeneratorSubsts, Ty};
+use crate::ty::{CanonicalUserTypeAnnotation, Ty};
use crate::mir::*;
use syntax_pos::Span;
self.super_substs(substs);
}
- fn visit_generator_substs(&mut self,
- substs: & $($mutability)? GeneratorSubsts<'tcx>,
- _: Location) {
- self.super_generator_substs(substs);
- }
-
fn visit_local_decl(&mut self,
local: Local,
local_decl: & $($mutability)? LocalDecl<'tcx>) {
generator_substs,
_movability,
) => {
- self.visit_generator_substs(generator_substs, location);
+ self.visit_substs(generator_substs, location);
}
}
fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) {
}
- fn super_generator_substs(&mut self,
- _substs: & $($mutability)? GeneratorSubsts<'tcx>) {
- }
-
// Convenience methods
fn visit_location(&mut self, body: & $($mutability)? Body<'tcx>, location: Location) {
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"enable queries of the dependency graph for regression testing"),
- profile_queries: bool = (false, parse_bool, [UNTRACKED],
- "trace and profile the queries of the incremental compilation framework"),
- profile_queries_and_keys: bool = (false, parse_bool, [UNTRACKED],
- "trace and profile the queries and keys of the incremental compilation framework"),
no_analysis: bool = (false, parse_bool, [UNTRACKED],
"parse and expand the source, but run no analysis"),
extra_plugins: Vec<String> = (Vec::new(), parse_list, [TRACKED],
mir_opt_level: usize = (1, parse_uint, [TRACKED],
"set the MIR optimization level (0-3, default: 1)"),
mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED],
- "emit noalias metadata for mutable references (default: yes on LLVM >= 6)"),
+ "emit noalias metadata for mutable references (default: no)"),
dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
"dump MIR state to file.
`val` is used to select which passes and functions to dump. For example:
use crate::session::search_paths::{PathKind, SearchPath};
use crate::util::nodemap::{FxHashMap, FxHashSet};
use crate::util::common::{duration_to_secs_str, ErrorReported};
-use crate::util::common::ProfileQueriesMsg;
use rustc_data_structures::base_n;
use rustc_data_structures::sync::{
use std::io::Write;
use std::path::PathBuf;
use std::time::Duration;
-use std::sync::{Arc, mpsc};
+use std::sync::Arc;
mod code_stats;
pub mod config;
/// `-Zquery-dep-graph` is specified.
pub cgu_reuse_tracker: CguReuseTracker,
- /// Used by `-Z profile-queries` in `util::common`.
- pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
-
/// Used by `-Z self-profile`.
pub prof: SelfProfilerRef,
pub fn time_extended(&self) -> bool {
self.opts.debugging_opts.time_passes
}
- pub fn profile_queries(&self) -> bool {
- self.opts.debugging_opts.profile_queries
- || self.opts.debugging_opts.profile_queries_and_keys
- }
- pub fn profile_queries_and_keys(&self) -> bool {
- self.opts.debugging_opts.profile_queries_and_keys
- }
pub fn instrument_mcount(&self) -> bool {
self.opts.debugging_opts.instrument_mcount
}
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
cgu_reuse_tracker,
prof: SelfProfilerRef::new(self_profiler),
- profile_channel: Lock::new(None),
perf_stats: PerfStats {
symbol_hash_time: Lock::new(Duration::from_secs(0)),
decode_def_path_tables_time: Lock::new(Duration::from_secs(0)),
}
}
- fn find_similar_impl_candidates(&self,
- trait_ref: ty::PolyTraitRef<'tcx>)
- -> Vec<ty::TraitRef<'tcx>>
- {
- let simp = fast_reject::simplify_type(self.tcx,
- trait_ref.skip_binder().self_ty(),
- true);
+ fn find_similar_impl_candidates(
+ &self,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ ) -> Vec<ty::TraitRef<'tcx>> {
+ let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true);
let all_impls = self.tcx.all_impls(trait_ref.def_id());
match simp {
Some(simp) => all_impls.iter().filter_map(|&def_id| {
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
- let imp_simp = fast_reject::simplify_type(self.tcx,
- imp.self_ty(),
- true);
+ let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true);
if let Some(imp_simp) = imp_simp {
if simp != imp_simp {
return None
}
}
- fn report_similar_impl_candidates(&self,
- impl_candidates: Vec<ty::TraitRef<'tcx>>,
- err: &mut DiagnosticBuilder<'_>)
- {
+ fn report_similar_impl_candidates(
+ &self,
+ impl_candidates: Vec<ty::TraitRef<'tcx>>,
+ err: &mut DiagnosticBuilder<'_>,
+ ) {
if impl_candidates.is_empty() {
return;
}
// which is somewhat confusing.
err.help(&format!("consider adding a `where {}` bound",
trait_ref.to_predicate()));
- } else if !have_alt_message {
- // Can't show anything else useful, try to find similar impls.
- let impl_candidates = self.find_similar_impl_candidates(trait_ref);
- self.report_similar_impl_candidates(impl_candidates, &mut err);
+ } else {
+ if !have_alt_message {
+ // Can't show anything else useful, try to find similar impls.
+ let impl_candidates = self.find_similar_impl_candidates(trait_ref);
+ self.report_similar_impl_candidates(impl_candidates, &mut err);
+ }
+ self.suggest_change_mut(
+ &obligation,
+ &mut err,
+ &trait_ref,
+ points_at_arg,
+ );
}
// If this error is due to `!: Trait` not implemented but `(): Trait` is
let substs = self.tcx.mk_substs_trait(trait_type, &[]);
let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs);
- let new_obligation = Obligation::new(ObligationCause::dummy(),
- obligation.param_env,
- new_trait_ref.to_predicate());
+ let new_obligation = Obligation::new(
+ ObligationCause::dummy(),
+ obligation.param_env,
+ new_trait_ref.to_predicate(),
+ );
if self.predicate_may_hold(&new_obligation) {
let sp = self.tcx.sess.source_map()
}
}
+ /// Check if the trait bound is implemented for a different mutability and note it in the
+ /// final error.
+ fn suggest_change_mut(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut DiagnosticBuilder<'tcx>,
+ trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
+ points_at_arg: bool,
+ ) {
+ let span = obligation.cause.span;
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let refs_number = snippet.chars()
+ .filter(|c| !c.is_whitespace())
+ .take_while(|c| *c == '&')
+ .count();
+ if let Some('\'') = snippet.chars()
+ .filter(|c| !c.is_whitespace())
+ .skip(refs_number)
+ .next()
+ { // Do not suggest removal of borrow from type arguments.
+ return;
+ }
+ let trait_ref = self.resolve_vars_if_possible(trait_ref);
+ if trait_ref.has_infer_types() {
+ // Do not ICE while trying to find if a reborrow would succeed on a trait with
+ // unresolved bindings.
+ return;
+ }
+
+ if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind {
+ let trait_type = match mutability {
+ hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
+ hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
+ };
+
+ let substs = self.tcx.mk_substs_trait(&trait_type, &[]);
+ let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs);
+ let new_obligation = Obligation::new(
+ ObligationCause::dummy(),
+ obligation.param_env,
+ new_trait_ref.to_predicate(),
+ );
+
+ if self.evaluate_obligation_no_overflow(
+ &new_obligation,
+ ).must_apply_modulo_regions() {
+ let sp = self.tcx.sess.source_map()
+ .span_take_while(span, |c| c.is_whitespace() || *c == '&');
+ if points_at_arg &&
+ mutability == hir::Mutability::MutImmutable &&
+ refs_number > 0
+ {
+ err.span_suggestion(
+ sp,
+ "consider changing this borrow's mutability",
+ "&mut ".to_string(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.note(&format!(
+ "`{}` is implemented for `{:?}`, but not for `{:?}`",
+ trait_ref,
+ trait_type,
+ trait_ref.skip_binder().self_ty(),
+ ));
+ }
+ }
+ }
+ }
+ }
+
fn suggest_semicolon_removal(
&self,
obligation: &PredicateObligation<'tcx>,
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, HashStable)]
pub struct VtableGeneratorData<'tcx, N> {
pub generator_def_id: DefId,
- pub substs: ty::GeneratorSubsts<'tcx>,
+ pub substs: SubstsRef<'tcx>,
/// Nested obligations. This can be non-empty if the generator
/// signature contains associated types.
pub nested: Vec<N>
obligation: &ProjectionTyObligation<'tcx>,
vtable: VtableGeneratorData<'tcx, PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let gen_sig = vtable.substs.poly_sig(vtable.generator_def_id, selcx.tcx());
+ let gen_sig = vtable.substs.as_generator().poly_sig(vtable.generator_def_id, selcx.tcx());
let Normalized {
value: gen_sig,
obligations
if let Some(assoc_item) = trait_def
.ancestors(tcx, impl_def_id)
- .defs(tcx, assoc_ty_name, ty::AssocKind::Type, trait_def_id)
- .next() {
+ .leaf_def(tcx, assoc_ty_name, ty::AssocKind::Type) {
+
assoc_item
} else {
// This is saying that neither the trait nor
// Helper function that canonicalizes and runs the query. If an
// overflow results, we re-run it in the local context so we can
// report a nice error.
- fn evaluate_obligation_no_overflow(
+ crate fn evaluate_obligation_no_overflow(
&self,
obligation: &PredicateObligation<'tcx>,
) -> EvaluationResult {
.collect(),
ty::Generator(def_id, ref substs, _) => {
- let witness = substs.witness(def_id, self.tcx());
+ let witness = substs.as_generator().witness(def_id, self.tcx());
substs
+ .as_generator()
.upvar_tys(def_id, self.tcx())
.chain(iter::once(witness))
.collect()
// binder moved -\
let ty: ty::Binder<Ty<'tcx>> = ty::Binder::bind(ty); // <----/
- self.infcx.in_snapshot(|_| {
+ self.infcx.commit_unconditionally(|_| {
let (skol_ty, _) = self.infcx
.replace_bound_vars_with_placeholders(&ty);
let Normalized {
}
fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) {
- self.infcx.in_snapshot(|snapshot| {
+ self.infcx.commit_unconditionally(|snapshot| {
let result =
self.match_projection_obligation_against_definition_bounds(
obligation,
nested,
);
- let trait_obligations: Vec<PredicateObligation<'_>> = self.infcx.in_snapshot(|_| {
- let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
- let (trait_ref, _) = self.infcx
- .replace_bound_vars_with_placeholders(&poly_trait_ref);
- let cause = obligation.derived_cause(ImplDerivedObligation);
- self.impl_or_trait_obligations(
- cause,
- obligation.recursion_depth + 1,
- obligation.param_env,
- trait_def_id,
- &trait_ref.substs,
- )
- });
+ let trait_obligations: Vec<PredicateObligation<'_>> =
+ self.infcx.commit_unconditionally(|_| {
+ let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
+ let (trait_ref, _) = self.infcx
+ .replace_bound_vars_with_placeholders(&poly_trait_ref);
+ let cause = obligation.derived_cause(ImplDerivedObligation);
+ self.impl_or_trait_obligations(
+ cause,
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ trait_def_id,
+ &trait_ref.substs,
+ )
+ });
// Adds the predicates from the trait. Note that this contains a `Self: Trait`
// predicate as usual. It won't have any effect since auto traits are coinductive.
// First, create the substitutions by matching the impl again,
// this time not in a probe.
- self.infcx.in_snapshot(|snapshot| {
+ self.infcx.commit_unconditionally(|snapshot| {
let substs = self.rematch_impl(impl_def_id, obligation, snapshot);
debug!("confirm_impl_candidate: substs={:?}", substs);
let cause = obligation.derived_cause(ImplDerivedObligation);
obligation, alias_def_id
);
- self.infcx.in_snapshot(|_| {
+ self.infcx.commit_unconditionally(|_| {
let (predicate, _) = self.infcx()
.replace_bound_vars_with_placeholders(&obligation.predicate);
let trait_ref = predicate.trait_ref;
)?);
Ok(VtableGeneratorData {
- generator_def_id: generator_def_id,
- substs: substs.clone(),
+ generator_def_id,
+ substs,
nested: obligations,
})
}
&mut self,
obligation: &TraitObligation<'tcx>,
closure_def_id: DefId,
- substs: ty::GeneratorSubsts<'tcx>,
+ substs: SubstsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
- let gen_sig = substs.poly_sig(closure_def_id, self.tcx());
+ let gen_sig = substs.as_generator().poly_sig(closure_def_id, self.tcx());
// (1) Feels icky to skip the binder here, but OTOH we know
// that the self-type is an generator type and hence is
let trait_def = tcx.trait_def(trait_def_id);
let ancestors = trait_def.ancestors(tcx, impl_data.impl_def_id);
- match ancestors.defs(tcx, item.ident, item.kind, trait_def_id).next() {
+ match ancestors.leaf_def(tcx, item.ident, item.kind) {
Some(node_item) => {
let substs = tcx.infer_ctxt().enter(|infcx| {
let param_env = param_env.with_reveal_all();
use crate::ty::{self, TyCtxt, TypeFoldable};
use crate::ty::fast_reject::{self, SimplifiedType};
use syntax::ast::Ident;
-use crate::util::captures::Captures;
use crate::util::nodemap::{DefIdMap, FxHashMap};
/// A per-trait graph of impls in specialization order. At the moment, this
tcx.associated_items(self.def_id())
}
+ /// Finds an associated item defined in this node.
+ ///
+ /// If this returns `None`, the item can potentially still be found in
+ /// parents of this node.
+ pub fn item(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ trait_item_name: Ident,
+ trait_item_kind: ty::AssocKind,
+ trait_def_id: DefId,
+ ) -> Option<ty::AssocItem> {
+ use crate::ty::AssocKind::*;
+
+ tcx.associated_items(self.def_id())
+ .find(move |impl_item| match (trait_item_kind, impl_item.kind) {
+ | (Const, Const)
+ | (Method, Method)
+ | (Type, Type)
+ | (Type, OpaqueTy) // assoc. types can be made opaque in impls
+ => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id),
+
+ | (Const, _)
+ | (Method, _)
+ | (Type, _)
+ | (OpaqueTy, _)
+ => false,
+ })
+ }
+
pub fn def_id(&self) -> DefId {
match *self {
Node::Impl(did) => did,
}
}
+#[derive(Copy, Clone)]
pub struct Ancestors<'tcx> {
trait_def_id: DefId,
specialization_graph: &'tcx Graph,
}
impl<'tcx> Ancestors<'tcx> {
- /// Search the items from the given ancestors, returning each definition
- /// with the given name and the given kind.
- // FIXME(#35870): avoid closures being unexported due to `impl Trait`.
- #[inline]
- pub fn defs(
- self,
+ /// Finds the bottom-most (ie. most specialized) definition of an associated
+ /// item.
+ pub fn leaf_def(
+ mut self,
tcx: TyCtxt<'tcx>,
trait_item_name: Ident,
trait_item_kind: ty::AssocKind,
- trait_def_id: DefId,
- ) -> impl Iterator<Item = NodeItem<ty::AssocItem>> + Captures<'tcx> + 'tcx {
- self.flat_map(move |node| {
- use crate::ty::AssocKind::*;
- node.items(tcx).filter(move |impl_item| match (trait_item_kind, impl_item.kind) {
- | (Const, Const)
- | (Method, Method)
- | (Type, Type)
- | (Type, OpaqueTy)
- => tcx.hygienic_eq(impl_item.ident, trait_item_name, trait_def_id),
-
- | (Const, _)
- | (Method, _)
- | (Type, _)
- | (OpaqueTy, _)
- => false,
- }).map(move |item| NodeItem { node: node, item: item })
+ ) -> Option<NodeItem<ty::AssocItem>> {
+ let trait_def_id = self.trait_def_id;
+ self.find_map(|node| {
+ node.item(tcx, trait_item_name, trait_item_kind, trait_def_id)
+ .map(|item| NodeItem { node, item })
})
}
}
use crate::hir;
use crate::hir::def_id::DefId;
-use crate::traits::specialize::specialization_graph::NodeItem;
use crate::ty::{self, Ty, TyCtxt, ToPredicate, ToPolyTraitRef};
use crate::ty::outlives::Component;
use crate::ty::subst::{GenericArg, Subst, SubstsRef};
}
}
- pub fn impl_item_is_final(self, node_item: &NodeItem<hir::Defaultness>) -> bool {
- node_item.item.is_final() && !self.impl_is_default(node_item.node.def_id())
+ pub fn impl_item_is_final(self, assoc_item: &ty::AssocItem) -> bool {
+ assoc_item.defaultness.is_final() && !self.impl_is_default(assoc_item.container.id())
}
}
use crate::traits::{Clause, Clauses, GoalKind, Goal, Goals};
use crate::ty::{self, DefIdTree, Ty, TypeAndMut};
use crate::ty::{TyS, TyKind, List};
-use crate::ty::{AdtKind, AdtDef, GeneratorSubsts, Region, Const};
+use crate::ty::{AdtKind, AdtDef, Region, Const};
use crate::ty::{PolyFnSig, InferTy, ParamTy, ProjectionTy, ExistentialPredicate, Predicate};
use crate::ty::RegionKind;
use crate::ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid, ConstVid};
#[inline]
pub fn mk_generator(self,
id: DefId,
- generator_substs: GeneratorSubsts<'tcx>,
+ generator_substs: SubstsRef<'tcx>,
movability: hir::GeneratorMovability)
-> Ty<'tcx> {
self.mk_ty(Generator(id, generator_substs, movability))
impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
type Output = Result<R, E>;
- fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
- Ok(f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?))
+ fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(mut iter: I, f: F)
+ -> Self::Output {
+ // This code is hot enough that it's worth specializing for the most
+ // common length lists, to avoid the overhead of `SmallVec` creation.
+ // The match arms are in order of frequency. The 1, 2, and 0 cases are
+ // typically hit in ~95% of cases. We assume that if the upper and
+ // lower bounds from `size_hint` agree they are correct.
+ Ok(match iter.size_hint() {
+ (1, Some(1)) => {
+ f(&[iter.next().unwrap()?])
+ }
+ (2, Some(2)) => {
+ let t0 = iter.next().unwrap()?;
+ let t1 = iter.next().unwrap()?;
+ f(&[t0, t1])
+ }
+ (0, Some(0)) => {
+ f(&[])
+ }
+ _ => {
+ f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?)
+ }
+ })
}
}
&ty::Generator(_, ref substs, _) => {
self.add_flags(TypeFlags::HAS_TY_CLOSURE);
self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES);
- self.add_substs(&substs.substs);
+ self.add_substs(substs);
}
&ty::GeneratorWitness(ref ts) => {
ConstValue::Placeholder(_) => {
self.add_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_CT_PLACEHOLDER);
}
- _ => {},
+ ConstValue::Scalar(_) => { }
+ ConstValue::Slice { data: _, start: _, end: _ } => { }
+ ConstValue::ByRef { alloc: _, offset: _ } => { }
}
}
}
fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> bool {
- if let ty::Const {
- val: ConstValue::Infer(ty::InferConst::Canonical(debruijn, _)),
- ..
- } = *ct {
- debruijn >= self.outer_index
- } else {
- false
+ // we don't have a `visit_infer_const` callback, so we have to
+ // hook in here to catch this case (annoying...), but
+ // otherwise we do want to remember to visit the rest of the
+ // const, as it has types/regions embedded in a lot of other
+ // places.
+ match ct.val {
+ ConstValue::Infer(ty::InferConst::Canonical(debruijn, _))
+ if debruijn >= self.outer_index => true,
+ _ => ct.super_visit_with(self),
}
}
}
))
}
ty::Generator(def_id, substs, _) => {
- let sig = substs.poly_sig(def_id, tcx);
+ let sig = substs.as_generator().poly_sig(def_id, tcx);
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
traits::VtableGenerator(generator_data) => {
Some(Instance {
def: ty::InstanceDef::Item(generator_data.generator_def_id),
- substs: generator_data.substs.substs
+ substs: generator_data.substs
})
}
traits::VtableClosure(closure_data) => {
use crate::session::{self, DataTypeKind};
-use crate::ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};
+use crate::ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, subst::SubstsRef};
use syntax::ast::{self, Ident, IntTy, UintTy};
use syntax::attr;
use crate::hir;
use crate::ich::StableHashingContext;
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
-use crate::ty::GeneratorSubsts;
use crate::ty::subst::Subst;
use rustc_index::bit_set::BitSet;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
tcx.intern_layout(unit)
}
- ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, &substs)?,
+ ty::Generator(def_id, substs, _) => self.generator_layout(ty, def_id, substs)?,
ty::Closure(def_id, ref substs) => {
let tys = substs.as_closure().upvar_tys(def_id, tcx);
&self,
ty: Ty<'tcx>,
def_id: hir::def_id::DefId,
- substs: &GeneratorSubsts<'tcx>,
+ substs: SubstsRef<'tcx>,
) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> {
use SavedLocalEligibility::*;
let tcx = self.tcx;
- let subst_field = |ty: Ty<'tcx>| { ty.subst(tcx, substs.substs) };
+ let subst_field = |ty: Ty<'tcx>| { ty.subst(tcx, substs) };
let info = tcx.generator_layout(def_id);
let (ineligible_locals, assignments) = self.generator_saved_local_eligibility(&info);
// Build a prefix layout, including "promoting" all ineligible
// locals as part of the prefix. We compute the layout of all of
// these fields at once to get optimal packing.
- let discr_index = substs.prefix_tys(def_id, tcx).count();
+ let discr_index = substs.as_generator().prefix_tys(def_id, tcx).count();
// FIXME(eddyb) set the correct vaidity range for the discriminant.
- let discr_layout = self.layout_of(substs.discr_ty(tcx))?;
+ let discr_layout = self.layout_of(substs.as_generator().discr_ty(tcx))?;
let discr = match &discr_layout.abi {
Abi::Scalar(s) => s.clone(),
_ => bug!(),
.map(|local| subst_field(info.field_tys[local]))
.map(|ty| tcx.mk_maybe_uninit(ty))
.map(|ty| self.layout_of(ty));
- let prefix_layouts = substs.prefix_tys(def_id, tcx)
+ let prefix_layouts = substs.as_generator().prefix_tys(def_id, tcx)
.map(|ty| self.layout_of(ty))
.chain(iter::once(Ok(discr_layout)))
.chain(promoted_layouts)
ty::Generator(def_id, ref substs, _) => {
match this.variants {
Variants::Single { index } => {
- substs.state_tys(def_id, tcx)
+ substs.as_generator().state_tys(def_id, tcx)
.nth(index.as_usize()).unwrap()
.nth(i).unwrap()
}
if i == discr_index {
return discr_layout(discr);
}
- substs.prefix_tys(def_id, tcx).nth(i).unwrap()
+ substs.as_generator().prefix_tys(def_id, tcx).nth(i).unwrap()
}
}
}
ty::Generator(def_id, ref substs, _) => {
// Same as the closure case
- for upvar_ty in substs.upvar_tys(def_id, *self) {
+ for upvar_ty in substs.as_generator().upvar_tys(def_id, *self) {
self.compute_components(upvar_ty, out);
}
use rustc::hir::def_id::DefId;
use rustc::mir::interpret::ConstValue;
use rustc::ty::subst::SubstsRef;
-use rustc::ty::{self, Const, GeneratorSubsts, Instance, Ty, TyCtxt};
+use rustc::ty::{self, Const, Instance, Ty, TyCtxt};
use rustc::{bug, hir};
use std::fmt::Write;
use std::iter;
self.push_type_name(sig.output(), output, debug);
}
}
- ty::Generator(def_id, GeneratorSubsts { substs }, _)
+ ty::Generator(def_id, substs, _)
| ty::Closure(def_id, substs) => {
self.push_def_path(def_id, output);
let generics = self.tcx.generics_of(self.tcx.closure_base_def_id(def_id));
}
ty::Str => p!(write("str")),
ty::Generator(did, substs, movability) => {
- let upvar_tys = substs.upvar_tys(did, self.tcx());
- let witness = substs.witness(did, self.tcx());
+ let upvar_tys = substs.as_generator().upvar_tys(did, self.tcx());
+ let witness = substs.as_generator().witness(did, self.tcx());
if movability == hir::GeneratorMovability::Movable {
p!(write("[generator"));
} else {
if self.tcx().sess.verbose() {
p!(write(
" closure_kind_ty={:?} closure_sig_ty={:?}",
- substs.as_closure().kind(did, self.tcx()),
+ substs.as_closure().kind_ty(did, self.tcx()),
substs.as_closure().sig_ty(did, self.tcx())
));
}
},
ty::Array(ty, sz) => {
p!(write("["), print(ty), write("; "));
- if let ConstValue::Unevaluated(..) = sz.val {
+ if self.tcx().sess.verbose() {
+ p!(write("{:?}", sz));
+ } else if let ConstValue::Unevaluated(..) = sz.val {
// do not try to evalute unevaluated constants. If we are const evaluating an
// array length anon const, rustc will (with debug assertions) print the
// constant's path. Which will end up here again.
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);
+ if self.tcx().sess.verbose() {
+ p!(write("Const({:?}: {:?})", ct.val, ct.ty));
+ return Ok(self);
+ }
+
let u8 = self.tcx().types.u8;
if let ty::FnDef(did, substs) = ct.ty.kind {
p!(print_value_path(did, substs));
format!("processing {:?} with query `{}`", def_id, name).into()
}
}
+
+ default fn cache_on_disk(_: TyCtxt<'tcx>, _: Self::Key, _: Option<&Self::Value>) -> bool {
+ false
+ }
+
+ default fn try_load_from_disk(
+ _: TyCtxt<'tcx>,
+ _: SerializedDepNodeIndex,
+ ) -> Option<Self::Value> {
+ bug!("QueryDescription::load_from_disk() called for an unsupported query.")
+ }
}
impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> {
let desc = &format!("encode_query_results for {}",
::std::any::type_name::<Q>());
- time_ext(tcx.sess.time_extended(), Some(tcx.sess), desc, || {
+ time_ext(tcx.sess.time_extended(), desc, || {
let shards = Q::query_cache(tcx).lock_shards();
assert!(shards.iter().all(|shard| shard.active.is_empty()));
for (key, entry) in shards.iter().flat_map(|shard| shard.results.iter()) {
use crate::ty::query::config::{QueryConfig, QueryDescription};
use crate::ty::query::job::{QueryJob, QueryResult, QueryInfo};
-use crate::util::common::{profq_msg, ProfileQueriesMsg, QueryMsg};
-
use errors::DiagnosticBuilder;
use errors::Level;
use errors::Diagnostic;
}
}
-// If enabled, sends a message to the profile-queries thread.
-macro_rules! profq_msg {
- ($tcx:expr, $msg:expr) => {
- if cfg!(debug_assertions) {
- if $tcx.sess.profile_queries() {
- profq_msg($tcx.sess, $msg)
- }
- }
- }
-}
-
-// If enabled, formats a key using its debug string, which can be
-// expensive to compute (in terms of time).
-macro_rules! profq_query_msg {
- ($query:expr, $tcx:expr, $key:expr) => {{
- let msg = if cfg!(debug_assertions) {
- if $tcx.sess.profile_queries_and_keys() {
- Some(format!("{:?}", $key))
- } else { None }
- } else { None };
- QueryMsg {
- query: $query,
- msg,
- }
- }}
-}
-
/// A type representing the responsibility to execute the job in the `job` field.
/// This will poison the relevant query if dropped.
pub(super) struct JobOwner<'a, 'tcx, Q: QueryDescription<'tcx>> {
loop {
let mut lock = cache.get_shard_by_value(key).lock();
if let Some(value) = lock.results.get(key) {
- profq_msg!(tcx, ProfileQueriesMsg::CacheHit);
tcx.prof.query_cache_hit(Q::NAME);
let result = (value.value.clone(), value.index);
#[cfg(debug_assertions)]
key,
span);
- profq_msg!(self,
- ProfileQueriesMsg::QueryBegin(
- span.data(),
- profq_query_msg!(Q::NAME.as_str(), self, key),
- )
- );
-
let job = match JobOwner::try_get(self, span, &key) {
TryGetJob::NotYetStarted(job) => job,
TryGetJob::Cycle(result) => return result,
if Q::ANON {
- profq_msg!(self, ProfileQueriesMsg::ProviderBegin);
let prof_timer = self.prof.query_provider(Q::NAME);
let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| {
});
drop(prof_timer);
- profq_msg!(self, ProfileQueriesMsg::ProviderEnd);
self.dep_graph.read_index(dep_node_index);
};
let result = if let Some(result) = result {
- profq_msg!(self, ProfileQueriesMsg::CacheHit);
result
} else {
// We could not load a result from the on-disk cache, so
self.incremental_verify_ich::<Q>(&result, dep_node, dep_node_index);
}
- if unlikely!(self.sess.opts.debugging_opts.query_dep_graph) {
- self.dep_graph.mark_loaded_from_cache(dep_node_index, true);
- }
-
result
}
- dep-node: {:?}",
key, dep_node);
- profq_msg!(self, ProfileQueriesMsg::ProviderBegin);
let prof_timer = self.prof.query_provider(Q::NAME);
let ((result, dep_node_index), diagnostics) = with_diagnostics(|diagnostics| {
});
drop(prof_timer);
- profq_msg!(self, ProfileQueriesMsg::ProviderEnd);
-
- if unlikely!(self.sess.opts.debugging_opts.query_dep_graph) {
- self.dep_graph.mark_loaded_from_cache(dep_node_index, false);
- }
if unlikely!(!diagnostics.is_empty()) {
if dep_node.kind != crate::dep_graph::DepKind::Null {
let _ = self.get_query::<Q>(DUMMY_SP, key);
} else {
- profq_msg!(self, ProfileQueriesMsg::CacheHit);
self.prof.query_cache_hit(Q::NAME);
}
}
#[allow(dead_code)]
fn force_query<Q: QueryDescription<'tcx>>(self, key: Q::Key, span: Span, dep_node: DepNode) {
- profq_msg!(
- self,
- ProfileQueriesMsg::QueryBegin(span.data(),
- profq_query_msg!(Q::NAME.as_str(), self, key))
- );
-
// We may be concurrently trying both execute and force a query.
// Ensure that only one of them runs the query.
let job = match JobOwner::try_get(self, span, &key) {
return false
}
- macro_rules! def_id {
- () => {
- if let Some(def_id) = dep_node.extract_def_id(tcx) {
- def_id
- } else {
- // Return from the whole function.
- return false
- }
- }
- };
-
- macro_rules! krate {
- () => { (def_id!()).krate }
- };
-
- macro_rules! force_ex {
- ($tcx:expr, $query:ident, $key:expr) => {
- {
- $tcx.force_query::<crate::ty::query::queries::$query<'_>>(
- $key,
- DUMMY_SP,
- *dep_node
- );
- }
- }
- };
-
- macro_rules! force {
- ($query:ident, $key:expr) => { force_ex!(tcx, $query, $key) }
- };
-
rustc_dep_node_force!([dep_node, tcx]
// These are inputs that are expected to be pre-allocated and that
// should therefore always be red or green already.
bug!("force_from_dep_node: encountered {:?}", dep_node)
}
- DepKind::Analysis => { force!(analysis, krate!()); }
+ DepKind::Analysis => {
+ let def_id = if let Some(def_id) = dep_node.extract_def_id(tcx) {
+ def_id
+ } else {
+ // Return from the whole function.
+ return false
+ };
+ tcx.force_query::<crate::ty::query::queries::analysis<'_>>(
+ def_id.krate,
+ DUMMY_SP,
+ *dep_node
+ );
+ }
);
true
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
- folder.tcx().intern_predicates(&v)
+ // This code is hot enough that it's worth specializing for a list of
+ // length 0. (No other length is common enough to be worth singling
+ // out).
+ if self.len() == 0 {
+ self
+ } else {
+ // Don't bother interning if nothing changed, which is the common
+ // case.
+ let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
+ if v[..] == self[..] {
+ self
+ } else {
+ folder.tcx().intern_predicates(&v)
+ }
+ }
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, GenericArg, GenericArgKind};
use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
-use crate::ty::layout::{Size, Integer, IntegerExt, VariantIdx};
+use crate::ty::layout::VariantIdx;
use crate::util::captures::Captures;
use crate::mir::interpret::{Scalar, GlobalId};
use std::ops::Range;
use rustc_target::spec::abi;
use syntax::ast::{self, Ident};
-use syntax::attr::{SignedInt, UnsignedInt};
use syntax::symbol::{kw, InternedString};
use self::InferTy::*;
/// The anonymous type of a generator. Used to represent the type of
/// `|a| yield a`.
- Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),
+ Generator(DefId, SubstsRef<'tcx>, hir::GeneratorMovability),
/// A type representin the types stored inside a generator.
/// This should only appear in GeneratorInteriors.
/// variant indices.
#[inline]
pub fn discriminants(
- &'tcx self,
+ self,
def_id: DefId,
tcx: TyCtxt<'tcx>,
) -> impl Iterator<Item = (VariantIdx, Discr<'tcx>)> + Captures<'tcx> {
/// Calls `f` with a reference to the name of the enumerator for the given
/// variant `v`.
#[inline]
- pub fn variant_name(&self, v: VariantIdx) -> Cow<'static, str> {
+ pub fn variant_name(self, v: VariantIdx) -> Cow<'static, str> {
match v.as_usize() {
Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME),
Self::RETURNED => Cow::from(Self::RETURNED_NAME),
#[derive(Debug, Copy, Clone)]
pub enum UpvarSubsts<'tcx> {
Closure(SubstsRef<'tcx>),
- Generator(GeneratorSubsts<'tcx>),
+ Generator(SubstsRef<'tcx>),
}
impl<'tcx> UpvarSubsts<'tcx> {
) -> impl Iterator<Item = Ty<'tcx>> + 'tcx {
let upvar_kinds = match self {
UpvarSubsts::Closure(substs) => substs.as_closure().split(def_id, tcx).upvar_kinds,
- UpvarSubsts::Generator(substs) => substs.split(def_id, tcx).upvar_kinds,
+ UpvarSubsts::Generator(substs) => substs.as_generator().split(def_id, tcx).upvar_kinds,
};
upvar_kinds.iter().map(|t| {
if let GenericArgKind::Type(ty) = t.unpack() {
#[inline]
pub fn is_bool(&self) -> bool { self.kind == Bool }
+ /// Returns `true` if this type is a `str`.
+ #[inline]
+ pub fn is_str(&self) -> bool { self.kind == Str }
+
#[inline]
pub fn is_param(&self, index: u32) -> bool {
match self.kind {
pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> {
match self.kind {
TyKind::Adt(adt, _) => Some(adt.variant_range()),
- TyKind::Generator(def_id, substs, _) => Some(substs.variant_range(def_id, tcx)),
+ TyKind::Generator(def_id, substs, _) =>
+ Some(substs.as_generator().variant_range(def_id, tcx)),
_ => None,
}
}
match self.kind {
TyKind::Adt(adt, _) => Some(adt.discriminant_for_variant(tcx, variant_index)),
TyKind::Generator(def_id, substs, _) =>
- Some(substs.discriminant_for_variant(def_id, tcx, variant_index)),
+ Some(substs.as_generator().discriminant_for_variant(def_id, tcx, variant_index)),
_ => None,
}
}
out.extend(substs.regions())
}
Closure(_, ref substs ) |
- Generator(_, GeneratorSubsts { ref substs }, _) => {
+ Generator(_, ref substs, _) => {
out.extend(substs.regions())
}
Projection(ref data) | UnnormalizedProjection(ref data) => {
_ => bug!("cannot convert type `{:?}` to a closure kind", self),
},
- Infer(_) => None,
+ // "Bound" types appear in canonical queries when the
+ // closure type is not yet known
+ Bound(..) | Infer(_) => None,
Error => Some(ty::ClosureKind::Fn),
ty: Ty<'tcx>,
) -> Option<u128> {
assert_eq!(self.ty, ty);
- // This is purely an optimization -- layout_of is a pretty expensive operation,
- // but if we can determine the size without calling it, we don't need all that complexity
- // (hashing, caching, etc.). As such, try to skip it.
- let size = match ty.kind {
- ty::Bool => Size::from_bytes(1),
- ty::Char => Size::from_bytes(4),
- ty::Int(ity) => {
- Integer::from_attr(&tcx, SignedInt(ity)).size()
- }
- ty::Uint(uty) => {
- Integer::from_attr(&tcx, UnsignedInt(uty)).size()
- }
- _ => tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size,
- };
+ let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
self.eval(tcx, param_env).val.try_to_bits(size)
}
use crate::ty::{self, Lift, List, Ty, TyCtxt, InferConst, ParamConst};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::mir::interpret::ConstValue;
-use crate::ty::sty::ClosureSubsts;
+use crate::ty::sty::{ClosureSubsts, GeneratorSubsts};
use rustc_serialize::{self, Encodable, Encoder, Decodable, Decoder};
use syntax_pos::{Span, DUMMY_SP};
}
}
+ /// Interpret these substitutions as the substitutions of a generator type.
+ /// Closure substitutions have a particular structure controlled by the
+ /// compiler that encodes information like the signature and generator kind;
+ /// see `ty::GeneratorSubsts` struct for more comments.
+ pub fn as_generator(&'tcx self) -> GeneratorSubsts<'tcx> {
+ GeneratorSubsts { substs: self }
+ }
+
/// Creates a `InternalSubsts` that maps each generic parameter to itself.
pub fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
Self::for_item(tcx, def_id, |param, _| {
impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> {
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
- let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
-
- // If folding doesn't change the substs, it's faster to avoid
- // calling `mk_substs` and instead reuse the existing substs.
- if params[..] == self[..] {
- self
- } else {
- folder.tcx().intern_substs(¶ms)
+ // This code is hot enough that it's worth specializing for the most
+ // common length lists, to avoid the overhead of `SmallVec` creation.
+ // The match arms are in order of frequency. The 1, 2, and 0 cases are
+ // typically hit in 90--99.99% of cases. When folding doesn't change
+ // the substs, it's faster to reuse the existing substs rather than
+ // calling `intern_substs`.
+ match self.len() {
+ 1 => {
+ let param0 = self[0].fold_with(folder);
+ if param0 == self[0] {
+ self
+ } else {
+ folder.tcx().intern_substs(&[param0])
+ }
+ }
+ 2 => {
+ let param0 = self[0].fold_with(folder);
+ let param1 = self[1].fold_with(folder);
+ if param0 == self[0] && param1 == self[1] {
+ self
+ } else {
+ folder.tcx().intern_substs(&[param0, param1])
+ }
+ }
+ 0 => {
+ self
+ }
+ _ => {
+ let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
+ if params[..] == self[..] {
+ self
+ } else {
+ folder.tcx().intern_substs(¶ms)
+ }
+ }
}
}
ty::Adt(_, substs) | ty::Opaque(_, substs) => {
stack.extend(substs.types().rev());
}
- ty::Closure(_, ref substs) => {
+ ty::Closure(_, ref substs)
+ | ty::Generator(_, ref substs, _) => {
stack.extend(substs.types().rev());
}
- ty::Generator(_, ref substs, _) => {
- stack.extend(substs.substs.types().rev());
- }
ty::GeneratorWitness(ts) => {
stack.extend(ts.skip_binder().iter().cloned().rev());
}
use std::fmt::Debug;
use std::time::{Duration, Instant};
-use std::sync::mpsc::{Sender};
-use syntax_pos::{SpanData};
use syntax::symbol::{Symbol, sym};
use rustc_macros::HashStable;
-use crate::dep_graph::{DepNode};
use crate::session::Session;
#[cfg(test)]
thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
-/// Parameters to the `Dump` variant of type `ProfileQueriesMsg`.
-#[derive(Clone,Debug)]
-pub struct ProfQDumpParams {
- /// A base path for the files we will dump.
- pub path:String,
- /// To ensure that the compiler waits for us to finish our dumps.
- pub ack:Sender<()>,
- /// Toggle dumping a log file with every `ProfileQueriesMsg`.
- pub dump_profq_msg_log:bool,
-}
-
#[allow(nonstandard_style)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryMsg {
pub msg: Option<String>,
}
-/// A sequence of these messages induce a trace of query-based incremental compilation.
-// FIXME(matthewhammer): Determine whether we should include cycle detection here or not.
-#[derive(Clone,Debug)]
-pub enum ProfileQueriesMsg {
- /// Begin a timed pass.
- TimeBegin(String),
- /// End a timed pass.
- TimeEnd,
- /// Begin a task (see `dep_graph::graph::with_task`).
- TaskBegin(DepNode),
- /// End a task.
- TaskEnd,
- /// Begin a new query.
- /// Cannot use `Span` because queries are sent to other thread.
- QueryBegin(SpanData, QueryMsg),
- /// Query is satisfied by using an already-known value for the given key.
- CacheHit,
- /// Query requires running a provider; providers may nest, permitting queries to nest.
- ProviderBegin,
- /// Query is satisfied by a provider terminating with a value.
- ProviderEnd,
- /// Dump a record of the queries to the given path.
- Dump(ProfQDumpParams),
- /// Halt the profiling/monitoring background thread.
- Halt
-}
-
-/// If enabled, send a message to the profile-queries thread.
-pub fn profq_msg(sess: &Session, msg: ProfileQueriesMsg) {
- if let Some(s) = sess.profile_channel.borrow().as_ref() {
- s.send(msg).unwrap()
- } else {
- // Do nothing.
- }
-}
-
-/// Set channel for profile queries channel.
-pub fn profq_set_chan(sess: &Session, s: Sender<ProfileQueriesMsg>) -> bool {
- let mut channel = sess.profile_channel.borrow_mut();
- if channel.is_none() {
- *channel = Some(s);
- true
- } else {
- false
- }
-}
-
/// Read the current depth of `time()` calls. This is used to
/// encourage indentation across threads.
pub fn time_depth() -> usize {
pub fn time<T, F>(sess: &Session, what: &str, f: F) -> T where
F: FnOnce() -> T,
{
- time_ext(sess.time_passes(), Some(sess), what, f)
+ time_ext(sess.time_passes(), what, f)
}
-pub fn time_ext<T, F>(do_it: bool, sess: Option<&Session>, what: &str, f: F) -> T where
+pub fn time_ext<T, F>(do_it: bool, what: &str, f: F) -> T where
F: FnOnce() -> T,
{
if !do_it { return f(); }
r
});
- if let Some(sess) = sess {
- if cfg!(debug_assertions) {
- profq_msg(sess, ProfileQueriesMsg::TimeBegin(what.to_string()))
- }
- }
let start = Instant::now();
let rv = f();
let dur = start.elapsed();
- if let Some(sess) = sess {
- if cfg!(debug_assertions) {
- profq_msg(sess, ProfileQueriesMsg::TimeEnd)
- }
- }
print_time_passes_entry(true, what, dur);
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx,
llfn,
- "entry\0".as_ptr() as *const _);
+ "entry\0".as_ptr().cast());
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
args.as_ptr(),
args.len() as c_uint,
None,
- "\0".as_ptr() as *const _);
+ "\0".as_ptr().cast());
llvm::LLVMSetTailCall(ret, True);
if output.is_some() {
llvm::LLVMBuildRet(llbuilder, ret);
}
// Currently stack probes seem somewhat incompatible with the address
- // sanitizer. With asan we're already protected from stack overflow anyway
- // so we don't really need stack probes regardless.
- if let Some(Sanitizer::Address) = cx.sess().opts.debugging_opts.sanitizer {
- return
+ // sanitizer and thread sanitizer. With asan we're already protected from
+ // stack overflow anyway so we don't really need stack probes regardless.
+ match cx.sess().opts.debugging_opts.sanitizer {
+ Some(Sanitizer::Address) |
+ Some(Sanitizer::Thread) => return,
+ _ => {},
}
// probestack doesn't play nice either with `-C profile-generate`.
info!("adding bytecode {}", name);
let bc_encoded = data.data();
- let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || {
+ let (bc, id) = time_ext(cgcx.time_passes, &format!("decode {}", name), || {
match DecodedBytecode::new(bc_encoded) {
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
Err(e) => Err(diag_handler.fatal(&e)),
for (bc_decoded, name) in serialized_modules {
let _timer = cgcx.prof.generic_activity("LLVM_fat_lto_link_module");
info!("linking {:?}", name);
- time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || {
+ time_ext(cgcx.time_passes, &format!("ll link {:?}", name), || {
let data = bc_decoded.data();
linker.add(&data).map_err(|()| {
let msg = format!("failed to load bc of {:?}", name);
llvm::LLVMRustAddAnalysisPasses(module.module_llvm.tm, pm, module.module_llvm.llmod());
if config.verify_llvm_ir {
- let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
+ let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast());
llvm::LLVMRustAddPass(pm, pass.unwrap());
}
// We always generate bitcode through ThinLTOBuffers,
// which do not support anonymous globals
if config.bitcode_needed() {
- let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr() as *const _);
+ let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr().cast());
llvm::LLVMRustAddPass(pm, pass.unwrap());
}
if config.verify_llvm_ir {
- let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _);
+ let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast());
llvm::LLVMRustAddPass(pm, pass.unwrap());
}
- time_ext(cgcx.time_passes, None, "LTO passes", ||
+ time_ext(cgcx.time_passes, "LTO passes", ||
llvm::LLVMRunPassManager(pm, module.module_llvm.llmod()));
llvm::LLVMDisposePassManager(pm);
llcx: &'a llvm::Context) -> Self {
let data = Box::into_raw(Box::new((cgcx, handler)));
unsafe {
- llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data as *mut _);
- llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, data as *mut _);
+ llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data.cast());
+ llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, data.cast());
}
DiagnosticHandlers { data, llcx }
}
{
let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_function_passes");
time_ext(config.time_passes,
- None,
&format!("llvm function passes [{}]", module_name.unwrap()),
|| {
llvm::LLVMRustRunFunctionPassManager(fpm, llmod)
{
let _timer = cgcx.prof.generic_activity("LLVM_module_optimize_module_passes");
time_ext(config.time_passes,
- None,
&format!("llvm module passes [{}]", module_name.unwrap()),
|| {
llvm::LLVMRunPassManager(mpm, llmod)
embed_bitcode(cgcx, llcx, llmod, None);
}
- time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()),
+ time_ext(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()),
|| -> Result<(), FatalError> {
if config.emit_ir {
let _timer = cgcx.prof.generic_activity("LLVM_module_codegen_emit_ir");
let llglobal = llvm::LLVMAddGlobal(
llmod,
common::val_ty(llconst),
- "rustc.embedded.module\0".as_ptr() as *const _,
+ "rustc.embedded.module\0".as_ptr().cast(),
);
llvm::LLVMSetInitializer(llglobal, llconst);
} else {
".llvmbc\0"
};
- llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
+ llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
let llglobal = llvm::LLVMAddGlobal(
llmod,
common::val_ty(llconst),
- "rustc.embedded.cmdline\0".as_ptr() as *const _,
+ "rustc.embedded.cmdline\0".as_ptr().cast(),
);
llvm::LLVMSetInitializer(llglobal, llconst);
let section = if is_apple {
} else {
".llvmcmd\0"
};
- llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
+ llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
}
for (imp_name, val) in globals {
let imp = llvm::LLVMAddGlobal(llmod,
i8p_ty,
- imp_name.as_ptr() as *const _);
+ imp_name.as_ptr().cast());
llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
}
if let Some(section) = attrs.link_section {
let section = llvm::LLVMMDStringInContext(
self.llcx,
- section.as_str().as_ptr() as *const _,
+ section.as_str().as_ptr().cast(),
section.as_str().len() as c_uint,
);
assert!(alloc.relocations().is_empty());
0..alloc.len());
let alloc = llvm::LLVMMDStringInContext(
self.llcx,
- bytes.as_ptr() as *const _,
+ bytes.as_ptr().cast(),
bytes.len() as c_uint,
);
let data = [section, alloc];
let meta = llvm::LLVMMDNodeInContext(self.llcx, data.as_ptr(), 2);
llvm::LLVMAddNamedMetadataOperand(
self.llmod,
- "wasm.custom_sections\0".as_ptr() as *const _,
+ "wasm.custom_sections\0".as_ptr().cast(),
meta,
);
}
// If skipping the PLT is enabled, we need to add some module metadata
// to ensure intrinsic calls don't use it.
if !sess.needs_plt() {
- let avoid_plt = "RtLibUseGOT\0".as_ptr() as *const _;
+ let avoid_plt = "RtLibUseGOT\0".as_ptr().cast();
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
}
let section_var = unsafe {
llvm::LLVMGetNamedGlobal(cx.llmod,
- c_section_var_name.as_ptr() as *const _)
+ c_section_var_name.as_ptr().cast())
};
section_var.unwrap_or_else(|| {
llvm_type).unwrap_or_else(||{
bug!("symbol `{}` is already defined", section_var_name)
});
- llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _);
+ llvm::LLVMSetSection(section_var, section_name.as_ptr().cast());
llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents));
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
llvm::LLVMSetUnnamedAddr(section_var, llvm::True);
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
use rustc::ty::layout::{self, Align, Integer, IntegerExt, LayoutOf,
PrimitiveExt, Size, TyLayout, VariantIdx};
-use rustc::ty::subst::GenericArgKind;
+use rustc::ty::subst::{GenericArgKind, SubstsRef};
use rustc::session::config::{self, DebugInfo};
use rustc::util::nodemap::FxHashMap;
use rustc_fs_util::path_to_c_string;
Some(containing_scope)).finalize(cx)
}
ty::Generator(def_id, substs, _) => {
- let upvar_tys : Vec<_> = substs.prefix_tys(def_id, cx.tcx).map(|t| {
- cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)
- }).collect();
+ let upvar_tys : Vec<_> = substs
+ .as_generator().prefix_tys(def_id, cx.tcx).map(|t| {
+ cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)
+ }).collect();
prepare_enum_metadata(cx,
t,
def_id,
file_metadata,
producer.as_ptr(),
tcx.sess.opts.optimize != config::OptLevel::No,
- flags.as_ptr() as *const _,
+ flags.as_ptr().cast(),
0,
- split_name.as_ptr() as *const _,
+ split_name.as_ptr().cast(),
kind);
if tcx.sess.opts.debugging_opts.profile {
if tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
let name_metadata = llvm::LLVMMDStringInContext(
debug_context.llcontext,
- rustc_producer.as_ptr() as *const _,
+ rustc_producer.as_ptr().cast(),
rustc_producer.as_bytes().len() as c_uint,
);
llvm::LLVMAddNamedMetadataOperand(
ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
ty::Generator(def_id, substs, _) => {
let generator_layout = cx.tcx.generator_layout(*def_id);
- VariantInfo::Generator(*substs, generator_layout, index)
+ VariantInfo::Generator(substs, generator_layout, index)
}
_ => bug!(),
}
#[derive(Copy, Clone)]
enum VariantInfo<'tcx> {
Adt(&'tcx ty::VariantDef),
- Generator(ty::GeneratorSubsts<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
+ Generator(SubstsRef<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
}
impl<'tcx> VariantInfo<'tcx> {
match self {
VariantInfo::Adt(variant) => f(&variant.ident.as_str()),
VariantInfo::Generator(substs, _, variant_index) =>
- f(&substs.variant_name(*variant_index)),
+ f(&substs.as_generator().variant_name(*variant_index)),
}
}
})
.collect(),
ty::Generator(_, substs, _) => substs
+ .as_generator()
.variant_range(enum_def_id, cx.tcx)
.map(|variant_index| {
- let name = SmallCStr::new(&substs.variant_name(variant_index));
+ let name = SmallCStr::new(&substs.as_generator().variant_name(variant_index));
unsafe {
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
DIB(cx),
if cx.sess().target.target.options.is_like_osx ||
cx.sess().target.target.options.is_like_android {
llvm::LLVMRustAddModuleFlag(cx.llmod,
- "Dwarf Version\0".as_ptr() as *const _,
+ "Dwarf Version\0".as_ptr().cast(),
2)
}
// Indicate that we want CodeView debug information on MSVC
if cx.sess().target.target.options.is_like_msvc {
llvm::LLVMRustAddModuleFlag(cx.llmod,
- "CodeView\0".as_ptr() as *const _,
+ "CodeView\0".as_ptr().cast(),
1)
}
// Prevent bitcode readers from deleting the debug info.
let ptr = "Debug Info Version\0".as_ptr();
- llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _,
+ llvm::LLVMRustAddModuleFlag(cx.llmod, ptr.cast(),
llvm::LLVMRustDebugMetadataVersion());
};
}
"hexagon" => HEXAGON_WHITELIST,
"mips" | "mips64" => MIPS_WHITELIST,
"powerpc" | "powerpc64" => POWERPC_WHITELIST,
- "wasm32" => WASM_WHITELIST,
+ // wasm32 on emscripten does not support these target features
+ "wasm32" if !sess.target.target.options.is_like_emscripten => WASM_WHITELIST,
_ => &[],
}
}
if let (&ty::Generator(_, substs, _), &layout::Variants::Single { index })
= (&layout.ty.kind, &layout.variants)
{
- write!(&mut name, "::{}", substs.variant_name(index)).unwrap();
+ write!(&mut name, "::{}", substs.as_generator().variant_name(index)).unwrap();
}
Some(name)
}
NativeLibraryKind::NativeStatic => {}
NativeLibraryKind::NativeStaticNobundle |
NativeLibraryKind::NativeFramework |
+ NativeLibraryKind::NativeRawDylib |
NativeLibraryKind::NativeUnknown => continue,
}
if let Some(name) = lib.name {
Some(format!("-framework {}", name))
},
// These are included, no need to print them
- NativeLibraryKind::NativeStatic => None,
+ NativeLibraryKind::NativeStatic |
+ NativeLibraryKind::NativeRawDylib => None,
}
})
.collect();
NativeLibraryKind::NativeUnknown => cmd.link_dylib(name),
NativeLibraryKind::NativeFramework => cmd.link_framework(name),
NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(name),
- NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(name, &search_path)
+ NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(name, &search_path),
+ NativeLibraryKind::NativeRawDylib => {
+ // FIXME(#58713): Proper handling for raw dylibs.
+ bug!("raw_dylib feature not yet implemented");
+ },
}
}
}
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
}
- _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) => {
+ _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) &&
+ crate_type == config::CrateType::Executable => {
+ // Link the sanitizer runtimes only if we are actually producing an executable
link_sanitizer_runtime::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// compiler-builtins are always placed last to ensure that they're
let name = cratepath.file_name().unwrap().to_str().unwrap();
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
- time_ext(sess.time_extended(), Some(sess), &format!("altering {}.rlib", name), || {
+ time_ext(sess.time_extended(), &format!("altering {}.rlib", name), || {
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
archive.update_symbols();
// ignore statically included native libraries here as we've
// already included them when we included the rust library
// previously
- NativeLibraryKind::NativeStatic => {}
+ NativeLibraryKind::NativeStatic => {},
+ NativeLibraryKind::NativeRawDylib => {
+ // FIXME(#58713): Proper handling for raw dylibs.
+ bug!("raw_dylib feature not yet implemented");
+ },
}
}
}
// Copy what clang does by turning on loop vectorization at O2 and
// slp vectorization at O3. Otherwise configure other optimization aspects
// of this pass manager builder.
+ // Turn off vectorization for emscripten, as it's not very well supported.
self.vectorize_loop = !sess.opts.cg.no_vectorize_loops &&
(sess.opts.optimize == config::OptLevel::Default ||
- sess.opts.optimize == config::OptLevel::Aggressive);
+ sess.opts.optimize == config::OptLevel::Aggressive) &&
+ !sess.target.target.options.is_like_emscripten;
self.vectorize_slp = !sess.opts.cg.no_vectorize_slp &&
- sess.opts.optimize == config::OptLevel::Aggressive;
+ sess.opts.optimize == config::OptLevel::Aggressive &&
+ !sess.target.target.options.is_like_emscripten;
// Some targets (namely, NVPTX) interact badly with the MergeFunctions
// pass. This is because MergeFunctions can generate new function calls
let (coordinator_send, coordinator_receive) = channel();
let sess = tcx.sess;
- sess.prof.generic_activity_start("codegen_and_optimize_crate");
-
let crate_name = tcx.crate_name(LOCAL_CRATE);
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
let no_builtins = attr::contains_name(&tcx.hir().krate().attrs, sym::no_builtins);
self.backend.print_pass_timings()
}
- sess.prof.generic_activity_end("codegen_and_optimize_crate");
-
(CodegenResults {
crate_name: self.crate_name,
crate_hash: self.crate_hash,
ty::Generator(def_id, substs, _) => (def_id, substs),
_ => bug!("generator layout without generator substs"),
};
- let state_tys = gen_substs.state_tys(def_id, tcx);
+ let state_tys = gen_substs.as_generator().state_tys(def_id, tcx);
generator_layout.variant_fields.iter()
.zip(state_tys)
ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) |
ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) |
ty::Closure(def_id, substs) |
- ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => {
+ ty::Generator(def_id, substs, _) => {
self.print_def_path(def_id, substs)
}
_ => self.pretty_print_type(ty),
ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs }) |
ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs }) |
ty::Closure(def_id, substs) |
- ty::Generator(def_id, ty::GeneratorSubsts { substs }, _) => {
+ ty::Generator(def_id, substs, _) => {
self = self.print_def_path(def_id, substs)?;
}
ty::Foreign(def_id) => {
cfg-if = "0.1.2"
crossbeam-utils = { version = "0.6.5", features = ["nightly"] }
stable_deref_trait = "1.0.0"
-rayon = { version = "0.2.0", package = "rustc-rayon" }
-rayon-core = { version = "0.2.0", package = "rustc-rayon-core" }
+rayon = { version = "0.3.0", package = "rustc-rayon" }
+rayon-core = { version = "0.3.0", package = "rustc-rayon-core" }
rustc-hash = "1.0.1"
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
rustc_index = { path = "../librustc_index", package = "rustc_index" }
/// * may contain a name of a function, variable, or type, but not whole expressions
///
/// See `CodeSuggestion` for more information.
- pub fn span_suggestion(&mut self, sp: Span, msg: &str,
- suggestion: String,
- applicability: Applicability) -> &mut Self {
+ pub fn span_suggestion(
+ &mut self,
+ sp: Span,
+ msg: &str,
+ suggestion: String,
+ applicability: Applicability,
+ ) -> &mut Self {
+ self.span_suggestion_with_style(
+ sp,
+ msg,
+ suggestion,
+ applicability,
+ SuggestionStyle::ShowCode,
+ );
+ self
+ }
+
+ pub fn span_suggestion_with_style(
+ &mut self,
+ sp: Span,
+ msg: &str,
+ suggestion: String,
+ applicability: Applicability,
+ style: SuggestionStyle,
+ ) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: vec![Substitution {
parts: vec![SubstitutionPart {
}],
}],
msg: msg.to_owned(),
- style: SuggestionStyle::ShowCode,
+ style,
applicability,
});
self
}
+ pub fn span_suggestion_verbose(
+ &mut self,
+ sp: Span,
+ msg: &str,
+ suggestion: String,
+ applicability: Applicability,
+ ) -> &mut Self {
+ self.span_suggestion_with_style(
+ sp,
+ msg,
+ suggestion,
+ applicability,
+ SuggestionStyle::ShowAlways,
+ );
+ self
+ }
+
/// Prints out a message with multiple suggested edits of the code.
- pub fn span_suggestions(&mut self, sp: Span, msg: &str,
- suggestions: impl Iterator<Item = String>, applicability: Applicability) -> &mut Self
- {
+ pub fn span_suggestions(
+ &mut self,
+ sp: Span,
+ msg: &str,
+ suggestions: impl Iterator<Item = String>,
+ applicability: Applicability,
+ ) -> &mut Self {
self.suggestions.push(CodeSuggestion {
substitutions: suggestions.map(|snippet| Substitution {
parts: vec![SubstitutionPart {
pub fn span_suggestion_short(
&mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
) -> &mut Self {
- self.suggestions.push(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: vec![SubstitutionPart {
- snippet: suggestion,
- span: sp,
- }],
- }],
- msg: msg.to_owned(),
- style: SuggestionStyle::HideCodeInline,
+ self.span_suggestion_with_style(
+ sp,
+ msg,
+ suggestion,
applicability,
- });
+ SuggestionStyle::HideCodeInline,
+ );
self
}
pub fn span_suggestion_hidden(
&mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
) -> &mut Self {
- self.suggestions.push(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: vec![SubstitutionPart {
- snippet: suggestion,
- span: sp,
- }],
- }],
- msg: msg.to_owned(),
- style: SuggestionStyle::HideCodeAlways,
+ self.span_suggestion_with_style(
+ sp,
+ msg,
+ suggestion,
applicability,
- });
+ SuggestionStyle::HideCodeAlways,
+ );
self
}
pub fn tool_only_span_suggestion(
&mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
) -> &mut Self {
- self.suggestions.push(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: vec![SubstitutionPart {
- snippet: suggestion,
- span: sp,
- }],
- }],
- msg: msg.to_owned(),
- style: SuggestionStyle::CompletelyHidden,
+ self.span_suggestion_with_style(
+ sp,
+ msg,
+ suggestion,
applicability,
- });
+ SuggestionStyle::CompletelyHidden,
+ );
self
}
sugg.msg.split_whitespace().count() < 10 &&
// don't display multiline suggestions as labels
!sugg.substitutions[0].parts[0].snippet.contains('\n') &&
- // when this style is set we want the suggestion to be a message, not inline
- sugg.style != SuggestionStyle::HideCodeAlways &&
- // trivial suggestion for tooling's sake, never shown
- sugg.style != SuggestionStyle::CompletelyHidden
+ ![
+ // when this style is set we want the suggestion to be a message, not inline
+ SuggestionStyle::HideCodeAlways,
+ // trivial suggestion for tooling's sake, never shown
+ SuggestionStyle::CompletelyHidden,
+ // subtle suggestion, never shown inline
+ SuggestionStyle::ShowAlways,
+ ].contains(&sugg.style)
{
let substitution = &sugg.substitutions[0].parts[0].snippet.trim();
let msg = if substitution.len() == 0 || sugg.style.hide_inline() {
/// This will *not* show the code if the suggestion is inline *and* the suggested code is
/// empty.
ShowCode,
+ /// Always show the suggested code independently.
+ ShowAlways,
}
impl SuggestionStyle {
pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
self.ensure_lines(line);
- let string_len = string.len();
+ let string_len = string.chars().count();
// Push the old content over to make room for new content
for _ in 0..string_len {
// before we fire the background thread.
let time_passes = sess.time_passes();
+ let prof = sess.prof.clone();
if sess.opts.incremental.is_none() {
// No incremental compilation.
}
MaybeAsync::Async(std::thread::spawn(move || {
- time_ext(time_passes, None, "background load prev dep-graph", move || {
+ time_ext(time_passes, "background load prev dep-graph", move || {
+ let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
+
match load_data(report_incremental_info, &path) {
LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
LoadResult::Error { message } => LoadResult::Error { message },
return OnDiskCache::new_empty(sess.source_map());
}
+ let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
+
match load_data(sess.opts.debugging_opts.incremental_info, &query_cache_path(sess)) {
LoadResult::Ok{ data: (bytes, start_pos) } => OnDiskCache::new(sess, bytes, start_pos),
_ => OnDiskCache::new_empty(sess.source_map())
fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut Encoder) {
time(tcx.sess, "serialize query result cache", || {
+ let _timer = tcx.prof.generic_activity("incr_comp_serialize_result_cache");
+
tcx.serialize_query_result_cache(encoder).unwrap();
})
}
[dependencies]
log = "0.4"
-rayon = { version = "0.2.0", package = "rustc-rayon" }
+rayon = { version = "0.3.0", package = "rustc-rayon" }
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
syntax_ext = { path = "../libsyntax_ext" }
use crate::queries::Queries;
use crate::util;
-use crate::profile;
pub use crate::passes::BoxedResolver;
use rustc::lint;
compiler.sess.diagnostic().print_error_count(&util::diagnostics_registry());
});
- if compiler.sess.profile_queries() {
- profile::begin(&compiler.sess);
- }
-
- let r = f(&compiler);
-
- if compiler.sess.profile_queries() {
- profile::dump(&compiler.sess, "profile_queries".to_string())
- }
-
- r
+ f(&compiler)
}
pub fn run_compiler<F, R>(mut config: Config, f: F) -> R
mod queries;
pub mod util;
mod proc_macro_decls;
-mod profile;
pub use interface::{run_compiler, Config};
if sess.opts.incremental.is_some() {
time(sess, "garbage-collect incremental cache directory", || {
+ let _prof_timer =
+ sess.prof.generic_activity("incr_comp_garbage_collect_session_directories");
if let Err(e) = rustc_incremental::garbage_collect_session_directories(sess) {
warn!(
"Error while trying to garbage collect incremental \
ty::provide(providers);
traits::provide(providers);
stability::provide(providers);
- middle::intrinsicck::provide(providers);
- middle::liveness::provide(providers);
reachable::provide(providers);
rustc_passes::provide(providers);
rustc_traits::provide(providers);
middle::region::provide(providers);
- middle::entry::provide(providers);
cstore::provide(providers);
lint::provide(providers);
rustc_lint::provide(providers);
time(sess, "misc checking 1", || {
parallel!({
entry_point = time(sess, "looking for entry point", || {
- middle::entry::find_entry_point(tcx)
+ rustc_passes::entry::find_entry_point(tcx)
});
time(sess, "looking for plugin registrar", || {
tcx.ensure().check_private_in_public(LOCAL_CRATE);
});
}, {
- time(sess, "death checking", || middle::dead::check_crate(tcx));
+ time(sess, "death checking", || rustc_passes::dead::check_crate(tcx));
}, {
time(sess, "unused lib feature checking", || {
stability::check_unused_or_stable_features(tcx)
+++ /dev/null
-use log::debug;
-use rustc::dep_graph::DepNode;
-use rustc::session::Session;
-use rustc::util::common::{ProfQDumpParams, ProfileQueriesMsg, profq_msg, profq_set_chan};
-use std::sync::mpsc::{Receiver};
-use std::io::{Write};
-use std::time::{Duration, Instant};
-
-pub mod trace;
-
-/// begin a profile thread, if not already running
-pub fn begin(sess: &Session) {
- use std::thread;
- use std::sync::mpsc::{channel};
- let (tx, rx) = channel();
- if profq_set_chan(sess, tx) {
- thread::spawn(move || profile_queries_thread(rx));
- }
-}
-
-/// dump files with profiling information to the given base path, and
-/// wait for this dump to complete.
-///
-/// wraps the RPC (send/recv channel logic) of requesting a dump.
-pub fn dump(sess: &Session, path: String) {
- use std::sync::mpsc::{channel};
- let (tx, rx) = channel();
- let params = ProfQDumpParams {
- path,
- ack: tx,
- // FIXME: Add another compiler flag to toggle whether this log
- // is written; false for now
- dump_profq_msg_log: true,
- };
- profq_msg(sess, ProfileQueriesMsg::Dump(params));
- let _ = rx.recv().unwrap();
-}
-
-// State for parsing recursive trace structure in separate thread, via messages
-#[derive(Clone, Eq, PartialEq)]
-enum ParseState {
- // No (local) parse state; may be parsing a tree, focused on a
- // sub-tree that could be anything.
- Clear,
- // Have Query information from the last message
- HaveQuery(trace::Query, Instant),
- // Have "time-begin" information from the last message (doit flag, and message)
- HaveTimeBegin(String, Instant),
- // Have "task-begin" information from the last message
- HaveTaskBegin(DepNode, Instant),
-}
-struct StackFrame {
- pub parse_st: ParseState,
- pub traces: Vec<trace::Rec>,
-}
-
-fn total_duration(traces: &[trace::Rec]) -> Duration {
- Duration::new(0, 0) + traces.iter().map(|t| t.dur_total).sum()
-}
-
-// profiling thread; retains state (in local variables) and dump traces, upon request.
-fn profile_queries_thread(r: Receiver<ProfileQueriesMsg>) {
- use self::trace::*;
- use std::fs::File;
-
- let mut profq_msgs: Vec<ProfileQueriesMsg> = vec![];
- let mut frame: StackFrame = StackFrame { parse_st: ParseState::Clear, traces: vec![] };
- let mut stack: Vec<StackFrame> = vec![];
- loop {
- let msg = r.recv();
- if let Err(_recv_err) = msg {
- // FIXME: Perhaps do something smarter than simply quitting?
- break
- };
- let msg = msg.unwrap();
- debug!("profile_queries_thread: {:?}", msg);
-
- // Meta-level versus _actual_ queries messages
- match msg {
- ProfileQueriesMsg::Halt => return,
- ProfileQueriesMsg::Dump(params) => {
- assert!(stack.is_empty());
- assert!(frame.parse_st == ParseState::Clear);
-
- // write log of all messages
- if params.dump_profq_msg_log {
- let mut log_file =
- File::create(format!("{}.log.txt", params.path)).unwrap();
- for m in profq_msgs.iter() {
- writeln!(&mut log_file, "{:?}", m).unwrap()
- };
- }
-
- // write HTML file, and counts file
- let html_path = format!("{}.html", params.path);
- let mut html_file = File::create(&html_path).unwrap();
-
- let counts_path = format!("{}.counts.txt", params.path);
- let mut counts_file = File::create(&counts_path).unwrap();
-
- writeln!(html_file,
- "<html>\n<head>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{}\">",
- "profile_queries.css").unwrap();
- writeln!(html_file, "<style>").unwrap();
- trace::write_style(&mut html_file);
- writeln!(html_file, "</style>\n</head>\n<body>").unwrap();
- trace::write_traces(&mut html_file, &mut counts_file, &frame.traces);
- writeln!(html_file, "</body>\n</html>").unwrap();
-
- let ack_path = format!("{}.ack", params.path);
- let ack_file = File::create(&ack_path).unwrap();
- drop(ack_file);
-
- // Tell main thread that we are done, e.g., so it can exit
- params.ack.send(()).unwrap();
- }
- // Actual query message:
- msg => {
- // Record msg in our log
- profq_msgs.push(msg.clone());
- // Respond to the message, knowing that we've already handled Halt and Dump, above.
- match (frame.parse_st.clone(), msg) {
- (_, ProfileQueriesMsg::Halt) | (_, ProfileQueriesMsg::Dump(_)) => {
- unreachable!();
- },
- // Parse State: Clear
- (ParseState::Clear,
- ProfileQueriesMsg::QueryBegin(span, querymsg)) => {
- let start = Instant::now();
- frame.parse_st = ParseState::HaveQuery
- (Query { span, msg: querymsg }, start)
- },
- (ParseState::Clear,
- ProfileQueriesMsg::CacheHit) => {
- panic!("parse error: unexpected CacheHit; expected QueryBegin")
- },
- (ParseState::Clear,
- ProfileQueriesMsg::ProviderBegin) => {
- panic!("parse error: expected QueryBegin before beginning a provider")
- },
- (ParseState::Clear,
- ProfileQueriesMsg::ProviderEnd) => {
- let provider_extent = frame.traces;
- match stack.pop() {
- None =>
- panic!("parse error: expected a stack frame; found an empty stack"),
- Some(old_frame) => {
- match old_frame.parse_st {
- ParseState::HaveQuery(q, start) => {
- let duration = start.elapsed();
- frame = StackFrame{
- parse_st: ParseState::Clear,
- traces: old_frame.traces
- };
- let dur_extent = total_duration(&provider_extent);
- let trace = Rec {
- effect: Effect::QueryBegin(q, CacheCase::Miss),
- extent: Box::new(provider_extent),
- start: start,
- dur_self: duration - dur_extent,
- dur_total: duration,
- };
- frame.traces.push( trace );
- },
- _ => panic!("internal parse error: malformed parse stack")
- }
- }
- }
- },
- (ParseState::Clear,
- ProfileQueriesMsg::TimeBegin(msg)) => {
- let start = Instant::now();
- frame.parse_st = ParseState::HaveTimeBegin(msg, start);
- stack.push(frame);
- frame = StackFrame{parse_st: ParseState::Clear, traces: vec![]};
- },
- (_, ProfileQueriesMsg::TimeBegin(_)) => {
- panic!("parse error; did not expect time begin here");
- },
- (ParseState::Clear,
- ProfileQueriesMsg::TimeEnd) => {
- let provider_extent = frame.traces;
- match stack.pop() {
- None =>
- panic!("parse error: expected a stack frame; found an empty stack"),
- Some(old_frame) => {
- match old_frame.parse_st {
- ParseState::HaveTimeBegin(msg, start) => {
- let duration = start.elapsed();
- frame = StackFrame{
- parse_st: ParseState::Clear,
- traces: old_frame.traces
- };
- let dur_extent = total_duration(&provider_extent);
- let trace = Rec {
- effect: Effect::TimeBegin(msg),
- extent: Box::new(provider_extent),
- start: start,
- dur_total: duration,
- dur_self: duration - dur_extent,
- };
- frame.traces.push( trace );
- },
- _ => panic!("internal parse error: malformed parse stack")
- }
- }
- }
- },
- (_, ProfileQueriesMsg::TimeEnd) => {
- panic!("parse error")
- },
- (ParseState::Clear,
- ProfileQueriesMsg::TaskBegin(key)) => {
- let start = Instant::now();
- frame.parse_st = ParseState::HaveTaskBegin(key, start);
- stack.push(frame);
- frame = StackFrame{ parse_st: ParseState::Clear, traces: vec![] };
- },
- (_, ProfileQueriesMsg::TaskBegin(_)) => {
- panic!("parse error; did not expect time begin here");
- },
- (ParseState::Clear,
- ProfileQueriesMsg::TaskEnd) => {
- let provider_extent = frame.traces;
- match stack.pop() {
- None =>
- panic!("parse error: expected a stack frame; found an empty stack"),
- Some(old_frame) => {
- match old_frame.parse_st {
- ParseState::HaveTaskBegin(key, start) => {
- let duration = start.elapsed();
- frame = StackFrame{
- parse_st: ParseState::Clear,
- traces: old_frame.traces
- };
- let dur_extent = total_duration(&provider_extent);
- let trace = Rec {
- effect: Effect::TaskBegin(key),
- extent: Box::new(provider_extent),
- start: start,
- dur_total: duration,
- dur_self: duration - dur_extent,
- };
- frame.traces.push( trace );
- },
- _ => panic!("internal parse error: malformed parse stack")
- }
- }
- }
- },
- (_, ProfileQueriesMsg::TaskEnd) => {
- panic!("parse error")
- },
- // Parse State: HaveQuery
- (ParseState::HaveQuery(q,start),
- ProfileQueriesMsg::CacheHit) => {
- let duration = start.elapsed();
- let trace : Rec = Rec{
- effect: Effect::QueryBegin(q, CacheCase::Hit),
- extent: Box::new(vec![]),
- start: start,
- dur_self: duration,
- dur_total: duration,
- };
- frame.traces.push( trace );
- frame.parse_st = ParseState::Clear;
- },
- (ParseState::HaveQuery(_, _),
- ProfileQueriesMsg::ProviderBegin) => {
- stack.push(frame);
- frame = StackFrame{ parse_st: ParseState::Clear, traces: vec![] };
- },
-
- // Parse errors:
-
- (ParseState::HaveQuery(q, _),
- ProfileQueriesMsg::ProviderEnd) => {
- panic!("parse error: unexpected ProviderEnd; \
- expected something else to follow BeginQuery for {:?}", q)
- },
- (ParseState::HaveQuery(q1, _),
- ProfileQueriesMsg::QueryBegin(span2, querymsg2)) => {
- panic!("parse error: unexpected QueryBegin; \
- earlier query is unfinished: {:?} and now {:?}",
- q1, Query{span:span2, msg: querymsg2})
- },
- (ParseState::HaveTimeBegin(_, _), _) => {
- unreachable!()
- },
- (ParseState::HaveTaskBegin(_, _), _) => {
- unreachable!()
- },
- }
- }
- }
- }
-}
+++ /dev/null
-use super::*;
-use syntax_pos::SpanData;
-use rustc_data_structures::fx::FxHashMap;
-use rustc::util::common::QueryMsg;
-use std::fs::File;
-use std::time::{Duration, Instant};
-use rustc::dep_graph::{DepNode};
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Query {
- pub span: SpanData,
- pub msg: QueryMsg,
-}
-pub enum Effect {
- QueryBegin(Query, CacheCase),
- TimeBegin(String),
- TaskBegin(DepNode),
-}
-pub enum CacheCase {
- Hit, Miss
-}
-/// Recursive trace structure
-pub struct Rec {
- pub effect: Effect,
- pub start: Instant,
- pub dur_self: Duration,
- pub dur_total: Duration,
- pub extent: Box<Vec<Rec>>,
-}
-pub struct QueryMetric {
- pub count: usize,
- pub dur_self: Duration,
- pub dur_total: Duration,
-}
-
-fn cons(s: &str) -> String {
- let first = s.split(|d| d == '(' || d == '{').next();
- assert!(first.is_some() && first != Some(""));
- first.unwrap().to_owned()
-}
-
-pub fn cons_of_query_msg(q: &trace::Query) -> String {
- cons(&format!("{:?}", q.msg))
-}
-
-pub fn cons_of_key(k: &DepNode) -> String {
- cons(&format!("{:?}", k))
-}
-
-// First return value is text; second return value is a CSS class
-pub fn html_of_effect(eff: &Effect) -> (String, String) {
- match *eff {
- Effect::TimeBegin(ref msg) => {
- (msg.clone(),
- "time-begin".to_string())
- },
- Effect::TaskBegin(ref key) => {
- let cons = cons_of_key(key);
- (cons.clone(), format!("{} task-begin", cons))
- },
- Effect::QueryBegin(ref qmsg, ref cc) => {
- let cons = cons_of_query_msg(qmsg);
- (cons.clone(),
- format!("{} {}",
- cons,
- match *cc {
- CacheCase::Hit => "hit",
- CacheCase::Miss => "miss",
- }))
- }
- }
-}
-
-// First return value is text; second return value is a CSS class
-fn html_of_duration(_start: &Instant, dur: &Duration) -> (String, String) {
- use rustc::util::common::duration_to_secs_str;
- (duration_to_secs_str(dur.clone()), String::new())
-}
-
-fn html_of_fraction(frac: f64) -> (String, &'static str) {
- let css = {
- if frac > 0.50 { "frac-50" }
- else if frac > 0.40 { "frac-40" }
- else if frac > 0.30 { "frac-30" }
- else if frac > 0.20 { "frac-20" }
- else if frac > 0.10 { "frac-10" }
- else if frac > 0.05 { "frac-05" }
- else if frac > 0.02 { "frac-02" }
- else if frac > 0.01 { "frac-01" }
- else if frac > 0.001 { "frac-001" }
- else { "frac-0" }
- };
- let percent = frac * 100.0;
-
- if percent > 0.1 {
- (format!("{:.1}%", percent), css)
- } else {
- ("< 0.1%".to_string(), css)
- }
-}
-
-fn total_duration(traces: &[Rec]) -> Duration {
- Duration::new(0, 0) + traces.iter().map(|t| t.dur_total).sum()
-}
-
-fn duration_div(nom: Duration, den: Duration) -> f64 {
- fn to_nanos(d: Duration) -> u64 {
- d.as_secs() * 1_000_000_000 + d.subsec_nanos() as u64
- }
-
- to_nanos(nom) as f64 / to_nanos(den) as f64
-}
-
-fn write_traces_rec(file: &mut File, traces: &[Rec], total: Duration, depth: usize) {
- for t in traces {
- let (eff_text, eff_css_classes) = html_of_effect(&t.effect);
- let (dur_text, dur_css_classes) = html_of_duration(&t.start, &t.dur_total);
- let fraction = duration_div(t.dur_total, total);
- let percent = fraction * 100.0;
- let (frc_text, frc_css_classes) = html_of_fraction(fraction);
- writeln!(file, "<div class=\"trace depth-{} extent-{}{} {} {} {}\">",
- depth,
- t.extent.len(),
- /* Heuristic for 'important' CSS class: */
- if t.extent.len() > 5 || percent >= 1.0 { " important" } else { "" },
- eff_css_classes,
- dur_css_classes,
- frc_css_classes,
- ).unwrap();
- writeln!(file, "<div class=\"eff\">{}</div>", eff_text).unwrap();
- writeln!(file, "<div class=\"dur\">{}</div>", dur_text).unwrap();
- writeln!(file, "<div class=\"frc\">{}</div>", frc_text).unwrap();
- write_traces_rec(file, &t.extent, total, depth + 1);
- writeln!(file, "</div>").unwrap();
- }
-}
-
-fn compute_counts_rec(counts: &mut FxHashMap<String,QueryMetric>, traces: &[Rec]) {
- counts.reserve(traces.len());
- for t in traces.iter() {
- match t.effect {
- Effect::TimeBegin(ref msg) => {
- let qm = match counts.get(msg) {
- Some(_qm) => panic!("TimeBegin with non-unique, repeat message"),
- None => QueryMetric {
- count: 1,
- dur_self: t.dur_self,
- dur_total: t.dur_total,
- }
- };
- counts.insert(msg.clone(), qm);
- },
- Effect::TaskBegin(ref key) => {
- let cons = cons_of_key(key);
- let qm = match counts.get(&cons) {
- Some(qm) =>
- QueryMetric {
- count: qm.count + 1,
- dur_self: qm.dur_self + t.dur_self,
- dur_total: qm.dur_total + t.dur_total,
- },
- None => QueryMetric {
- count: 1,
- dur_self: t.dur_self,
- dur_total: t.dur_total,
- }
- };
- counts.insert(cons, qm);
- },
- Effect::QueryBegin(ref qmsg, ref _cc) => {
- let qcons = cons_of_query_msg(qmsg);
- let qm = match counts.get(&qcons) {
- Some(qm) =>
- QueryMetric {
- count: qm.count + 1,
- dur_total: qm.dur_total + t.dur_total,
- dur_self: qm.dur_self + t.dur_self
- },
- None => QueryMetric {
- count: 1,
- dur_total: t.dur_total,
- dur_self: t.dur_self,
- }
- };
- counts.insert(qcons, qm);
- }
- }
- compute_counts_rec(counts, &t.extent)
- }
-}
-
-pub fn write_counts(count_file: &mut File, counts: &mut FxHashMap<String, QueryMetric>) {
- use rustc::util::common::duration_to_secs_str;
- use std::cmp::Reverse;
-
- let mut data = counts.iter().map(|(ref cons, ref qm)|
- (cons.clone(), qm.count.clone(), qm.dur_total.clone(), qm.dur_self.clone())
- ).collect::<Vec<_>>();
-
- data.sort_by_key(|k| Reverse(k.3));
- for (cons, count, dur_total, dur_self) in data {
- writeln!(count_file, "{}, {}, {}, {}",
- cons, count,
- duration_to_secs_str(dur_total),
- duration_to_secs_str(dur_self)
- ).unwrap();
- }
-}
-
-pub fn write_traces(html_file: &mut File, counts_file: &mut File, traces: &[Rec]) {
- let capacity = traces.iter().fold(0, |acc, t| acc + 1 + t.extent.len());
- let mut counts = FxHashMap::with_capacity_and_hasher(capacity, Default::default());
- compute_counts_rec(&mut counts, traces);
- write_counts(counts_file, &mut counts);
-
- let total: Duration = total_duration(traces);
- write_traces_rec(html_file, traces, total, 0)
-}
-
-pub fn write_style(html_file: &mut File) {
- write!(html_file, "{}", "
-body {
- font-family: sans-serif;
- background: black;
-}
-.trace {
- color: black;
- display: inline-block;
- border-style: solid;
- border-color: red;
- border-width: 1px;
- border-radius: 5px;
- padding: 0px;
- margin: 1px;
- font-size: 0px;
-}
-.task-begin {
- border-width: 1px;
- color: white;
- border-color: #ff8;
- font-size: 0px;
-}
-.miss {
- border-color: red;
- border-width: 1px;
-}
-.extent-0 {
- padding: 2px;
-}
-.time-begin {
- border-width: 4px;
- font-size: 12px;
- color: white;
- border-color: #afa;
-}
-.important {
- border-width: 3px;
- font-size: 12px;
- color: white;
- border-color: #f77;
-}
-.hit {
- padding: 0px;
- border-color: blue;
- border-width: 3px;
-}
-.eff {
- color: #fff;
- display: inline-block;
-}
-.frc {
- color: #7f7;
- display: inline-block;
-}
-.dur {
- display: none
-}
-.frac-50 {
- padding: 10px;
- border-width: 10px;
- font-size: 32px;
-}
-.frac-40 {
- padding: 8px;
- border-width: 8px;
- font-size: 24px;
-}
-.frac-30 {
- padding: 6px;
- border-width: 6px;
- font-size: 18px;
-}
-.frac-20 {
- padding: 4px;
- border-width: 6px;
- font-size: 16px;
-}
-.frac-10 {
- padding: 2px;
- border-width: 6px;
- font-size: 14px;
-}
-").unwrap();
-}
stderr: &Option<Arc<Mutex<Vec<u8>>>>,
f: F,
) -> R {
- use rayon::{ThreadPool, ThreadPoolBuilder};
+ use rayon::{ThreadBuilder, ThreadPool, ThreadPoolBuilder};
let gcx_ptr = &Lock::new(0);
let mut config = ThreadPoolBuilder::new()
+ .thread_name(|_| "rustc".to_string())
.acquire_thread_handler(jobserver::acquire_thread)
.release_thread_handler(jobserver::release_thread)
.num_threads(threads)
// the thread local rustc uses. syntax_globals and syntax_pos_globals are
// captured and set on the new threads. ty::tls::with_thread_locals sets up
// thread local callbacks from libsyntax
- let main_handler = move |worker: &mut dyn FnMut()| {
+ let main_handler = move |thread: ThreadBuilder| {
syntax::GLOBALS.set(syntax_globals, || {
syntax_pos::GLOBALS.set(syntax_pos_globals, || {
if let Some(stderr) = stderr {
io::set_panic(Some(box Sink(stderr.clone())));
}
ty::tls::with_thread_locals(|| {
- ty::tls::GCX_PTR.set(gcx_ptr, || worker())
+ ty::tls::GCX_PTR.set(gcx_ptr, || thread.run())
})
})
})
};
- ThreadPool::scoped_pool(config, main_handler, with_pool).unwrap()
+ config.build_scoped(main_handler, with_pool).unwrap()
})
})
})
syntax::register_diagnostics! {
;
- E0721, // `await` keyword
+// E0721, // `await` keyword
}
};
}
+ let is_non_exhaustive =
+ def.non_enum_variant().is_field_list_non_exhaustive();
+ if is_non_exhaustive && !def.did.is_local() {
+ return FfiUnsafe {
+ ty,
+ reason: "this struct is non-exhaustive",
+ help: None,
+ };
+ }
+
if def.non_enum_variant().fields.is_empty() {
return FfiUnsafe {
ty,
}
}
+ if def.is_variant_list_non_exhaustive() && !def.did.is_local() {
+ return FfiUnsafe {
+ ty,
+ reason: "this enum is non-exhaustive",
+ help: None,
+ };
+ }
+
// Check the contained variants.
for variant in &def.variants {
+ let is_non_exhaustive = variant.is_field_list_non_exhaustive();
+ if is_non_exhaustive && !variant.def_id.is_local() {
+ return FfiUnsafe {
+ ty,
+ reason: "this enum has non-exhaustive variants",
+ help: None,
+ };
+ }
+
for field in &variant.fields {
let field_ty = cx.normalize_erasing_regions(
ParamEnv::reveal_all(),
proc-macro = true
[dependencies]
-synstructure = "0.10.2"
-syn = { version = "0.15.22", features = ["full"] }
-proc-macro2 = "0.4.24"
-quote = "0.6.10"
+synstructure = "0.12.1"
+syn = { version = "1", features = ["full"] }
+proc-macro2 = "1"
+quote = "1"
itertools = "0.8"
};
for attr in &field.attrs {
if let Ok(meta) = attr.parse_meta() {
- if &meta.name().to_string() != "stable_hasher" {
+ if !meta.path().is_ident("stable_hasher") {
continue;
}
let mut any_attr = false;
if let Meta::List(list) = meta {
for nested in list.nested.iter() {
if let NestedMeta::Meta(meta) = nested {
- if &meta.name().to_string() == "ignore" {
+ if meta.path().is_ident("ignore") {
attrs.ignore = true;
any_attr = true;
}
- if &meta.name().to_string() == "project" {
+ if meta.path().is_ident("project") {
if let Meta::List(list) = meta {
if let Some(nested) = list.nested.iter().next() {
if let NestedMeta::Meta(meta) = nested {
- attrs.project = Some(meta.name());
+ attrs.project = meta.path().get_ident().cloned();
any_attr = true;
}
}
dep_node_force_stream.extend(quote! {
DepKind::#name => {
if let Some(key) = RecoverKey::recover($tcx, $dep_node) {
- force_ex!($tcx, #name, key);
+ $tcx.force_query::<crate::ty::query::queries::#name<'_>>(
+ key,
+ DUMMY_SP,
+ *$dep_node
+ );
} else {
return false;
}
// resolve! Does this work? Unsure! That's what the issue is about
*providers = Providers {
is_dllimport_foreign_item: |tcx, id| {
- tcx.native_library_kind(id) == Some(NativeLibraryKind::NativeUnknown)
+ match tcx.native_library_kind(id) {
+ Some(NativeLibraryKind::NativeUnknown) |
+ Some(NativeLibraryKind::NativeRawDylib) => true,
+ _ => false,
+ }
},
is_statically_included_foreign_item: |tcx, id| {
match tcx.native_library_kind(id) {
self.entry_unless_proc_macro(id)
.and_then(|entry| entry.mir.map(|mir| mir.decode((self, tcx))))
.unwrap_or_else(|| {
- bug!("get_optimized_mir: missing MIR for `{:?}", self.local_def_id(id))
+ bug!("get_optimized_mir: missing MIR for `{:?}`", self.local_def_id(id))
})
}
"static-nobundle" => cstore::NativeStaticNobundle,
"dylib" => cstore::NativeUnknown,
"framework" => cstore::NativeFramework,
+ "raw-dylib" => cstore::NativeRawDylib,
k => {
struct_span_err!(self.tcx.sess, item.span(), E0458,
"unknown kind: `{}`", k)
GateIssue::Language,
"kind=\"static-nobundle\" is unstable");
}
+ if lib.kind == cstore::NativeRawDylib &&
+ !self.tcx.features().raw_dylib {
+ feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
+ sym::raw_dylib,
+ span.unwrap_or_else(|| syntax_pos::DUMMY_SP),
+ GateIssue::Language,
+ "kind=\"raw-dylib\" is unstable");
+ }
self.libs.push(lib);
}
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
+ debug!(
+ "report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
+ place_desc,
+ explanation
+ );
let err = match (place_desc, explanation) {
(Some(_), _) if self.is_place_thread_local(root_place) => {
self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
span,
&format!("`{}`", name),
),
+ (
+ Some(ref name),
+ BorrowExplanation::MustBeValidFor {
+ category: category @ ConstraintCategory::OpaqueType,
+ from_closure: false,
+ ref region_name,
+ span,
+ ..
+ },
+
+ ) if borrow_spans.for_generator() => self.report_escaping_closure_capture(
+ borrow_spans.args_or_use(),
+ borrow_span,
+ region_name,
+ category,
+ span,
+ &format!("`{}`", name),
+ ),
(
ref name,
BorrowExplanation::MustBeValidFor {
ConstraintCategory::Return => {
err.span_note(constraint_span, "closure is returned here");
}
+ ConstraintCategory::OpaqueType => {
+ err.span_note(constraint_span, "generator is returned here");
+ }
ConstraintCategory::CallArgument => {
fr_name.highlight_region_name(&mut err);
err.span_note(
self.is_mutable(place.as_ref(), is_local_mutation_allowed),
self.errors_buffer.is_empty()
) {
- // rust-lang/rust#46908: In pure NLL mode this code path should
- // be unreachable (and thus we signal an ICE in the else branch here).
- span_bug!(
- span,
+ // rust-lang/rust#46908: In pure NLL mode this code path should be
+ // unreachable, but we use `delay_span_bug` because we can hit this when
+ // dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
+ // enabled. We don't want to ICE for that case, as other errors will have
+ // been emitted (#52262).
+ self.infcx.tcx.sess.delay_span_bug(span, &format!(
"Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
place,
kind,
- );
+ ));
}
return false;
}
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection,
};
use rustc::ty::fold::TypeFoldable;
-use rustc::ty::{self, GeneratorSubsts, RegionVid, Ty};
+use rustc::ty::{self, RegionVid, Ty};
use rustc::ty::subst::SubstsRef;
pub(super) fn generate_constraints<'cx, 'tcx>(
self.super_ty(ty);
}
- /// We sometimes have `generator_substs` within an rvalue, or within a
- /// call. Make them live at the location where they appear.
- fn visit_generator_substs(&mut self, substs: &GeneratorSubsts<'tcx>, location: Location) {
- self.add_regular_live_constraint(*substs, location);
- self.super_generator_substs(substs);
- }
-
fn visit_statement(
&mut self,
statement: &Statement<'tcx>,
mod find_use;
+#[derive(Debug)]
pub(in crate::borrow_check) enum BorrowExplanation {
UsedLater(LaterUseKind, Span),
UsedLaterInLoop(LaterUseKind, Span),
Unexplained,
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub(in crate::borrow_check) enum LaterUseKind {
TraitCapture,
ClosureCapture,
use rustc::ty::subst::SubstsRef;
-use rustc::ty::{self, GeneratorSubsts, Ty, TypeFoldable};
+use rustc::ty::{self, Ty, TypeFoldable};
use rustc::mir::{Location, Body, Promoted};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
*constant = self.renumber_regions(&*constant);
}
-
- fn visit_generator_substs(&mut self,
- substs: &mut GeneratorSubsts<'tcx>,
- location: Location) {
- debug!(
- "visit_generator_substs(substs={:?}, location={:?})",
- substs,
- location,
- );
-
- *substs = self.renumber_regions(substs);
-
- debug!("visit_generator_substs: substs={:?}", substs);
- }
}
PlaceTy { ty, variant_index: Some(variant_index) } => match ty.kind {
ty::Adt(adt_def, substs) => (&adt_def.variants[variant_index], substs),
ty::Generator(def_id, substs, _) => {
- let mut variants = substs.state_tys(def_id, tcx);
+ let mut variants = substs.as_generator().state_tys(def_id, tcx);
let mut variant = match variants.nth(variant_index.into()) {
Some(v) => v,
None => {
bug!("variant_index of generator out of range: {:?}/{:?}",
variant_index,
- substs.state_tys(def_id, tcx).count())
+ substs.as_generator().state_tys(def_id, tcx).count())
}
};
return match variant.nth(field.index()) {
ty::Generator(def_id, substs, _) => {
// Only prefix fields (upvars and current state) are
// accessible without a variant index.
- return match substs.prefix_tys(def_id, tcx).nth(field.index()) {
+ return match substs.as_generator().prefix_tys(def_id, tcx).nth(field.index()) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
- field_count: substs.prefix_tys(def_id, tcx).count(),
+ field_count: substs.as_generator().prefix_tys(def_id, tcx).count(),
}),
}
}
// It doesn't make sense to look at a field beyond the prefix;
// these require a variant index, and are not initialized in
// aggregate rvalues.
- match substs.prefix_tys(def_id, tcx).nth(field_index) {
+ match substs.as_generator().prefix_tys(def_id, tcx).nth(field_index) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
- field_count: substs.prefix_tys(def_id, tcx).count(),
+ field_count: substs.as_generator().prefix_tys(def_id, tcx).count(),
}),
}
}
// these extra requirements are basically like where
// clauses on the struct.
AggregateKind::Closure(def_id, substs)
- | AggregateKind::Generator(def_id, ty::GeneratorSubsts { substs }, _) => {
+ | AggregateKind::Generator(def_id, substs, _) => {
self.prove_closure_bounds(tcx, *def_id, substs, location)
}
use rustc::middle::lang_items;
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::{InternalSubsts, SubstsRef, Subst};
-use rustc::ty::{self, GeneratorSubsts, RegionVid, Ty, TyCtxt};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
use rustc_index::vec::{Idx, IndexVec};
use rustc_errors::DiagnosticBuilder;
/// The MIR is a generator. The signature is that generators take
/// no parameters and return the result of
/// `ClosureSubsts::generator_return_ty`.
- Generator(DefId, ty::GeneratorSubsts<'tcx>, hir::GeneratorMovability),
+ Generator(DefId, SubstsRef<'tcx>, hir::GeneratorMovability),
/// The MIR is a fn item with the given `DefId` and substs. The signature
/// of the function can be bound then with the `fn_sig` query.
substs.as_closure().upvar_tys(def_id, tcx)
),
DefiningTy::Generator(def_id, substs, _) => {
- Either::Right(Either::Left(substs.upvar_tys(def_id, tcx)))
+ Either::Right(Either::Left(substs.as_generator().upvar_tys(def_id, tcx)))
}
DefiningTy::FnDef(..) | DefiningTy::Const(..) => {
Either::Right(Either::Right(iter::empty()))
err.note(&format!(
"defining type: {:?} with generator substs {:#?}",
def_id,
- &substs.substs[..]
+ &substs[..]
));
// FIXME: As above, we'd like to print out the region
let yield_ty = match defining_ty {
DefiningTy::Generator(def_id, substs, _) => {
- Some(substs.yield_ty(def_id, self.infcx.tcx))
+ Some(substs.as_generator().yield_ty(def_id, self.infcx.tcx))
}
_ => None,
};
let identity_substs = InternalSubsts::identity_for_item(tcx, closure_base_def_id);
let fr_substs = match defining_ty {
DefiningTy::Closure(_, ref substs)
- | DefiningTy::Generator(_, GeneratorSubsts { ref substs }, _) => {
+ | DefiningTy::Generator(_, ref substs, _) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureSubsts are
// inherited from the `closure_base_def_id`.
DefiningTy::Generator(def_id, substs, movability) => {
assert_eq!(self.mir_def_id, def_id);
- let output = substs.return_ty(def_id, tcx);
+ let output = substs.as_generator().return_ty(def_id, tcx);
let generator_ty = tcx.mk_generator(def_id, substs, movability);
let inputs_and_output = self.infcx.tcx.intern_type_list(&[generator_ty, output]);
ty::Binder::dummy(inputs_and_output)
expr_span,
scope,
result,
- expr.ty,
);
}
upvar_span,
temp_lifetime,
temp,
- upvar_ty,
);
}
expr_span,
temp_lifetime,
temp,
- expr_ty,
DropKind::Storage,
);
}
expr_span,
temp_lifetime,
temp,
- expr_ty,
DropKind::Value,
);
}
kind: StatementKind::StorageLive(local_id),
},
);
- let var_ty = self.local_decls[local_id].ty;
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
- self.schedule_drop(span, region_scope, local_id, var_ty, DropKind::Storage);
+ self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
Place::from(local_id)
}
pub fn schedule_drop_for_binding(&mut self, var: HirId, span: Span, for_guard: ForGuard) {
let local_id = self.var_local_id(var, for_guard);
- let var_ty = self.local_decls[local_id].ty;
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
self.schedule_drop(
span,
region_scope,
local_id,
- var_ty,
DropKind::Value,
);
}
let (yield_ty, return_ty) = if body.generator_kind.is_some() {
let gen_sig = match ty.kind {
ty::Generator(gen_def_id, gen_substs, ..) =>
- gen_substs.sig(gen_def_id, tcx),
+ gen_substs.as_generator().sig(gen_def_id, tcx),
_ =>
span_bug!(tcx.hir().span(id),
"generator w/o generator type: {:?}", ty),
// Function arguments always get the first Local indices after the return place
let local = Local::new(index + 1);
let place = Place::from(local);
- let &ArgInfo(ty, opt_ty_info, arg_opt, ref self_binding) = arg_info;
+ let &ArgInfo(_, opt_ty_info, arg_opt, ref self_binding) = arg_info;
// Make sure we drop (parts of) the argument even when not matched on.
self.schedule_drop(
arg_opt.as_ref().map_or(ast_body.span, |arg| arg.pat.span),
- argument_scope, local, ty, DropKind::Value,
+ argument_scope, local, DropKind::Value,
);
if let Some(arg) = arg_opt {
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use crate::hair::{Expr, ExprRef, LintLevel};
use rustc::middle::region;
-use rustc::ty::Ty;
use rustc::hir;
use rustc::mir::*;
use syntax_pos::{DUMMY_SP, Span};
region_scope: region::Scope,
/// Where the body of the loop begins. `None` if block
continue_block: Option<BasicBlock>,
- /// Block to branch into when the loop or block terminates (either by being `break`-en out
- /// from, or by having its condition to become false)
+ /// Block to branch into when the loop or block terminates (either by being
+ /// `break`-en out from, or by having its condition to become false)
break_block: BasicBlock,
- /// The destination of the loop/block expression itself (i.e., where to put the result of a
- /// `break` expression)
+ /// The destination of the loop/block expression itself (i.e., where to put
+ /// the result of a `break` expression)
break_destination: Place<'tcx>,
}
span: Span,
region_scope: region::Scope,
local: Local,
- place_ty: Ty<'tcx>,
) {
- self.schedule_drop(span, region_scope, local, place_ty, DropKind::Storage);
- self.schedule_drop(span, region_scope, local, place_ty, DropKind::Value);
+ self.schedule_drop(span, region_scope, local, DropKind::Storage);
+ self.schedule_drop(span, region_scope, local, DropKind::Value);
}
/// Indicates that `place` should be dropped on exit from
span: Span,
region_scope: region::Scope,
local: Local,
- place_ty: Ty<'tcx>,
drop_kind: DropKind,
) {
- let needs_drop = self.hir.needs_drop(place_ty);
- match drop_kind {
- DropKind::Value => if !needs_drop { return },
+ let needs_drop = match drop_kind {
+ DropKind::Value => {
+ if !self.hir.needs_drop(self.local_decls[local].ty) { return }
+ true
+ },
DropKind::Storage => {
if local.index() <= self.arg_count {
span_bug!(
self.arg_count,
)
}
+ false
}
- }
+ };
for scope in self.scopes.iter_mut() {
let this_scope = scope.region_scope == region_scope;
assert_eq!(1, self.body.arg_count);
}
- fn statement_effect(&self,
- sets: &mut GenKillSet<Local>,
- loc: Location) {
- self.check_for_move(sets, loc);
+ fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
+ // If we borrow or assign to a place then it needs storage for that
+ // statement.
self.check_for_borrow(sets, loc);
let stmt = &self.body[loc.block].statements[loc.statement_index];
match stmt.kind {
- StatementKind::StorageLive(l) => sets.gen(l),
StatementKind::StorageDead(l) => sets.kill(l),
StatementKind::Assign(box(ref place, _))
| StatementKind::SetDiscriminant { box ref place, .. } => {
}
}
- fn terminator_effect(&self,
- sets: &mut GenKillSet<Local>,
- loc: Location) {
+ fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
+ // If we move from a place then only stops needing storage *after*
+ // that statement.
self.check_for_move(sets, loc);
+ }
+
+ fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
self.check_for_borrow(sets, loc);
+
+ if let TerminatorKind::Call {
+ destination: Some((Place { base: PlaceBase::Local(local), .. }, _)),
+ ..
+ } = self.body[loc.block].terminator().kind {
+ sets.gen(local);
+ }
+ }
+
+ fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
+ // For call terminators the destination requires storage for the call
+ // and after the call returns successfully, but not after a panic.
+ // Since `propagate_call_unwind` doesn't exist, we have to kill the
+ // destination here, and then gen it again in `propagate_call_return`.
+ if let TerminatorKind::Call {
+ destination: Some((Place { base: PlaceBase::Local(local), projection: box [] }, _)),
+ ..
+ } = self.body[loc.block].terminator().kind {
+ sets.kill(local);
+ }
+ self.check_for_move(sets, loc);
}
fn propagate_call_return(
"##,
E0388: r##"
-E0388 was removed and is no longer issued.
+#### Note: this error code is no longer emitted by the compiler.
"##,
E0389: r##"
}
impl<'tcx> Constructor<'tcx> {
+ fn is_slice(&self) -> bool {
+ match self {
+ Slice { .. } => true,
+ _ => false,
+ }
+ }
+
fn variant_index_for_adt<'a>(
&self,
cx: &MatchCheckCtxt<'a, 'tcx>,
}
impl<'tcx> IntRange<'tcx> {
+ #[inline]
+ fn is_integral(ty: Ty<'_>) -> bool {
+ match ty.kind {
+ ty::Char | ty::Int(_) | ty::Uint(_) => true,
+ _ => false,
+ }
+ }
+
+ #[inline]
+ fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
+ match ty.kind {
+ ty::Char => Some((Size::from_bytes(4), 0)),
+ ty::Int(ity) => {
+ let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
+ Some((size, 1u128 << (size.bits() as u128 - 1)))
+ }
+ ty::Uint(uty) => Some((Integer::from_attr(&tcx, UnsignedInt(uty)).size(), 0)),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ fn from_const(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ value: &Const<'tcx>,
+ ) -> Option<IntRange<'tcx>> {
+ if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) {
+ let ty = value.ty;
+ let val = if let ConstValue::Scalar(Scalar::Raw { data, size }) = value.val {
+ // For this specific pattern we can skip a lot of effort and go
+ // straight to the result, after doing a bit of checking. (We
+ // could remove this branch and just use the next branch, which
+ // is more general but much slower.)
+ Scalar::<()>::check_raw(data, size, target_size);
+ data
+ } else if let Some(val) = value.try_eval_bits(tcx, param_env, ty) {
+ // This is a more general form of the previous branch.
+ val
+ } else {
+ return None
+ };
+ let val = val ^ bias;
+ Some(IntRange { range: val..=val, ty })
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ fn from_range(
+ tcx: TyCtxt<'tcx>,
+ lo: u128,
+ hi: u128,
+ ty: Ty<'tcx>,
+ end: &RangeEnd,
+ ) -> Option<IntRange<'tcx>> {
+ if Self::is_integral(ty) {
+ // Perform a shift if the underlying types are signed,
+ // which makes the interval arithmetic simpler.
+ let bias = IntRange::signed_bias(tcx, ty);
+ let (lo, hi) = (lo ^ bias, hi ^ bias);
+ // Make sure the interval is well-formed.
+ if lo > hi || lo == hi && *end == RangeEnd::Excluded {
+ None
+ } else {
+ let offset = (*end == RangeEnd::Excluded) as u128;
+ Some(IntRange { range: lo..=(hi - offset), ty })
+ }
+ } else {
+ None
+ }
+ }
+
fn from_ctor(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Option<IntRange<'tcx>> {
// Floating-point ranges are permitted and we don't want
// to consider them when constructing integer ranges.
- fn is_integral(ty: Ty<'_>) -> bool {
- match ty.kind {
- ty::Char | ty::Int(_) | ty::Uint(_) => true,
- _ => false,
- }
- }
-
match ctor {
- ConstantRange(lo, hi, ty, end) if is_integral(ty) => {
- // Perform a shift if the underlying types are signed,
- // which makes the interval arithmetic simpler.
- let bias = IntRange::signed_bias(tcx, ty);
- let (lo, hi) = (lo ^ bias, hi ^ bias);
- // Make sure the interval is well-formed.
- if lo > hi || lo == hi && *end == RangeEnd::Excluded {
- None
- } else {
- let offset = (*end == RangeEnd::Excluded) as u128;
- Some(IntRange { range: lo..=(hi - offset), ty })
- }
- }
- ConstantValue(val) if is_integral(val.ty) => {
- let ty = val.ty;
- if let Some(val) = val.try_eval_bits(tcx, param_env, ty) {
- let bias = IntRange::signed_bias(tcx, ty);
- let val = val ^ bias;
- Some(IntRange { range: val..=val, ty })
- } else {
- None
- }
- }
+ ConstantRange(lo, hi, ty, end) => Self::from_range(tcx, *lo, *hi, ty, end),
+ ConstantValue(val) => Self::from_const(tcx, param_env, val),
_ => None,
}
}
param_env: ty::ParamEnv<'tcx>,
mut pat: &Pat<'tcx>,
) -> Option<IntRange<'tcx>> {
- let range = loop {
+ loop {
match pat.kind {
- box PatKind::Constant { value } => break ConstantValue(value),
- box PatKind::Range(PatRange { lo, hi, end }) => break ConstantRange(
- lo.eval_bits(tcx, param_env, lo.ty),
- hi.eval_bits(tcx, param_env, hi.ty),
- lo.ty,
- end,
- ),
+ box PatKind::Constant { value } => {
+ return Self::from_const(tcx, param_env, value);
+ }
+ box PatKind::Range(PatRange { lo, hi, end }) => {
+ return Self::from_range(
+ tcx,
+ lo.eval_bits(tcx, param_env, lo.ty),
+ hi.eval_bits(tcx, param_env, hi.ty),
+ &lo.ty,
+ &end,
+ );
+ }
box PatKind::AscribeUserType { ref subpattern, .. } => {
pat = subpattern;
},
_ => return None,
}
- };
- Self::from_ctor(tcx, param_env, &range)
+ }
}
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
}
}).collect();
let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect();
- let matrix = Matrix(m.iter().flat_map(|r| {
- specialize(cx, &r, &ctor, &wild_patterns)
- }).collect());
+ let matrix = Matrix(
+ m.iter()
+ .filter_map(|r| specialize(cx, &r, &ctor, &wild_patterns))
+ .collect()
+ );
match specialize(cx, v, &ctor, &wild_patterns) {
Some(v) => match is_useful(cx, &matrix, &v, witness) {
UsefulWithWitness(witnesses) => UsefulWithWitness(
split_ctors
}
-/// Checks whether there exists any shared value in either `ctor` or `pat` by intersecting them.
-fn constructor_intersects_pattern<'p, 'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ctor: &Constructor<'tcx>,
- pat: &'p Pat<'tcx>,
-) -> Option<SmallVec<[&'p Pat<'tcx>; 2]>> {
- if should_treat_range_exhaustively(tcx, ctor) {
- match (IntRange::from_ctor(tcx, param_env, ctor), IntRange::from_pat(tcx, param_env, pat)) {
- (Some(ctor), Some(pat)) => {
- ctor.intersection(&pat).map(|_| {
- let (pat_lo, pat_hi) = pat.range.into_inner();
- let (ctor_lo, ctor_hi) = ctor.range.into_inner();
- assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi);
- smallvec![]
- })
- }
- _ => None,
- }
- } else {
- // Fallback for non-ranges and ranges that involve floating-point numbers, which are not
- // conveniently handled by `IntRange`. For these cases, the constructor may not be a range
- // so intersection actually devolves into being covered by the pattern.
- match constructor_covered_by_range(tcx, param_env, ctor, pat) {
- Ok(true) => Some(smallvec![]),
- Ok(false) | Err(ErrorReported) => None,
- }
- }
-}
-
fn constructor_covered_by_range<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
Some(smallvec![subpattern])
}
- PatKind::Constant { value } => {
- match *constructor {
- Slice(..) => {
- // we extract an `Option` for the pointer because slices of zero elements don't
- // necessarily point to memory, they are usually just integers. The only time
- // they should be pointing to memory is when they are subslices of nonzero
- // slices
- let (alloc, offset, n, ty) = match value.ty.kind {
- ty::Array(t, n) => {
- match value.val {
- ConstValue::ByRef { offset, alloc, .. } => (
- alloc,
- offset,
- n.eval_usize(cx.tcx, cx.param_env),
- t,
- ),
- _ => span_bug!(
- pat.span,
- "array pattern is {:?}", value,
- ),
- }
- },
- ty::Slice(t) => {
- match value.val {
- ConstValue::Slice { data, start, end } => (
- data,
- Size::from_bytes(start as u64),
- (end - start) as u64,
- t,
- ),
- ConstValue::ByRef { .. } => {
- // FIXME(oli-obk): implement `deref` for `ConstValue`
- return None;
- },
- _ => span_bug!(
- pat.span,
- "slice pattern constant must be scalar pair but is {:?}",
- value,
- ),
- }
+ PatKind::Constant { value } if constructor.is_slice() => {
+ // We extract an `Option` for the pointer because slices of zero
+ // elements don't necessarily point to memory, they are usually
+ // just integers. The only time they should be pointing to memory
+ // is when they are subslices of nonzero slices.
+ let (alloc, offset, n, ty) = match value.ty.kind {
+ ty::Array(t, n) => {
+ match value.val {
+ ConstValue::ByRef { offset, alloc, .. } => (
+ alloc,
+ offset,
+ n.eval_usize(cx.tcx, cx.param_env),
+ t,
+ ),
+ _ => span_bug!(
+ pat.span,
+ "array pattern is {:?}", value,
+ ),
+ }
+ },
+ ty::Slice(t) => {
+ match value.val {
+ ConstValue::Slice { data, start, end } => (
+ data,
+ Size::from_bytes(start as u64),
+ (end - start) as u64,
+ t,
+ ),
+ ConstValue::ByRef { .. } => {
+ // FIXME(oli-obk): implement `deref` for `ConstValue`
+ return None;
},
_ => span_bug!(
pat.span,
- "unexpected const-val {:?} with ctor {:?}",
+ "slice pattern constant must be scalar pair but is {:?}",
value,
- constructor,
),
- };
- if wild_patterns.len() as u64 == n {
- // convert a constant slice/array pattern to a list of patterns.
- let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
- let ptr = Pointer::new(AllocId(0), offset);
- (0..n).map(|i| {
- let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
- let scalar = alloc.read_scalar(
- &cx.tcx, ptr, layout.size,
- ).ok()?;
- let scalar = scalar.not_undef().ok()?;
- let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
- let pattern = Pat {
- ty,
- span: pat.span,
- kind: box PatKind::Constant { value },
- };
- Some(&*cx.pattern_arena.alloc(pattern))
- }).collect()
- } else {
- None
}
- }
- _ => {
- // If the constructor is a:
- // Single value: add a row if the constructor equals the pattern.
- // Range: add a row if the constructor contains the pattern.
- constructor_intersects_pattern(cx.tcx, cx.param_env, constructor, pat)
- }
+ },
+ _ => span_bug!(
+ pat.span,
+ "unexpected const-val {:?} with ctor {:?}",
+ value,
+ constructor,
+ ),
+ };
+ if wild_patterns.len() as u64 == n {
+ // convert a constant slice/array pattern to a list of patterns.
+ let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
+ let ptr = Pointer::new(AllocId(0), offset);
+ (0..n).map(|i| {
+ let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
+ let scalar = alloc.read_scalar(
+ &cx.tcx, ptr, layout.size,
+ ).ok()?;
+ let scalar = scalar.not_undef().ok()?;
+ let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
+ let pattern = Pat {
+ ty,
+ span: pat.span,
+ kind: box PatKind::Constant { value },
+ };
+ Some(&*cx.pattern_arena.alloc(pattern))
+ }).collect()
+ } else {
+ None
}
}
+ PatKind::Constant { .. } |
PatKind::Range { .. } => {
// If the constructor is a:
- // Single value: add a row if the pattern contains the constructor.
- // Range: add a row if the constructor intersects the pattern.
- constructor_intersects_pattern(cx.tcx, cx.param_env, constructor, pat)
+ // - Single value: add a row if the pattern contains the constructor.
+ // - Range: add a row if the constructor intersects the pattern.
+ if should_treat_range_exhaustively(cx.tcx, constructor) {
+ match (IntRange::from_ctor(cx.tcx, cx.param_env, constructor),
+ IntRange::from_pat(cx.tcx, cx.param_env, pat)) {
+ (Some(ctor), Some(pat)) => {
+ ctor.intersection(&pat).map(|_| {
+ let (pat_lo, pat_hi) = pat.range.into_inner();
+ let (ctor_lo, ctor_hi) = ctor.range.into_inner();
+ assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi);
+ smallvec![]
+ })
+ }
+ _ => None,
+ }
+ } else {
+ // Fallback for non-ranges and ranges that involve
+ // floating-point numbers, which are not conveniently handled
+ // by `IntRange`. For these cases, the constructor may not be a
+ // range so intersection actually devolves into being covered
+ // by the pattern.
+ match constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat) {
+ Ok(true) => Some(smallvec![]),
+ Ok(false) | Err(ErrorReported) => None,
+ }
+ }
}
PatKind::Array { ref prefix, ref slice, ref suffix } |
| ty::Projection(ty::ProjectionTy { item_def_id: def_id, substs })
| ty::UnnormalizedProjection(ty::ProjectionTy { item_def_id: def_id, substs })
| ty::Closure(def_id, substs)
- | ty::Generator(def_id, ty::GeneratorSubsts { substs }, _)
- => self.print_def_path(def_id, substs),
+ | ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs),
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
ty::GeneratorWitness(_) => {
bits_discr
};
// Make sure we catch invalid discriminants
- let index = match &rval.layout.ty.kind {
+ let index = match rval.layout.ty.kind {
ty::Adt(adt, _) => adt
.discriminants(self.tcx.tcx)
.find(|(_, var)| var.val == real_discr),
- ty::Generator(def_id, substs, _) => substs
- .discriminants(*def_id, self.tcx.tcx)
- .find(|(_, var)| var.val == real_discr),
+ ty::Generator(def_id, substs, _) => {
+ let substs = substs.as_generator();
+ substs
+ .discriminants(def_id, self.tcx.tcx)
+ .find(|(_, var)| var.val == real_discr)
+ }
_ => bug!("tagged layout for non-adt non-generator"),
+
}.ok_or_else(
|| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag()))
)?;
StaticKind::Promoted(promoted, promoted_substs) => {
let substs = self.subst_from_frame_and_normalize_erasing_regions(promoted_substs);
let instance = ty::Instance::new(place_static.def_id, substs);
+
+ // Even after getting `substs` from the frame, this instance may still be
+ // polymorphic because `ConstProp` will try to promote polymorphic MIR.
+ if instance.needs_subst() {
+ throw_inval!(TooGeneric);
+ }
+
self.const_eval_raw(GlobalId {
instance,
promoted: Some(promoted),
tcx: TyCtxt<'_>,
mode: MonoItemCollectionMode,
) -> (FxHashSet<MonoItem<'_>>, InliningMap<'_>) {
+ let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
+
let roots = time(tcx.sess, "collecting roots", || {
+ let _prof_timer = tcx.prof
+ .generic_activity("monomorphization_collector_root_collections");
collect_roots(tcx, mode)
});
let mut inlining_map = MTLock::new(InliningMap::new());
{
+ let _prof_timer = tcx.prof
+ .generic_activity("monomorphization_collector_graph_walk");
+
let visited: MTRef<'_, _> = &mut visited;
let inlining_map: MTRef<'_, _> = &mut inlining_map;
+++ /dev/null
-use rustc::hir::def_id::LOCAL_CRATE;
-use rustc::mir::mono::MonoItem;
-use rustc::session::config::OptLevel;
-use rustc::ty::{self, TyCtxt, Instance};
-use rustc::ty::subst::InternalSubsts;
-use rustc::ty::print::obsolete::DefPathBasedNames;
-use syntax::attr::InlineAttr;
-use std::fmt;
-use rustc::mir::mono::Linkage;
-use syntax_pos::symbol::InternedString;
-use syntax::source_map::Span;
-
-/// Describes how a monomorphization will be instantiated in object files.
-#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
-pub enum InstantiationMode {
- /// There will be exactly one instance of the given MonoItem. It will have
- /// external linkage so that it can be linked to from other codegen units.
- GloballyShared {
- /// In some compilation scenarios we may decide to take functions that
- /// are typically `LocalCopy` and instead move them to `GloballyShared`
- /// to avoid codegenning them a bunch of times. In this situation,
- /// however, our local copy may conflict with other crates also
- /// inlining the same function.
- ///
- /// This flag indicates that this situation is occurring, and informs
- /// symbol name calculation that some extra mangling is needed to
- /// avoid conflicts. Note that this may eventually go away entirely if
- /// ThinLTO enables us to *always* have a globally shared instance of a
- /// function within one crate's compilation.
- may_conflict: bool,
- },
-
- /// Each codegen unit containing a reference to the given MonoItem will
- /// have its own private copy of the function (with internal linkage).
- LocalCopy,
-}
-
-pub trait MonoItemExt<'tcx>: fmt::Debug {
- fn as_mono_item(&self) -> &MonoItem<'tcx>;
-
- fn is_generic_fn(&self) -> bool {
- match *self.as_mono_item() {
- MonoItem::Fn(ref instance) => {
- instance.substs.non_erasable_generics().next().is_some()
- }
- MonoItem::Static(..) |
- MonoItem::GlobalAsm(..) => false,
- }
- }
-
- fn symbol_name(&self, tcx: TyCtxt<'tcx>) -> ty::SymbolName {
- match *self.as_mono_item() {
- MonoItem::Fn(instance) => tcx.symbol_name(instance),
- MonoItem::Static(def_id) => {
- tcx.symbol_name(Instance::mono(tcx, def_id))
- }
- MonoItem::GlobalAsm(hir_id) => {
- let def_id = tcx.hir().local_def_id(hir_id);
- ty::SymbolName {
- name: InternedString::intern(&format!("global_asm_{:?}", def_id))
- }
- }
- }
- }
- fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
- let inline_in_all_cgus =
- tcx.sess.opts.debugging_opts.inline_in_all_cgus.unwrap_or_else(|| {
- tcx.sess.opts.optimize != OptLevel::No
- }) && !tcx.sess.opts.cg.link_dead_code;
-
- match *self.as_mono_item() {
- MonoItem::Fn(ref instance) => {
- let entry_def_id = tcx.entry_fn(LOCAL_CRATE).map(|(id, _)| id);
- // If this function isn't inlined or otherwise has explicit
- // linkage, then we'll be creating a globally shared version.
- if self.explicit_linkage(tcx).is_some() ||
- !instance.def.requires_local(tcx) ||
- Some(instance.def_id()) == entry_def_id
- {
- return InstantiationMode::GloballyShared { may_conflict: false }
- }
-
- // At this point we don't have explicit linkage and we're an
- // inlined function. If we're inlining into all CGUs then we'll
- // be creating a local copy per CGU
- if inline_in_all_cgus {
- return InstantiationMode::LocalCopy
- }
-
- // Finally, if this is `#[inline(always)]` we're sure to respect
- // that with an inline copy per CGU, but otherwise we'll be
- // creating one copy of this `#[inline]` function which may
- // conflict with upstream crates as it could be an exported
- // symbol.
- match tcx.codegen_fn_attrs(instance.def_id()).inline {
- InlineAttr::Always => InstantiationMode::LocalCopy,
- _ => {
- InstantiationMode::GloballyShared { may_conflict: true }
- }
- }
- }
- MonoItem::Static(..) |
- MonoItem::GlobalAsm(..) => {
- InstantiationMode::GloballyShared { may_conflict: false }
- }
- }
- }
-
- fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option<Linkage> {
- let def_id = match *self.as_mono_item() {
- MonoItem::Fn(ref instance) => instance.def_id(),
- MonoItem::Static(def_id) => def_id,
- MonoItem::GlobalAsm(..) => return None,
- };
-
- let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
- codegen_fn_attrs.linkage
- }
-
- /// Returns `true` if this instance is instantiable - whether it has no unsatisfied
- /// predicates.
- ///
- /// In order to codegen an item, all of its predicates must hold, because
- /// otherwise the item does not make sense. Type-checking ensures that
- /// the predicates of every item that is *used by* a valid item *do*
- /// hold, so we can rely on that.
- ///
- /// However, we codegen collector roots (reachable items) and functions
- /// in vtables when they are seen, even if they are not used, and so they
- /// might not be instantiable. For example, a programmer can define this
- /// public function:
- ///
- /// pub fn foo<'a>(s: &'a mut ()) where &'a mut (): Clone {
- /// <&mut () as Clone>::clone(&s);
- /// }
- ///
- /// That function can't be codegened, because the method `<&mut () as Clone>::clone`
- /// does not exist. Luckily for us, that function can't ever be used,
- /// because that would require for `&'a mut (): Clone` to hold, so we
- /// can just not emit any code, or even a linker reference for it.
- ///
- /// Similarly, if a vtable method has such a signature, and therefore can't
- /// be used, we can just not emit it and have a placeholder (a null pointer,
- /// which will never be accessed) in its place.
- fn is_instantiable(&self, tcx: TyCtxt<'tcx>) -> bool {
- debug!("is_instantiable({:?})", self);
- let (def_id, substs) = match *self.as_mono_item() {
- MonoItem::Fn(ref instance) => (instance.def_id(), instance.substs),
- MonoItem::Static(def_id) => (def_id, InternalSubsts::empty()),
- // global asm never has predicates
- MonoItem::GlobalAsm(..) => return true
- };
-
- tcx.substitute_normalize_and_test_predicates((def_id, &substs))
- }
-
- fn to_string(&self, tcx: TyCtxt<'tcx>, debug: bool) -> String {
- return match *self.as_mono_item() {
- MonoItem::Fn(instance) => {
- to_string_internal(tcx, "fn ", instance, debug)
- },
- MonoItem::Static(def_id) => {
- let instance = Instance::new(def_id, tcx.intern_substs(&[]));
- to_string_internal(tcx, "static ", instance, debug)
- },
- MonoItem::GlobalAsm(..) => {
- "global_asm".to_string()
- }
- };
-
- fn to_string_internal<'a, 'tcx>(
- tcx: TyCtxt<'tcx>,
- prefix: &str,
- instance: Instance<'tcx>,
- debug: bool,
- ) -> String {
- let mut result = String::with_capacity(32);
- result.push_str(prefix);
- let printer = DefPathBasedNames::new(tcx, false, false);
- printer.push_instance_as_string(instance, &mut result, debug);
- result
- }
- }
-
- fn local_span(&self, tcx: TyCtxt<'tcx>) -> Option<Span> {
- match *self.as_mono_item() {
- MonoItem::Fn(Instance { def, .. }) => {
- tcx.hir().as_local_hir_id(def.def_id())
- }
- MonoItem::Static(def_id) => {
- tcx.hir().as_local_hir_id(def_id)
- }
- MonoItem::GlobalAsm(hir_id) => {
- Some(hir_id)
- }
- }.map(|hir_id| tcx.hir().span(hir_id))
- }
-}
-
-impl MonoItemExt<'tcx> for MonoItem<'tcx> {
- fn as_mono_item(&self) -> &MonoItem<'tcx> {
- self
- }
-}
where
I: Iterator<Item = MonoItem<'tcx>>,
{
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
+
// In the first step, we place all regular monomorphizations into their
// respective 'home' codegen unit. Regular monomorphizations are all
// functions and statics defined in the local crate.
- let mut initial_partitioning = place_root_mono_items(tcx, mono_items);
+ let mut initial_partitioning = {
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
+ place_root_mono_items(tcx, mono_items)
+ };
initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
// If the partitioning should produce a fixed count of codegen units, merge
// until that count is reached.
if let PartitioningStrategy::FixedUnitCount(count) = strategy {
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
merge_codegen_units(tcx, &mut initial_partitioning, count);
-
debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
}
// monomorphizations have to go into each codegen unit. These additional
// monomorphizations can be drop-glue, functions from external crates, and
// local functions the definition of which is marked with `#[inline]`.
- let mut post_inlining = place_inlined_mono_items(initial_partitioning,
- inlining_map);
+ let mut post_inlining = {
+ let _prof_timer =
+ tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
+ place_inlined_mono_items(initial_partitioning, inlining_map)
+ };
post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
// Next we try to make as many symbols "internal" as possible, so LLVM has
// more freedom to optimize.
if !tcx.sess.opts.cg.link_dead_code {
+ let _prof_timer =
+ tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
internalize_symbols(tcx, &mut post_inlining, inlining_map);
}
for (k, v) in smallest.items_mut().drain() {
second_smallest.items_mut().insert(k, v);
}
+ debug!("CodegenUnit {} merged in to CodegenUnit {}",
+ smallest.name(),
+ second_smallest.name());
}
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
if cfg!(debug_assertions) {
debug!("{}", label);
for cgu in cgus {
- debug!("CodegenUnit {}:", cgu.name());
+ debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
for (mono_item, linkage) in cgu.items() {
let symbol_name = mono_item.symbol_name(tcx).name.as_str();
let symbol_hash = symbol_hash_start.map(|i| &symbol_name[i ..])
.unwrap_or("<no hash>");
- debug!(" - {} [{:?}] [{}]",
+ debug!(" - {} [{:?}] [{}] estimated size {}",
mono_item.to_string(tcx, true),
linkage,
- symbol_hash);
+ symbol_hash,
+ mono_item.size_estimate(tcx));
}
debug!("");
// Check if this is a generator, if so, return the drop glue for it
if let Some(&ty::TyS { kind: ty::Generator(gen_def_id, substs, _), .. }) = ty {
let body = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
- return body.subst(tcx, substs.substs);
+ return body.subst(tcx, substs);
}
let substs = if let Some(ty) = ty {
fn visit_substs(&mut self, substs: &mut SubstsRef<'tcx>, _: Location) {
*substs = self.tcx.erase_regions(substs);
}
-
- fn visit_statement(&mut self,
- statement: &mut Statement<'tcx>,
- location: Location) {
- self.super_statement(statement, location);
- }
}
pub struct EraseRegions;
storage_liveness_map.insert(block, storage_liveness.clone());
requires_storage_cursor.seek(loc);
- let mut storage_required = requires_storage_cursor.get().clone();
-
- // Mark locals without storage statements as always requiring storage
- storage_required.union(&ignored.0);
+ let storage_required = requires_storage_cursor.get().clone();
// Locals live are live at this point only if they are used across
// suspension points (the `liveness` variable)
// Get the interior types and substs which typeck computed
let (upvars, interior, discr_ty, movable) = match gen_ty.kind {
ty::Generator(_, substs, movability) => {
+ let substs = substs.as_generator();
(substs.upvar_tys(def_id, tcx).collect(),
substs.witness(def_id, tcx),
substs.discr_ty(tcx),
// It effetively only contains upvars until the generator transformation runs.
// See librustc_body/transform/generator.rs for more details.
ty::Generator(def_id, substs, _) => {
- let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
+ let tys : Vec<_> = substs.as_generator().upvar_tys(def_id, self.tcx()).collect();
self.open_drop_for_tuple(&tys)
}
ty::Tuple(..) => {
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
+rustc_target = { path = "../librustc_target" }
+rustc_index = { path = "../librustc_index" }
--- /dev/null
+// This implements the dead-code warning pass. It follows middle::reachable
+// closely. The idea is that all reachable symbols are live, codes called
+// from live codes are live, and everything else is dead.
+
+use rustc::hir::Node;
+use rustc::hir::{self, PatKind, TyKind};
+use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+
+use rustc::hir::def::{CtorOf, Res, DefKind};
+use rustc::hir::CodegenFnAttrFlags;
+use rustc::hir::def_id::{DefId, LOCAL_CRATE};
+use rustc::lint;
+use rustc::middle::privacy;
+use rustc::ty::{self, DefIdTree, TyCtxt};
+use rustc::util::nodemap::FxHashSet;
+
+use rustc_data_structures::fx::FxHashMap;
+
+use syntax::{ast, attr};
+use syntax::symbol::sym;
+use syntax_pos;
+
+// Any local node that may call something in its body block should be
+// explored. For example, if it's a live Node::Item that is a
+// function, then we should explore its block to check for codes that
+// may need to be marked as live.
+fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
+ match tcx.hir().find(hir_id) {
+ Some(Node::Item(..)) |
+ Some(Node::ImplItem(..)) |
+ Some(Node::ForeignItem(..)) |
+ Some(Node::TraitItem(..)) |
+ Some(Node::Variant(..)) |
+ Some(Node::AnonConst(..)) |
+ Some(Node::Pat(..)) => true,
+ _ => false
+ }
+}
+
+struct MarkSymbolVisitor<'a, 'tcx> {
+ worklist: Vec<hir::HirId>,
+ tcx: TyCtxt<'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ live_symbols: FxHashSet<hir::HirId>,
+ repr_has_repr_c: bool,
+ in_pat: bool,
+ inherited_pub_visibility: bool,
+ ignore_variant_stack: Vec<DefId>,
+ // maps from tuple struct constructors to tuple struct items
+ struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
+}
+
+impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
+ fn check_def_id(&mut self, def_id: DefId) {
+ if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
+ if should_explore(self.tcx, hir_id) || self.struct_constructors.contains_key(&hir_id) {
+ self.worklist.push(hir_id);
+ }
+ self.live_symbols.insert(hir_id);
+ }
+ }
+
+ fn insert_def_id(&mut self, def_id: DefId) {
+ if let Some(hir_id) = self.tcx.hir().as_local_hir_id(def_id) {
+ debug_assert!(!should_explore(self.tcx, hir_id));
+ self.live_symbols.insert(hir_id);
+ }
+ }
+
+ fn handle_res(&mut self, res: Res) {
+ match res {
+ Res::Def(DefKind::Const, _)
+ | Res::Def(DefKind::AssocConst, _)
+ | Res::Def(DefKind::TyAlias, _) => {
+ self.check_def_id(res.def_id());
+ }
+ _ if self.in_pat => {},
+ Res::PrimTy(..) | Res::SelfCtor(..) |
+ Res::Local(..) => {}
+ Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
+ let variant_id = self.tcx.parent(ctor_def_id).unwrap();
+ let enum_id = self.tcx.parent(variant_id).unwrap();
+ self.check_def_id(enum_id);
+ if !self.ignore_variant_stack.contains(&ctor_def_id) {
+ self.check_def_id(variant_id);
+ }
+ }
+ Res::Def(DefKind::Variant, variant_id) => {
+ let enum_id = self.tcx.parent(variant_id).unwrap();
+ self.check_def_id(enum_id);
+ if !self.ignore_variant_stack.contains(&variant_id) {
+ self.check_def_id(variant_id);
+ }
+ }
+ Res::SelfTy(t, i) => {
+ if let Some(t) = t {
+ self.check_def_id(t);
+ }
+ if let Some(i) = i {
+ self.check_def_id(i);
+ }
+ }
+ Res::ToolMod | Res::NonMacroAttr(..) | Res::Err => {}
+ _ => {
+ self.check_def_id(res.def_id());
+ }
+ }
+ }
+
+ fn lookup_and_handle_method(&mut self, id: hir::HirId) {
+ if let Some(def_id) = self.tables.type_dependent_def_id(id) {
+ self.check_def_id(def_id);
+ } else {
+ bug!("no type-dependent def for method");
+ }
+ }
+
+ fn handle_field_access(&mut self, lhs: &hir::Expr, hir_id: hir::HirId) {
+ match self.tables.expr_ty_adjusted(lhs).kind {
+ ty::Adt(def, _) => {
+ let index = self.tcx.field_index(hir_id, self.tables);
+ self.insert_def_id(def.non_enum_variant().fields[index].did);
+ }
+ ty::Tuple(..) => {}
+ _ => span_bug!(lhs.span, "named field access on non-ADT"),
+ }
+ }
+
+ fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, res: Res, pats: &[hir::FieldPat]) {
+ let variant = match self.tables.node_type(lhs.hir_id).kind {
+ ty::Adt(adt, _) => adt.variant_of_res(res),
+ _ => span_bug!(lhs.span, "non-ADT in struct pattern")
+ };
+ for pat in pats {
+ if let PatKind::Wild = pat.pat.kind {
+ continue;
+ }
+ let index = self.tcx.field_index(pat.hir_id, self.tables);
+ self.insert_def_id(variant.fields[index].did);
+ }
+ }
+
+ fn mark_live_symbols(&mut self) {
+ let mut scanned = FxHashSet::default();
+ while let Some(id) = self.worklist.pop() {
+ if !scanned.insert(id) {
+ continue
+ }
+
+ // in the case of tuple struct constructors we want to check the item, not the generated
+ // tuple struct constructor function
+ let id = self.struct_constructors.get(&id).cloned().unwrap_or(id);
+
+ if let Some(node) = self.tcx.hir().find(id) {
+ self.live_symbols.insert(id);
+ self.visit_node(node);
+ }
+ }
+ }
+
+ fn visit_node(&mut self, node: Node<'tcx>) {
+ let had_repr_c = self.repr_has_repr_c;
+ self.repr_has_repr_c = false;
+ let had_inherited_pub_visibility = self.inherited_pub_visibility;
+ self.inherited_pub_visibility = false;
+ match node {
+ Node::Item(item) => {
+ match item.kind {
+ hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
+ let def_id = self.tcx.hir().local_def_id(item.hir_id);
+ let def = self.tcx.adt_def(def_id);
+ self.repr_has_repr_c = def.repr.c();
+
+ intravisit::walk_item(self, &item);
+ }
+ hir::ItemKind::Enum(..) => {
+ self.inherited_pub_visibility = item.vis.node.is_pub();
+
+ intravisit::walk_item(self, &item);
+ }
+ hir::ItemKind::ForeignMod(..) => {}
+ _ => {
+ intravisit::walk_item(self, &item);
+ }
+ }
+ }
+ Node::TraitItem(trait_item) => {
+ intravisit::walk_trait_item(self, trait_item);
+ }
+ Node::ImplItem(impl_item) => {
+ intravisit::walk_impl_item(self, impl_item);
+ }
+ Node::ForeignItem(foreign_item) => {
+ intravisit::walk_foreign_item(self, &foreign_item);
+ }
+ _ => {}
+ }
+ self.repr_has_repr_c = had_repr_c;
+ self.inherited_pub_visibility = had_inherited_pub_visibility;
+ }
+
+ fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &hir::HirVec<hir::Field>) {
+ if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
+ for field in fields {
+ let index = self.tcx.field_index(field.hir_id, self.tables);
+ self.insert_def_id(adt.non_enum_variant().fields[index].did);
+ }
+ }
+ }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_nested_body(&mut self, body: hir::BodyId) {
+ let old_tables = self.tables;
+ self.tables = self.tcx.body_tables(body);
+ let body = self.tcx.hir().body(body);
+ self.visit_body(body);
+ self.tables = old_tables;
+ }
+
+ fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name,
+ _: &hir::Generics, _: hir::HirId, _: syntax_pos::Span) {
+ let has_repr_c = self.repr_has_repr_c;
+ let inherited_pub_visibility = self.inherited_pub_visibility;
+ let live_fields = def.fields().iter().filter(|f| {
+ has_repr_c || inherited_pub_visibility || f.vis.node.is_pub()
+ });
+ self.live_symbols.extend(live_fields.map(|f| f.hir_id));
+
+ intravisit::walk_struct_def(self, def);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+ match expr.kind {
+ hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
+ let res = self.tables.qpath_res(qpath, expr.hir_id);
+ self.handle_res(res);
+ }
+ hir::ExprKind::MethodCall(..) => {
+ self.lookup_and_handle_method(expr.hir_id);
+ }
+ hir::ExprKind::Field(ref lhs, ..) => {
+ self.handle_field_access(&lhs, expr.hir_id);
+ }
+ hir::ExprKind::Struct(_, ref fields, _) => {
+ if let ty::Adt(ref adt, _) = self.tables.expr_ty(expr).kind {
+ self.mark_as_used_if_union(adt, fields);
+ }
+ }
+ _ => ()
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
+ // Inside the body, ignore constructions of variants
+ // necessary for the pattern to match. Those construction sites
+ // can't be reached unless the variant is constructed elsewhere.
+ let len = self.ignore_variant_stack.len();
+ self.ignore_variant_stack.extend(arm.pat.necessary_variants());
+ intravisit::walk_arm(self, arm);
+ self.ignore_variant_stack.truncate(len);
+ }
+
+ fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
+ match pat.kind {
+ PatKind::Struct(ref path, ref fields, _) => {
+ let res = self.tables.qpath_res(path, pat.hir_id);
+ self.handle_field_pattern_match(pat, res, fields);
+ }
+ PatKind::Path(ref qpath) => {
+ let res = self.tables.qpath_res(qpath, pat.hir_id);
+ self.handle_res(res);
+ }
+ _ => ()
+ }
+
+ self.in_pat = true;
+ intravisit::walk_pat(self, pat);
+ self.in_pat = false;
+ }
+
+ fn visit_path(&mut self, path: &'tcx hir::Path, _: hir::HirId) {
+ self.handle_res(path.res);
+ intravisit::walk_path(self, path);
+ }
+
+ fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
+ match ty.kind {
+ TyKind::Def(item_id, _) => {
+ let item = self.tcx.hir().expect_item(item_id.id);
+ intravisit::walk_item(self, item);
+ }
+ _ => ()
+ }
+ intravisit::walk_ty(self, ty);
+ }
+
+ fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
+ self.live_symbols.insert(c.hir_id);
+ intravisit::walk_anon_const(self, c);
+ }
+}
+
+fn has_allow_dead_code_or_lang_attr(
+ tcx: TyCtxt<'_>,
+ id: hir::HirId,
+ attrs: &[ast::Attribute],
+) -> bool {
+ if attr::contains_name(attrs, sym::lang) {
+ return true;
+ }
+
+ // Stable attribute for #[lang = "panic_impl"]
+ if attr::contains_name(attrs, sym::panic_handler) {
+ return true;
+ }
+
+ // (To be) stable attribute for #[lang = "oom"]
+ if attr::contains_name(attrs, sym::alloc_error_handler) {
+ return true;
+ }
+
+ let def_id = tcx.hir().local_def_id(id);
+ let cg_attrs = tcx.codegen_fn_attrs(def_id);
+
+ // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
+ // forcefully, e.g., for placing it in a specific section.
+ if cg_attrs.contains_extern_indicator() ||
+ cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
+ return true;
+ }
+
+ tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow
+}
+
+// This visitor seeds items that
+// 1) We want to explicitly consider as live:
+// * Item annotated with #[allow(dead_code)]
+// - This is done so that if we want to suppress warnings for a
+// group of dead functions, we only have to annotate the "root".
+// For example, if both `f` and `g` are dead and `f` calls `g`,
+// then annotating `f` with `#[allow(dead_code)]` will suppress
+// warning for both `f` and `g`.
+// * Item annotated with #[lang=".."]
+// - This is because lang items are always callable from elsewhere.
+// or
+// 2) We are not sure to be live or not
+// * Implementation of a trait method
+struct LifeSeeder<'k, 'tcx> {
+ worklist: Vec<hir::HirId>,
+ krate: &'k hir::Crate,
+ tcx: TyCtxt<'tcx>,
+ // see `MarkSymbolVisitor::struct_constructors`
+ struct_constructors: FxHashMap<hir::HirId, hir::HirId>,
+}
+
+impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> {
+ fn visit_item(&mut self, item: &hir::Item) {
+ let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx,
+ item.hir_id,
+ &item.attrs);
+ if allow_dead_code {
+ self.worklist.push(item.hir_id);
+ }
+ match item.kind {
+ hir::ItemKind::Enum(ref enum_def, _) => {
+ if allow_dead_code {
+ self.worklist.extend(enum_def.variants.iter().map(|variant| variant.id));
+ }
+
+ for variant in &enum_def.variants {
+ if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
+ self.struct_constructors.insert(ctor_hir_id, variant.id);
+ }
+ }
+ }
+ hir::ItemKind::Trait(.., ref trait_item_refs) => {
+ for trait_item_ref in trait_item_refs {
+ let trait_item = self.krate.trait_item(trait_item_ref.id);
+ match trait_item.kind {
+ hir::TraitItemKind::Const(_, Some(_)) |
+ hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
+ if has_allow_dead_code_or_lang_attr(self.tcx,
+ trait_item.hir_id,
+ &trait_item.attrs) {
+ self.worklist.push(trait_item.hir_id);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ hir::ItemKind::Impl(.., ref opt_trait, _, ref impl_item_refs) => {
+ for impl_item_ref in impl_item_refs {
+ let impl_item = self.krate.impl_item(impl_item_ref.id);
+ if opt_trait.is_some() ||
+ has_allow_dead_code_or_lang_attr(self.tcx,
+ impl_item.hir_id,
+ &impl_item.attrs) {
+ self.worklist.push(impl_item_ref.id.hir_id);
+ }
+ }
+ }
+ hir::ItemKind::Struct(ref variant_data, _) => {
+ if let Some(ctor_hir_id) = variant_data.ctor_hir_id() {
+ self.struct_constructors.insert(ctor_hir_id, item.hir_id);
+ }
+ }
+ _ => ()
+ }
+ }
+
+ fn visit_trait_item(&mut self, _item: &hir::TraitItem) {
+ // ignore: we are handling this in `visit_item` above
+ }
+
+ fn visit_impl_item(&mut self, _item: &hir::ImplItem) {
+ // ignore: we are handling this in `visit_item` above
+ }
+}
+
+fn create_and_seed_worklist<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ access_levels: &privacy::AccessLevels,
+ krate: &hir::Crate,
+) -> (Vec<hir::HirId>, FxHashMap<hir::HirId, hir::HirId>) {
+ let worklist = access_levels.map.iter().filter_map(|(&id, level)| {
+ if level >= &privacy::AccessLevel::Reachable {
+ Some(id)
+ } else {
+ None
+ }
+ }).chain(
+ // Seed entry point
+ tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().as_local_hir_id(def_id).unwrap())
+ ).collect::<Vec<_>>();
+
+ // Seed implemented trait items
+ let mut life_seeder = LifeSeeder {
+ worklist,
+ krate,
+ tcx,
+ struct_constructors: Default::default(),
+ };
+ krate.visit_all_item_likes(&mut life_seeder);
+
+ (life_seeder.worklist, life_seeder.struct_constructors)
+}
+
+fn find_live<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ access_levels: &privacy::AccessLevels,
+ krate: &hir::Crate,
+) -> FxHashSet<hir::HirId> {
+ let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels, krate);
+ let mut symbol_visitor = MarkSymbolVisitor {
+ worklist,
+ tcx,
+ tables: &ty::TypeckTables::empty(None),
+ live_symbols: Default::default(),
+ repr_has_repr_c: false,
+ in_pat: false,
+ inherited_pub_visibility: false,
+ ignore_variant_stack: vec![],
+ struct_constructors,
+ };
+ symbol_visitor.mark_live_symbols();
+ symbol_visitor.live_symbols
+}
+
+struct DeadVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ live_symbols: FxHashSet<hir::HirId>,
+}
+
+impl DeadVisitor<'tcx> {
+ fn should_warn_about_item(&mut self, item: &hir::Item) -> bool {
+ let should_warn = match item.kind {
+ hir::ItemKind::Static(..)
+ | hir::ItemKind::Const(..)
+ | hir::ItemKind::Fn(..)
+ | hir::ItemKind::TyAlias(..)
+ | hir::ItemKind::Enum(..)
+ | hir::ItemKind::Struct(..)
+ | hir::ItemKind::Union(..) => true,
+ _ => false
+ };
+ should_warn && !self.symbol_is_live(item.hir_id)
+ }
+
+ fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool {
+ let field_type = self.tcx.type_of(self.tcx.hir().local_def_id(field.hir_id));
+ !field.is_positional()
+ && !self.symbol_is_live(field.hir_id)
+ && !field_type.is_phantom_data()
+ && !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id, &field.attrs)
+ }
+
+ fn should_warn_about_variant(&mut self, variant: &hir::Variant) -> bool {
+ !self.symbol_is_live(variant.id)
+ && !has_allow_dead_code_or_lang_attr(self.tcx,
+ variant.id,
+ &variant.attrs)
+ }
+
+ fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool {
+ !self.symbol_is_live(fi.hir_id)
+ && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id, &fi.attrs)
+ }
+
+ // id := HIR id of an item's definition.
+ fn symbol_is_live(
+ &mut self,
+ id: hir::HirId,
+ ) -> bool {
+ if self.live_symbols.contains(&id) {
+ return true;
+ }
+ // If it's a type whose items are live, then it's live, too.
+ // This is done to handle the case where, for example, the static
+ // method of a private type is used, but the type itself is never
+ // called directly.
+ let def_id = self.tcx.hir().local_def_id(id);
+ let inherent_impls = self.tcx.inherent_impls(def_id);
+ for &impl_did in inherent_impls.iter() {
+ for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] {
+ if let Some(item_hir_id) = self.tcx.hir().as_local_hir_id(item_did) {
+ if self.live_symbols.contains(&item_hir_id) {
+ return true;
+ }
+ }
+ }
+ }
+ false
+ }
+
+ fn warn_dead_code(&mut self,
+ id: hir::HirId,
+ span: syntax_pos::Span,
+ name: ast::Name,
+ node_type: &str,
+ participle: &str) {
+ if !name.as_str().starts_with("_") {
+ self.tcx
+ .lint_hir(lint::builtin::DEAD_CODE,
+ id,
+ span,
+ &format!("{} is never {}: `{}`",
+ node_type, participle, name));
+ }
+ }
+}
+
+impl Visitor<'tcx> for DeadVisitor<'tcx> {
+ /// Walk nested items in place so that we don't report dead-code
+ /// on inner functions when the outer function is already getting
+ /// an error. We could do this also by checking the parents, but
+ /// this is how the code is setup and it seems harmless enough.
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::All(&self.tcx.hir())
+ }
+
+ fn visit_item(&mut self, item: &'tcx hir::Item) {
+ if self.should_warn_about_item(item) {
+ // For items that have a definition with a signature followed by a
+ // block, point only at the signature.
+ let span = match item.kind {
+ hir::ItemKind::Fn(..) |
+ hir::ItemKind::Mod(..) |
+ hir::ItemKind::Enum(..) |
+ hir::ItemKind::Struct(..) |
+ hir::ItemKind::Union(..) |
+ hir::ItemKind::Trait(..) |
+ hir::ItemKind::Impl(..) => self.tcx.sess.source_map().def_span(item.span),
+ _ => item.span,
+ };
+ let participle = match item.kind {
+ hir::ItemKind::Struct(..) => "constructed", // Issue #52325
+ _ => "used"
+ };
+ self.warn_dead_code(
+ item.hir_id,
+ span,
+ item.ident.name,
+ item.kind.descriptive_variant(),
+ participle,
+ );
+ } else {
+ // Only continue if we didn't warn
+ intravisit::walk_item(self, item);
+ }
+ }
+
+ fn visit_variant(&mut self,
+ variant: &'tcx hir::Variant,
+ g: &'tcx hir::Generics,
+ id: hir::HirId) {
+ if self.should_warn_about_variant(&variant) {
+ self.warn_dead_code(variant.id, variant.span, variant.ident.name,
+ "variant", "constructed");
+ } else {
+ intravisit::walk_variant(self, variant, g, id);
+ }
+ }
+
+ fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) {
+ if self.should_warn_about_foreign_item(fi) {
+ self.warn_dead_code(fi.hir_id, fi.span, fi.ident.name,
+ fi.kind.descriptive_variant(), "used");
+ }
+ intravisit::walk_foreign_item(self, fi);
+ }
+
+ fn visit_struct_field(&mut self, field: &'tcx hir::StructField) {
+ if self.should_warn_about_field(&field) {
+ self.warn_dead_code(field.hir_id, field.span, field.ident.name, "field", "used");
+ }
+ intravisit::walk_struct_field(self, field);
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
+ match impl_item.kind {
+ hir::ImplItemKind::Const(_, body_id) => {
+ if !self.symbol_is_live(impl_item.hir_id) {
+ self.warn_dead_code(impl_item.hir_id,
+ impl_item.span,
+ impl_item.ident.name,
+ "associated const",
+ "used");
+ }
+ self.visit_nested_body(body_id)
+ }
+ hir::ImplItemKind::Method(_, body_id) => {
+ if !self.symbol_is_live(impl_item.hir_id) {
+ let span = self.tcx.sess.source_map().def_span(impl_item.span);
+ self.warn_dead_code(impl_item.hir_id, span, impl_item.ident.name, "method",
+ "used");
+ }
+ self.visit_nested_body(body_id)
+ }
+ hir::ImplItemKind::OpaqueTy(..) |
+ hir::ImplItemKind::TyAlias(..) => {}
+ }
+ }
+
+ // Overwrite so that we don't warn the trait item itself.
+ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
+ match trait_item.kind {
+ hir::TraitItemKind::Const(_, Some(body_id)) |
+ hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)) => {
+ self.visit_nested_body(body_id)
+ }
+ hir::TraitItemKind::Const(_, None) |
+ hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) |
+ hir::TraitItemKind::Type(..) => {}
+ }
+ }
+}
+
+pub fn check_crate(tcx: TyCtxt<'_>) {
+ let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
+ let krate = tcx.hir().krate();
+ let live_symbols = find_live(tcx, access_levels, krate);
+ let mut visitor = DeadVisitor {
+ tcx,
+ live_symbols,
+ };
+ intravisit::walk_crate(&mut visitor, krate);
+}
--- /dev/null
+use rustc::hir::map as hir_map;
+use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE};
+use rustc::session::{config, Session};
+use rustc::session::config::EntryFnType;
+use syntax::attr;
+use syntax::entry::EntryPointType;
+use syntax::symbol::sym;
+use syntax_pos::Span;
+use rustc::hir::{HirId, Item, ItemKind, ImplItem, TraitItem};
+use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc::ty::TyCtxt;
+use rustc::ty::query::Providers;
+
+struct EntryContext<'a, 'tcx> {
+ session: &'a Session,
+
+ map: &'a hir_map::Map<'tcx>,
+
+ /// The top-level function called `main`.
+ main_fn: Option<(HirId, Span)>,
+
+ /// The function that has attribute named `main`.
+ attr_main_fn: Option<(HirId, Span)>,
+
+ /// The function that has the attribute 'start' on it.
+ start_fn: Option<(HirId, Span)>,
+
+ /// The functions that one might think are `main` but aren't, e.g.
+ /// main functions not defined at the top level. For diagnostics.
+ non_main_fns: Vec<(HirId, Span)> ,
+}
+
+impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
+ fn visit_item(&mut self, item: &'tcx Item) {
+ let def_id = self.map.local_def_id(item.hir_id);
+ let def_key = self.map.def_key(def_id);
+ let at_root = def_key.parent == Some(CRATE_DEF_INDEX);
+ find_item(item, self, at_root);
+ }
+
+ fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem) {
+ // Entry fn is never a trait item.
+ }
+
+ fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem) {
+ // Entry fn is never a trait item.
+ }
+}
+
+fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> {
+ assert_eq!(cnum, LOCAL_CRATE);
+
+ let any_exe = tcx.sess.crate_types.borrow().iter().any(|ty| {
+ *ty == config::CrateType::Executable
+ });
+ if !any_exe {
+ // No need to find a main function.
+ return None;
+ }
+
+ // If the user wants no main function at all, then stop here.
+ if attr::contains_name(&tcx.hir().krate().attrs, sym::no_main) {
+ return None;
+ }
+
+ let mut ctxt = EntryContext {
+ session: tcx.sess,
+ map: tcx.hir(),
+ main_fn: None,
+ attr_main_fn: None,
+ start_fn: None,
+ non_main_fns: Vec::new(),
+ };
+
+ tcx.hir().krate().visit_all_item_likes(&mut ctxt);
+
+ configure_main(tcx, &ctxt)
+}
+
+// Beware, this is duplicated in `libsyntax/entry.rs`, so make sure to keep
+// them in sync.
+fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType {
+ match item.kind {
+ ItemKind::Fn(..) => {
+ if attr::contains_name(&item.attrs, sym::start) {
+ EntryPointType::Start
+ } else if attr::contains_name(&item.attrs, sym::main) {
+ EntryPointType::MainAttr
+ } else if item.ident.name == sym::main {
+ if at_root {
+ // This is a top-level function so can be `main`.
+ EntryPointType::MainNamed
+ } else {
+ EntryPointType::OtherMain
+ }
+ } else {
+ EntryPointType::None
+ }
+ }
+ _ => EntryPointType::None,
+ }
+}
+
+
+fn find_item(item: &Item, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
+ match entry_point_type(item, at_root) {
+ EntryPointType::MainNamed => {
+ if ctxt.main_fn.is_none() {
+ ctxt.main_fn = Some((item.hir_id, item.span));
+ } else {
+ span_err!(ctxt.session, item.span, E0136,
+ "multiple `main` functions");
+ }
+ },
+ EntryPointType::OtherMain => {
+ ctxt.non_main_fns.push((item.hir_id, item.span));
+ },
+ EntryPointType::MainAttr => {
+ if ctxt.attr_main_fn.is_none() {
+ ctxt.attr_main_fn = Some((item.hir_id, item.span));
+ } else {
+ struct_span_err!(ctxt.session, item.span, E0137,
+ "multiple functions with a `#[main]` attribute")
+ .span_label(item.span, "additional `#[main]` function")
+ .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function")
+ .emit();
+ }
+ },
+ EntryPointType::Start => {
+ if ctxt.start_fn.is_none() {
+ ctxt.start_fn = Some((item.hir_id, item.span));
+ } else {
+ struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions")
+ .span_label(ctxt.start_fn.unwrap().1, "previous `start` function here")
+ .span_label(item.span, "multiple `start` functions")
+ .emit();
+ }
+ }
+ EntryPointType::None => (),
+ }
+}
+
+fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> {
+ if let Some((hir_id, _)) = visitor.start_fn {
+ Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start))
+ } else if let Some((hir_id, _)) = visitor.attr_main_fn {
+ Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
+ } else if let Some((hir_id, _)) = visitor.main_fn {
+ Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
+ } else {
+ no_main_err(tcx, visitor);
+ None
+ }
+}
+
+fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
+ // There is no main function.
+ let mut err = struct_err!(tcx.sess, E0601,
+ "`main` function not found in crate `{}`", tcx.crate_name(LOCAL_CRATE));
+ let filename = &tcx.sess.local_crate_source_file;
+ let note = if !visitor.non_main_fns.is_empty() {
+ for &(_, span) in &visitor.non_main_fns {
+ err.span_note(span, "here is a function named `main`");
+ }
+ err.note("you have one or more functions named `main` not defined at the crate level");
+ err.help("either move the `main` function definitions or attach the `#[main]` attribute \
+ to one of them");
+ // There were some functions named `main` though. Try to give the user a hint.
+ format!("the main function must be defined at the crate level{}",
+ filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default())
+ } else if let Some(filename) = filename {
+ format!("consider adding a `main` function to `{}`", filename.display())
+ } else {
+ String::from("consider adding a `main` function at the crate level")
+ };
+ let sp = tcx.hir().krate().span;
+ // The file may be empty, which leads to the diagnostic machinery not emitting this
+ // note. This is a relatively simple way to detect that case and emit a span-less
+ // note instead.
+ if let Ok(_) = tcx.sess.source_map().lookup_line(sp.lo()) {
+ err.set_span(sp);
+ err.span_label(sp, ¬e);
+ } else {
+ err.note(¬e);
+ }
+ if tcx.sess.teach(&err.get_code().unwrap()) {
+ err.note("If you don't know the basics of Rust, you can go look to the Rust Book \
+ to get started: https://doc.rust-lang.org/book/");
+ }
+ err.emit();
+}
+
+pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> {
+ tcx.entry_fn(LOCAL_CRATE)
+}
+
+pub fn provide(providers: &mut Providers<'_>) {
+ *providers = Providers {
+ entry_fn,
+ ..*providers
+ };
+}
syntax::register_diagnostics! {
-/*
E0014: r##"
+#### Note: this error code is no longer emitted by the compiler.
+
Constants can only be initialized by a constant value or, in a future
version of Rust, a call to a const function. This error indicates the use
of a path (like a::b, or x) denoting something other than one of these
-allowed items. Erroneous code xample:
+allowed items.
-```compile_fail
+Erroneous code example:
+
+```
const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
```
const FOO2: i32 = { 0 }; // but brackets are useless here
```
"##,
-*/
E0130: r##"
You declared a pattern as an argument in a foreign function declaration.
+
Erroneous code example:
```compile_fail
```
"##,
+// This shouldn't really ever trigger since the repeated value error comes first
+E0136: r##"
+A binary can only have one entry point, and by default that entry point is the
+function `main()`. If there are multiple such functions, please rename one.
+
+Erroneous code example:
+
+```compile_fail,E0136
+fn main() {
+ // ...
+}
+
+// ...
+
+fn main() { // error!
+ // ...
+}
+```
+"##,
+
+E0137: r##"
+More than one function was declared with the `#[main]` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0137
+#![feature(main)]
+
+#[main]
+fn foo() {}
+
+#[main]
+fn f() {} // error: multiple functions with a `#[main]` attribute
+```
+
+This error indicates that the compiler found multiple functions with the
+`#[main]` attribute. This is an error because there must be a unique entry
+point into a Rust program. Example:
+
+```
+#![feature(main)]
+
+#[main]
+fn f() {} // ok!
+```
+"##,
+
+E0138: r##"
+More than one function was declared with the `#[start]` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0138
+#![feature(start)]
+
+#[start]
+fn foo(argc: isize, argv: *const *const u8) -> isize {}
+
+#[start]
+fn f(argc: isize, argv: *const *const u8) -> isize {}
+// error: multiple 'start' functions
+```
+
+This error indicates that the compiler found multiple functions with the
+`#[start]` attribute. This is an error because there must be a unique entry
+point into a Rust program. Example:
+
+```
+#![feature(start)]
+
+#[start]
+fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok!
+```
+"##,
+
E0197: r##"
Inherent implementations (one that do not implement a trait but provide
methods associated with a type) are always safe because they are not
```
"##,
+E0512: r##"
+Transmute with two differently sized types was attempted. Erroneous code
+example:
-E0590: r##"
-`break` or `continue` must include a label when used in the condition of a
-`while` loop.
+```compile_fail,E0512
+fn takes_u8(_: u8) {}
-Example of erroneous code:
+fn main() {
+ unsafe { takes_u8(::std::mem::transmute(0u16)); }
+ // error: cannot transmute between types of different sizes,
+ // or dependently-sized types
+}
+```
+
+Please use types with same size or use the expected type directly. Example:
-```compile_fail
-while break {}
```
+fn takes_u8(_: u8) {}
-To fix this, add a label specifying which loop is being broken out of:
+fn main() {
+ unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
+ // or:
+ unsafe { takes_u8(0u8); } // ok!
+}
```
-'foo: while break 'foo {}
+"##,
+
+E0561: r##"
+A non-ident or non-wildcard pattern has been used as a parameter of a function
+pointer type.
+
+Erroneous code example:
+
+```compile_fail,E0561
+type A1 = fn(mut param: u8); // error!
+type A2 = fn(¶m: u32); // error!
+```
+
+When using an alias over a function type, you cannot e.g. denote a parameter as
+being mutable.
+
+To fix the issue, remove patterns (`_` is allowed though). Example:
+
+```
+type A1 = fn(param: u8); // ok!
+type A2 = fn(_: u32); // ok!
+```
+
+You can also omit the parameter name:
+
+```
+type A3 = fn(i16); // ok!
+```
+"##,
+
+E0567: r##"
+Generics have been used on an auto trait.
+
+Erroneous code example:
+
+```compile_fail,E0567
+#![feature(optin_builtin_traits)]
+
+auto trait Generic<T> {} // error!
+
+fn main() {}
+```
+
+Since an auto trait is implemented on all existing types, the
+compiler would not be able to infer the types of the trait's generic
+parameters.
+
+To fix this issue, just remove the generics:
+
+```
+#![feature(optin_builtin_traits)]
+
+auto trait Generic {} // ok!
+
+fn main() {}
```
"##,
```
"##,
+E0590: r##"
+`break` or `continue` must include a label when used in the condition of a
+`while` loop.
+
+Example of erroneous code:
+
+```compile_fail
+while break {}
+```
+
+To fix this, add a label specifying which loop is being broken out of:
+```
+'foo: while break 'foo {}
+```
+"##,
+
+E0591: r##"
+Per [RFC 401][rfc401], if you have a function declaration `foo`:
+
+```
+// For the purposes of this explanation, all of these
+// different kinds of `fn` declarations are equivalent:
+struct S;
+fn foo(x: S) { /* ... */ }
+# #[cfg(for_demonstration_only)]
+extern "C" { fn foo(x: S); }
+# #[cfg(for_demonstration_only)]
+impl S { fn foo(self) { /* ... */ } }
+```
+
+the type of `foo` is **not** `fn(S)`, as one might expect.
+Rather, it is a unique, zero-sized marker type written here as `typeof(foo)`.
+However, `typeof(foo)` can be _coerced_ to a function pointer `fn(S)`,
+so you rarely notice this:
+
+```
+# struct S;
+# fn foo(_: S) {}
+let x: fn(S) = foo; // OK, coerces
+```
+
+The reason that this matter is that the type `fn(S)` is not specific to
+any particular function: it's a function _pointer_. So calling `x()` results
+in a virtual call, whereas `foo()` is statically dispatched, because the type
+of `foo` tells us precisely what function is being called.
+
+As noted above, coercions mean that most code doesn't have to be
+concerned with this distinction. However, you can tell the difference
+when using **transmute** to convert a fn item into a fn pointer.
+
+This is sometimes done as part of an FFI:
+
+```compile_fail,E0591
+extern "C" fn foo(userdata: Box<i32>) {
+ /* ... */
+}
+
+# fn callback(_: extern "C" fn(*mut i32)) {}
+# use std::mem::transmute;
+# unsafe {
+let f: extern "C" fn(*mut i32) = transmute(foo);
+callback(f);
+# }
+```
+
+Here, transmute is being used to convert the types of the fn arguments.
+This pattern is incorrect because, because the type of `foo` is a function
+**item** (`typeof(foo)`), which is zero-sized, and the target type (`fn()`)
+is a function pointer, which is not zero-sized.
+This pattern should be rewritten. There are a few possible ways to do this:
+
+- change the original fn declaration to match the expected signature,
+ and do the cast in the fn body (the preferred option)
+- cast the fn item fo a fn pointer before calling transmute, as shown here:
+
+ ```
+ # extern "C" fn foo(_: Box<i32>) {}
+ # use std::mem::transmute;
+ # unsafe {
+ let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_));
+ let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too
+ # }
+ ```
+
+The same applies to transmutes to `*mut fn()`, which were observed in practice.
+Note though that use of this type is generally incorrect.
+The intention is typically to describe a function pointer, but just `fn()`
+alone suffices for that. `*mut fn()` is a pointer to a fn pointer.
+(Since these values are typically just passed to C code, however, this rarely
+makes a difference in practice.)
+
+[rfc401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
+"##,
+
+E0601: r##"
+No `main` function was found in a binary crate. To fix this error, add a
+`main` function. For example:
+
+```
+fn main() {
+ // Your program will start here.
+ println!("Hello world!");
+}
+```
+
+If you don't know the basics of Rust, you can go look to the Rust Book to get
+started: https://doc.rust-lang.org/book/
+"##,
+
E0642: r##"
Trait methods currently cannot take patterns as arguments.
Switch to the Rust 2018 edition to use `async fn`.
"##,
+
;
E0226, // only a single explicit lifetime bound is permitted
E0472, // asm! is unsupported on this target
- E0561, // patterns aren't allowed in function pointer types
- E0567, // auto traits can not have generic parameters
E0568, // auto traits can not have super traits
E0666, // nested `impl Trait` is illegal
E0667, // `impl Trait` in projections
--- /dev/null
+use rustc::hir::def::{Res, DefKind};
+use rustc::hir::def_id::DefId;
+use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::layout::{LayoutError, Pointer, SizeSkeleton, VariantIdx};
+use rustc::ty::query::Providers;
+
+use rustc_target::spec::abi::Abi::RustIntrinsic;
+use rustc_index::vec::Idx;
+use syntax_pos::{Span, sym};
+use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
+use rustc::hir;
+
+fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) {
+ tcx.hir().visit_item_likes_in_module(
+ module_def_id,
+ &mut ItemVisitor { tcx }.as_deep_visitor()
+ );
+}
+
+pub fn provide(providers: &mut Providers<'_>) {
+ *providers = Providers {
+ check_mod_intrinsics,
+ ..*providers
+ };
+}
+
+struct ItemVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+}
+
+struct ExprVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ tables: &'tcx ty::TypeckTables<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+}
+
+/// If the type is `Option<T>`, it will return `T`, otherwise
+/// the type itself. Works on most `Option`-like types.
+fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let (def, substs) = match ty.kind {
+ ty::Adt(def, substs) => (def, substs),
+ _ => return ty
+ };
+
+ if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() {
+ let data_idx;
+
+ let one = VariantIdx::new(1);
+ let zero = VariantIdx::new(0);
+
+ if def.variants[zero].fields.is_empty() {
+ data_idx = one;
+ } else if def.variants[one].fields.is_empty() {
+ data_idx = zero;
+ } else {
+ return ty;
+ }
+
+ if def.variants[data_idx].fields.len() == 1 {
+ return def.variants[data_idx].fields[0].ty(tcx, substs);
+ }
+ }
+
+ ty
+}
+
+impl ExprVisitor<'tcx> {
+ fn def_id_is_transmute(&self, def_id: DefId) -> bool {
+ self.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
+ self.tcx.item_name(def_id) == sym::transmute
+ }
+
+ fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
+ let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
+ let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
+
+ // Check for same size using the skeletons.
+ if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
+ if sk_from.same_size(sk_to) {
+ return;
+ }
+
+ // Special-case transmutting from `typeof(function)` and
+ // `Option<typeof(function)>` to present a clearer error.
+ let from = unpack_option_like(self.tcx, from);
+ if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (&from.kind, sk_to) {
+ if size_to == Pointer.size(&self.tcx) {
+ struct_span_err!(self.tcx.sess, span, E0591,
+ "can't transmute zero-sized type")
+ .note(&format!("source type: {}", from))
+ .note(&format!("target type: {}", to))
+ .help("cast with `as` to a pointer instead")
+ .emit();
+ return;
+ }
+ }
+ }
+
+ // Try to display a sensible error with as much information as possible.
+ let skeleton_string = |ty: Ty<'tcx>, sk| {
+ match sk {
+ Ok(SizeSkeleton::Known(size)) => {
+ format!("{} bits", size.bits())
+ }
+ Ok(SizeSkeleton::Pointer { tail, .. }) => {
+ format!("pointer to `{}`", tail)
+ }
+ Err(LayoutError::Unknown(bad)) => {
+ if bad == ty {
+ "this type does not have a fixed size".to_owned()
+ } else {
+ format!("size can vary because of {}", bad)
+ }
+ }
+ Err(err) => err.to_string()
+ }
+ };
+
+ let mut err = struct_span_err!(self.tcx.sess, span, E0512,
+ "cannot transmute between types of different sizes, \
+ or dependently-sized types");
+ if from == to {
+ err.note(&format!("`{}` does not have a fixed size", from));
+ } else {
+ err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
+ .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
+ }
+ err.emit()
+ }
+}
+
+impl Visitor<'tcx> for ItemVisitor<'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_nested_body(&mut self, body_id: hir::BodyId) {
+ let owner_def_id = self.tcx.hir().body_owner_def_id(body_id);
+ let body = self.tcx.hir().body(body_id);
+ let param_env = self.tcx.param_env(owner_def_id);
+ let tables = self.tcx.typeck_tables_of(owner_def_id);
+ ExprVisitor { tcx: self.tcx, param_env, tables }.visit_body(body);
+ self.visit_body(body);
+ }
+}
+
+impl Visitor<'tcx> for ExprVisitor<'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+ let res = if let hir::ExprKind::Path(ref qpath) = expr.kind {
+ self.tables.qpath_res(qpath, expr.hir_id)
+ } else {
+ Res::Err
+ };
+ if let Res::Def(DefKind::Fn, did) = res {
+ if self.def_id_is_transmute(did) {
+ let typ = self.tables.node_type(expr.hir_id);
+ let sig = typ.fn_sig(self.tcx);
+ let from = sig.inputs().skip_binder()[0];
+ let to = *sig.output().skip_binder();
+ self.check_transmute(expr.span, from, to);
+ }
+ }
+
+ intravisit::walk_expr(self, expr);
+ }
+}
#[macro_use]
extern crate rustc;
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate syntax;
use rustc::ty::query::Providers;
pub mod hir_stats;
pub mod layout_test;
pub mod loops;
+pub mod dead;
+pub mod entry;
+mod liveness;
+mod intrinsicck;
pub fn provide(providers: &mut Providers<'_>) {
+ entry::provide(providers);
loops::provide(providers);
+ liveness::provide(providers);
+ intrinsicck::provide(providers);
}
--- /dev/null
+//! A classic liveness analysis based on dataflow over the AST. Computes,
+//! for each local variable in a function, whether that variable is live
+//! at a given point. Program execution points are identified by their
+//! IDs.
+//!
+//! # Basic idea
+//!
+//! The basic model is that each local variable is assigned an index. We
+//! represent sets of local variables using a vector indexed by this
+//! index. The value in the vector is either 0, indicating the variable
+//! is dead, or the ID of an expression that uses the variable.
+//!
+//! We conceptually walk over the AST in reverse execution order. If we
+//! find a use of a variable, we add it to the set of live variables. If
+//! we find an assignment to a variable, we remove it from the set of live
+//! variables. When we have to merge two flows, we take the union of
+//! those two flows -- if the variable is live on both paths, we simply
+//! pick one ID. In the event of loops, we continue doing this until a
+//! fixed point is reached.
+//!
+//! ## Checking initialization
+//!
+//! At the function entry point, all variables must be dead. If this is
+//! not the case, we can report an error using the ID found in the set of
+//! live variables, which identifies a use of the variable which is not
+//! dominated by an assignment.
+//!
+//! ## Checking moves
+//!
+//! After each explicit move, the variable must be dead.
+//!
+//! ## Computing last uses
+//!
+//! Any use of the variable where the variable is dead afterwards is a
+//! last use.
+//!
+//! # Implementation details
+//!
+//! The actual implementation contains two (nested) walks over the AST.
+//! The outer walk has the job of building up the ir_maps instance for the
+//! enclosing function. On the way down the tree, it identifies those AST
+//! nodes and variable IDs that will be needed for the liveness analysis
+//! and assigns them contiguous IDs. The liveness ID for an AST node is
+//! called a `live_node` (it's a newtype'd `u32`) and the ID for a variable
+//! is called a `variable` (another newtype'd `u32`).
+//!
+//! On the way back up the tree, as we are about to exit from a function
+//! declaration we allocate a `liveness` instance. Now that we know
+//! precisely how many nodes and variables we need, we can allocate all
+//! the various arrays that we will need to precisely the right size. We then
+//! perform the actual propagation on the `liveness` instance.
+//!
+//! This propagation is encoded in the various `propagate_through_*()`
+//! methods. It effectively does a reverse walk of the AST; whenever we
+//! reach a loop node, we iterate until a fixed point is reached.
+//!
+//! ## The `RWU` struct
+//!
+//! At each live node `N`, we track three pieces of information for each
+//! variable `V` (these are encapsulated in the `RWU` struct):
+//!
+//! - `reader`: the `LiveNode` ID of some node which will read the value
+//! that `V` holds on entry to `N`. Formally: a node `M` such
+//! that there exists a path `P` from `N` to `M` where `P` does not
+//! write `V`. If the `reader` is `invalid_node()`, then the current
+//! value will never be read (the variable is dead, essentially).
+//!
+//! - `writer`: the `LiveNode` ID of some node which will write the
+//! variable `V` and which is reachable from `N`. Formally: a node `M`
+//! such that there exists a path `P` from `N` to `M` and `M` writes
+//! `V`. If the `writer` is `invalid_node()`, then there is no writer
+//! of `V` that follows `N`.
+//!
+//! - `used`: a boolean value indicating whether `V` is *used*. We
+//! distinguish a *read* from a *use* in that a *use* is some read that
+//! is not just used to generate a new value. For example, `x += 1` is
+//! a read but not a use. This is used to generate better warnings.
+//!
+//! ## Special Variables
+//!
+//! We generate various special variables for various, well, special purposes.
+//! These are described in the `specials` struct:
+//!
+//! - `exit_ln`: a live node that is generated to represent every 'exit' from
+//! the function, whether it be by explicit return, panic, or other means.
+//!
+//! - `fallthrough_ln`: a live node that represents a fallthrough
+//!
+//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
+//! fallthrough node. It is only live if the function could converge
+//! via means other than an explicit `return` expression. That is, it is
+//! only dead if the end of the function's block can never be reached.
+//! It is the responsibility of typeck to ensure that there are no
+//! `return` expressions in a function declared as diverging.
+
+use self::LiveNodeKind::*;
+use self::VarKind::*;
+
+use rustc::hir;
+use rustc::hir::{Expr, HirId};
+use rustc::hir::def::*;
+use rustc::hir::def_id::DefId;
+use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap};
+use rustc::hir::Node;
+use rustc::hir::ptr::P;
+use rustc::ty::{self, TyCtxt};
+use rustc::ty::query::Providers;
+use rustc::lint;
+use rustc::util::nodemap::{HirIdMap, HirIdSet};
+
+use errors::Applicability;
+use rustc_data_structures::fx::FxIndexMap;
+use std::collections::VecDeque;
+use std::{fmt, u32};
+use std::io::prelude::*;
+use std::io;
+use std::rc::Rc;
+use syntax::ast;
+use syntax::symbol::sym;
+use syntax_pos::Span;
+
+#[derive(Copy, Clone, PartialEq)]
+struct Variable(u32);
+
+#[derive(Copy, Clone, PartialEq)]
+struct LiveNode(u32);
+
+impl Variable {
+ fn get(&self) -> usize { self.0 as usize }
+}
+
+impl LiveNode {
+ fn get(&self) -> usize { self.0 as usize }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum LiveNodeKind {
+ UpvarNode(Span),
+ ExprNode(Span),
+ VarDefNode(Span),
+ ExitNode
+}
+
+fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
+ let cm = tcx.sess.source_map();
+ match lnk {
+ UpvarNode(s) => {
+ format!("Upvar node [{}]", cm.span_to_string(s))
+ }
+ ExprNode(s) => {
+ format!("Expr node [{}]", cm.span_to_string(s))
+ }
+ VarDefNode(s) => {
+ format!("Var def node [{}]", cm.span_to_string(s))
+ }
+ ExitNode => "Exit node".to_owned(),
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::OnlyBodies(&self.tcx.hir())
+ }
+
+ fn visit_fn(&mut self, fk: FnKind<'tcx>, fd: &'tcx hir::FnDecl,
+ b: hir::BodyId, s: Span, id: HirId) {
+ visit_fn(self, fk, fd, b, s, id);
+ }
+
+ fn visit_local(&mut self, l: &'tcx hir::Local) { visit_local(self, l); }
+ fn visit_expr(&mut self, ex: &'tcx Expr) { visit_expr(self, ex); }
+ fn visit_arm(&mut self, a: &'tcx hir::Arm) { visit_arm(self, a); }
+}
+
+fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: DefId) {
+ tcx.hir().visit_item_likes_in_module(
+ module_def_id,
+ &mut IrMaps::new(tcx, module_def_id).as_deep_visitor(),
+ );
+}
+
+pub fn provide(providers: &mut Providers<'_>) {
+ *providers = Providers {
+ check_mod_liveness,
+ ..*providers
+ };
+}
+
+impl fmt::Debug for LiveNode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ln({})", self.get())
+ }
+}
+
+impl fmt::Debug for Variable {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "v({})", self.get())
+ }
+}
+
+// ______________________________________________________________________
+// Creating ir_maps
+//
+// This is the first pass and the one that drives the main
+// computation. It walks up and down the IR once. On the way down,
+// we count for each function the number of variables as well as
+// liveness nodes. A liveness node is basically an expression or
+// capture clause that does something of interest: either it has
+// interesting control flow or it uses/defines a local variable.
+//
+// On the way back up, at each function node we create liveness sets
+// (we now know precisely how big to make our various vectors and so
+// forth) and then do the data-flow propagation to compute the set
+// of live variables at each program point.
+//
+// Finally, we run back over the IR one last time and, using the
+// computed liveness, check various safety conditions. For example,
+// there must be no live nodes at the definition site for a variable
+// unless it has an initializer. Similarly, each non-mutable local
+// variable must not be assigned if there is some successor
+// assignment. And so forth.
+
+impl LiveNode {
+ fn is_valid(&self) -> bool {
+ self.0 != u32::MAX
+ }
+}
+
+fn invalid_node() -> LiveNode { LiveNode(u32::MAX) }
+
+struct CaptureInfo {
+ ln: LiveNode,
+ var_hid: HirId
+}
+
+#[derive(Copy, Clone, Debug)]
+struct LocalInfo {
+ id: HirId,
+ name: ast::Name,
+ is_shorthand: bool,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum VarKind {
+ Param(HirId, ast::Name),
+ Local(LocalInfo),
+ CleanExit
+}
+
+struct IrMaps<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body_owner: DefId,
+ num_live_nodes: usize,
+ num_vars: usize,
+ live_node_map: HirIdMap<LiveNode>,
+ variable_map: HirIdMap<Variable>,
+ capture_info_map: HirIdMap<Rc<Vec<CaptureInfo>>>,
+ var_kinds: Vec<VarKind>,
+ lnks: Vec<LiveNodeKind>,
+}
+
+impl IrMaps<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>, body_owner: DefId) -> IrMaps<'tcx> {
+ IrMaps {
+ tcx,
+ body_owner,
+ num_live_nodes: 0,
+ num_vars: 0,
+ live_node_map: HirIdMap::default(),
+ variable_map: HirIdMap::default(),
+ capture_info_map: Default::default(),
+ var_kinds: Vec::new(),
+ lnks: Vec::new(),
+ }
+ }
+
+ fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode {
+ let ln = LiveNode(self.num_live_nodes as u32);
+ self.lnks.push(lnk);
+ self.num_live_nodes += 1;
+
+ debug!("{:?} is of kind {}", ln,
+ live_node_kind_to_string(lnk, self.tcx));
+
+ ln
+ }
+
+ fn add_live_node_for_node(&mut self, hir_id: HirId, lnk: LiveNodeKind) {
+ let ln = self.add_live_node(lnk);
+ self.live_node_map.insert(hir_id, ln);
+
+ debug!("{:?} is node {:?}", ln, hir_id);
+ }
+
+ fn add_variable(&mut self, vk: VarKind) -> Variable {
+ let v = Variable(self.num_vars as u32);
+ self.var_kinds.push(vk);
+ self.num_vars += 1;
+
+ match vk {
+ Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
+ self.variable_map.insert(node_id, v);
+ },
+ CleanExit => {}
+ }
+
+ debug!("{:?} is {:?}", v, vk);
+
+ v
+ }
+
+ fn variable(&self, hir_id: HirId, span: Span) -> Variable {
+ match self.variable_map.get(&hir_id) {
+ Some(&var) => var,
+ None => {
+ span_bug!(span, "no variable registered for id {:?}", hir_id);
+ }
+ }
+ }
+
+ fn variable_name(&self, var: Variable) -> String {
+ match self.var_kinds[var.get()] {
+ Local(LocalInfo { name, .. }) | Param(_, name) => {
+ name.to_string()
+ },
+ CleanExit => "<clean-exit>".to_owned()
+ }
+ }
+
+ fn variable_is_shorthand(&self, var: Variable) -> bool {
+ match self.var_kinds[var.get()] {
+ Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
+ Param(..) | CleanExit => false
+ }
+ }
+
+ fn set_captures(&mut self, hir_id: HirId, cs: Vec<CaptureInfo>) {
+ self.capture_info_map.insert(hir_id, Rc::new(cs));
+ }
+
+ fn lnk(&self, ln: LiveNode) -> LiveNodeKind {
+ self.lnks[ln.get()]
+ }
+}
+
+fn visit_fn<'tcx>(
+ ir: &mut IrMaps<'tcx>,
+ fk: FnKind<'tcx>,
+ decl: &'tcx hir::FnDecl,
+ body_id: hir::BodyId,
+ sp: Span,
+ id: hir::HirId,
+) {
+ debug!("visit_fn");
+
+ // swap in a new set of IR maps for this function body:
+ let def_id = ir.tcx.hir().local_def_id(id);
+ let mut fn_maps = IrMaps::new(ir.tcx, def_id);
+
+ // Don't run unused pass for #[derive()]
+ if let FnKind::Method(..) = fk {
+ let parent = ir.tcx.hir().get_parent_item(id);
+ if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) {
+ if i.attrs.iter().any(|a| a.check_name(sym::automatically_derived)) {
+ return;
+ }
+ }
+ }
+
+ debug!("creating fn_maps: {:p}", &fn_maps);
+
+ let body = ir.tcx.hir().body(body_id);
+
+ for param in &body.params {
+ let is_shorthand = match param.pat.kind {
+ rustc::hir::PatKind::Struct(..) => true,
+ _ => false,
+ };
+ param.pat.each_binding(|_bm, hir_id, _x, ident| {
+ debug!("adding parameters {:?}", hir_id);
+ let var = if is_shorthand {
+ Local(LocalInfo {
+ id: hir_id,
+ name: ident.name,
+ is_shorthand: true,
+ })
+ } else {
+ Param(hir_id, ident.name)
+ };
+ fn_maps.add_variable(var);
+ })
+ };
+
+ // gather up the various local variables, significant expressions,
+ // and so forth:
+ intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id);
+
+ // compute liveness
+ let mut lsets = Liveness::new(&mut fn_maps, body_id);
+ let entry_ln = lsets.compute(&body.value);
+
+ // check for various error conditions
+ lsets.visit_body(body);
+ lsets.warn_about_unused_args(body, entry_ln);
+}
+
+fn add_from_pat(ir: &mut IrMaps<'_>, pat: &P<hir::Pat>) {
+ // For struct patterns, take note of which fields used shorthand
+ // (`x` rather than `x: x`).
+ let mut shorthand_field_ids = HirIdSet::default();
+ let mut pats = VecDeque::new();
+ pats.push_back(pat);
+ while let Some(pat) = pats.pop_front() {
+ use rustc::hir::PatKind::*;
+ match &pat.kind {
+ Binding(.., inner_pat) => {
+ pats.extend(inner_pat.iter());
+ }
+ Struct(_, fields, _) => {
+ let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id);
+ shorthand_field_ids.extend(ids);
+ }
+ Ref(inner_pat, _) | Box(inner_pat) => {
+ pats.push_back(inner_pat);
+ }
+ TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => {
+ pats.extend(inner_pats.iter());
+ }
+ Slice(pre_pats, inner_pat, post_pats) => {
+ pats.extend(pre_pats.iter());
+ pats.extend(inner_pat.iter());
+ pats.extend(post_pats.iter());
+ }
+ _ => {}
+ }
+ }
+
+ pat.each_binding(|_, hir_id, _, ident| {
+ ir.add_live_node_for_node(hir_id, VarDefNode(ident.span));
+ ir.add_variable(Local(LocalInfo {
+ id: hir_id,
+ name: ident.name,
+ is_shorthand: shorthand_field_ids.contains(&hir_id)
+ }));
+ });
+}
+
+fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local) {
+ add_from_pat(ir, &local.pat);
+ intravisit::walk_local(ir, local);
+}
+
+fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm) {
+ add_from_pat(ir, &arm.pat);
+ intravisit::walk_arm(ir, arm);
+}
+
+fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr) {
+ match expr.kind {
+ // live nodes required for uses or definitions of variables:
+ hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
+ debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
+ if let Res::Local(var_hir_id) = path.res {
+ let upvars = ir.tcx.upvars(ir.body_owner);
+ if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
+ ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
+ }
+ }
+ intravisit::walk_expr(ir, expr);
+ }
+ hir::ExprKind::Closure(..) => {
+ // Interesting control flow (for loops can contain labeled
+ // breaks or continues)
+ ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
+
+ // Make a live_node for each captured variable, with the span
+ // being the location that the variable is used. This results
+ // in better error messages than just pointing at the closure
+ // construction site.
+ let mut call_caps = Vec::new();
+ let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
+ if let Some(upvars) = ir.tcx.upvars(closure_def_id) {
+ let parent_upvars = ir.tcx.upvars(ir.body_owner);
+ call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| {
+ let has_parent = parent_upvars
+ .map_or(false, |upvars| upvars.contains_key(&var_id));
+ if !has_parent {
+ let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
+ Some(CaptureInfo { ln: upvar_ln, var_hid: var_id })
+ } else {
+ None
+ }
+ }));
+ }
+ ir.set_captures(expr.hir_id, call_caps);
+ let old_body_owner = ir.body_owner;
+ ir.body_owner = closure_def_id;
+ intravisit::walk_expr(ir, expr);
+ ir.body_owner = old_body_owner;
+ }
+
+ // live nodes required for interesting control flow:
+ hir::ExprKind::Match(..) |
+ hir::ExprKind::Loop(..) => {
+ ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
+ intravisit::walk_expr(ir, expr);
+ }
+ hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => {
+ ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
+ intravisit::walk_expr(ir, expr);
+ }
+
+ // otherwise, live nodes are not required:
+ hir::ExprKind::Index(..) |
+ hir::ExprKind::Field(..) |
+ hir::ExprKind::Array(..) |
+ hir::ExprKind::Call(..) |
+ hir::ExprKind::MethodCall(..) |
+ hir::ExprKind::Tup(..) |
+ hir::ExprKind::Binary(..) |
+ hir::ExprKind::AddrOf(..) |
+ hir::ExprKind::Cast(..) |
+ hir::ExprKind::DropTemps(..) |
+ hir::ExprKind::Unary(..) |
+ hir::ExprKind::Break(..) |
+ hir::ExprKind::Continue(_) |
+ hir::ExprKind::Lit(_) |
+ hir::ExprKind::Ret(..) |
+ hir::ExprKind::Block(..) |
+ hir::ExprKind::Assign(..) |
+ hir::ExprKind::AssignOp(..) |
+ hir::ExprKind::Struct(..) |
+ hir::ExprKind::Repeat(..) |
+ hir::ExprKind::InlineAsm(..) |
+ hir::ExprKind::Box(..) |
+ hir::ExprKind::Yield(..) |
+ hir::ExprKind::Type(..) |
+ hir::ExprKind::Err |
+ hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => {
+ intravisit::walk_expr(ir, expr);
+ }
+ }
+}
+
+// ______________________________________________________________________
+// Computing liveness sets
+//
+// Actually we compute just a bit more than just liveness, but we use
+// the same basic propagation framework in all cases.
+
+#[derive(Clone, Copy)]
+struct RWU {
+ reader: LiveNode,
+ writer: LiveNode,
+ used: bool
+}
+
+/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
+/// very large, so it uses a more compact representation that takes advantage
+/// of the fact that when the number of `RWU`s is large, most of them have an
+/// invalid reader and an invalid writer.
+struct RWUTable {
+ /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
+ /// an index into `unpacked_rwus`. In the common cases, this compacts the
+ /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
+ /// in 96.
+ ///
+ /// More compact representations are possible -- e.g., use only 2 bits per
+ /// packed `RWU` and make the secondary table a HashMap that maps from
+ /// indices to `RWU`s -- but this one strikes a good balance between size
+ /// and speed.
+ packed_rwus: Vec<u32>,
+ unpacked_rwus: Vec<RWU>,
+}
+
+// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`.
+const INV_INV_FALSE: u32 = u32::MAX;
+
+// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`.
+const INV_INV_TRUE: u32 = u32::MAX - 1;
+
+impl RWUTable {
+ fn new(num_rwus: usize) -> RWUTable {
+ Self {
+ packed_rwus: vec![INV_INV_FALSE; num_rwus],
+ unpacked_rwus: vec![],
+ }
+ }
+
+ fn get(&self, idx: usize) -> RWU {
+ let packed_rwu = self.packed_rwus[idx];
+ match packed_rwu {
+ INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false },
+ INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true },
+ _ => self.unpacked_rwus[packed_rwu as usize],
+ }
+ }
+
+ fn get_reader(&self, idx: usize) -> LiveNode {
+ let packed_rwu = self.packed_rwus[idx];
+ match packed_rwu {
+ INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
+ _ => self.unpacked_rwus[packed_rwu as usize].reader,
+ }
+ }
+
+ fn get_writer(&self, idx: usize) -> LiveNode {
+ let packed_rwu = self.packed_rwus[idx];
+ match packed_rwu {
+ INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
+ _ => self.unpacked_rwus[packed_rwu as usize].writer,
+ }
+ }
+
+ fn get_used(&self, idx: usize) -> bool {
+ let packed_rwu = self.packed_rwus[idx];
+ match packed_rwu {
+ INV_INV_FALSE => false,
+ INV_INV_TRUE => true,
+ _ => self.unpacked_rwus[packed_rwu as usize].used,
+ }
+ }
+
+ #[inline]
+ fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) {
+ self.packed_rwus[dst_idx] = self.packed_rwus[src_idx];
+ }
+
+ fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
+ if rwu.reader == invalid_node() && rwu.writer == invalid_node() {
+ // When we overwrite an indexing entry in `self.packed_rwus` with
+ // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
+ // from `self.unpacked_rwus`; it's not worth the effort, and we
+ // can't have entries shifting around anyway.
+ self.packed_rwus[idx] = if rwu.used {
+ INV_INV_TRUE
+ } else {
+ INV_INV_FALSE
+ }
+ } else {
+ // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
+ // point to it.
+ self.packed_rwus[idx] = self.unpacked_rwus.len() as u32;
+ self.unpacked_rwus.push(rwu);
+ }
+ }
+
+ fn assign_inv_inv(&mut self, idx: usize) {
+ self.packed_rwus[idx] = if self.get_used(idx) {
+ INV_INV_TRUE
+ } else {
+ INV_INV_FALSE
+ };
+ }
+}
+
+#[derive(Copy, Clone)]
+struct Specials {
+ exit_ln: LiveNode,
+ fallthrough_ln: LiveNode,
+ clean_exit_var: Variable
+}
+
+const ACC_READ: u32 = 1;
+const ACC_WRITE: u32 = 2;
+const ACC_USE: u32 = 4;
+
+struct Liveness<'a, 'tcx> {
+ ir: &'a mut IrMaps<'tcx>,
+ tables: &'a ty::TypeckTables<'tcx>,
+ s: Specials,
+ successors: Vec<LiveNode>,
+ rwu_table: RWUTable,
+
+ // mappings from loop node ID to LiveNode
+ // ("break" label should map to loop node ID,
+ // it probably doesn't now)
+ break_ln: HirIdMap<LiveNode>,
+ cont_ln: HirIdMap<LiveNode>,
+}
+
+impl<'a, 'tcx> Liveness<'a, 'tcx> {
+ fn new(ir: &'a mut IrMaps<'tcx>, body: hir::BodyId) -> Liveness<'a, 'tcx> {
+ // Special nodes and variables:
+ // - exit_ln represents the end of the fn, either by return or panic
+ // - implicit_ret_var is a pseudo-variable that represents
+ // an implicit return
+ let specials = Specials {
+ exit_ln: ir.add_live_node(ExitNode),
+ fallthrough_ln: ir.add_live_node(ExitNode),
+ clean_exit_var: ir.add_variable(CleanExit)
+ };
+
+ let tables = ir.tcx.body_tables(body);
+
+ let num_live_nodes = ir.num_live_nodes;
+ let num_vars = ir.num_vars;
+
+ Liveness {
+ ir,
+ tables,
+ s: specials,
+ successors: vec![invalid_node(); num_live_nodes],
+ rwu_table: RWUTable::new(num_live_nodes * num_vars),
+ break_ln: Default::default(),
+ cont_ln: Default::default(),
+ }
+ }
+
+ fn live_node(&self, hir_id: HirId, span: Span) -> LiveNode {
+ match self.ir.live_node_map.get(&hir_id) {
+ Some(&ln) => ln,
+ None => {
+ // This must be a mismatch between the ir_map construction
+ // above and the propagation code below; the two sets of
+ // code have to agree about which AST nodes are worth
+ // creating liveness nodes for.
+ span_bug!(
+ span,
+ "no live node registered for node {:?}",
+ hir_id);
+ }
+ }
+ }
+
+ fn variable(&self, hir_id: HirId, span: Span) -> Variable {
+ self.ir.variable(hir_id, span)
+ }
+
+ fn define_bindings_in_pat(&mut self, pat: &hir::Pat, mut succ: LiveNode) -> LiveNode {
+ // In an or-pattern, only consider the first pattern; any later patterns
+ // must have the same bindings, and we also consider the first pattern
+ // to be the "authoritative" set of ids.
+ pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
+ let ln = self.live_node(hir_id, pat_sp);
+ let var = self.variable(hir_id, ident.span);
+ self.init_from_succ(ln, succ);
+ self.define(ln, var);
+ succ = ln;
+ });
+ succ
+ }
+
+ fn idx(&self, ln: LiveNode, var: Variable) -> usize {
+ ln.get() * self.ir.num_vars + var.get()
+ }
+
+ fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
+ assert!(ln.is_valid());
+ let reader = self.rwu_table.get_reader(self.idx(ln, var));
+ if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None }
+ }
+
+ // Is this variable live on entry to any of its successor nodes?
+ fn live_on_exit(&self, ln: LiveNode, var: Variable)
+ -> Option<LiveNodeKind> {
+ let successor = self.successors[ln.get()];
+ self.live_on_entry(successor, var)
+ }
+
+ fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
+ assert!(ln.is_valid());
+ self.rwu_table.get_used(self.idx(ln, var))
+ }
+
+ fn assigned_on_entry(&self, ln: LiveNode, var: Variable)
+ -> Option<LiveNodeKind> {
+ assert!(ln.is_valid());
+ let writer = self.rwu_table.get_writer(self.idx(ln, var));
+ if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None }
+ }
+
+ fn assigned_on_exit(&self, ln: LiveNode, var: Variable)
+ -> Option<LiveNodeKind> {
+ let successor = self.successors[ln.get()];
+ self.assigned_on_entry(successor, var)
+ }
+
+ fn indices2<F>(&mut self, ln: LiveNode, succ_ln: LiveNode, mut op: F) where
+ F: FnMut(&mut Liveness<'a, 'tcx>, usize, usize),
+ {
+ let node_base_idx = self.idx(ln, Variable(0));
+ let succ_base_idx = self.idx(succ_ln, Variable(0));
+ for var_idx in 0..self.ir.num_vars {
+ op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
+ }
+ }
+
+ fn write_vars<F>(&self,
+ wr: &mut dyn Write,
+ ln: LiveNode,
+ mut test: F)
+ -> io::Result<()> where
+ F: FnMut(usize) -> LiveNode,
+ {
+ let node_base_idx = self.idx(ln, Variable(0));
+ for var_idx in 0..self.ir.num_vars {
+ let idx = node_base_idx + var_idx;
+ if test(idx).is_valid() {
+ write!(wr, " {:?}", Variable(var_idx as u32))?;
+ }
+ }
+ Ok(())
+ }
+
+
+ #[allow(unused_must_use)]
+ fn ln_str(&self, ln: LiveNode) -> String {
+ let mut wr = Vec::new();
+ {
+ let wr = &mut wr as &mut dyn Write;
+ write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
+ self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
+ write!(wr, " writes");
+ self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
+ write!(wr, " precedes {:?}]", self.successors[ln.get()]);
+ }
+ String::from_utf8(wr).unwrap()
+ }
+
+ fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
+ self.successors[ln.get()] = succ_ln;
+
+ // It is not necessary to initialize the RWUs here because they are all
+ // set to INV_INV_FALSE when they are created, and the sets only grow
+ // during iterations.
+ }
+
+ fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
+ // more efficient version of init_empty() / merge_from_succ()
+ self.successors[ln.get()] = succ_ln;
+
+ self.indices2(ln, succ_ln, |this, idx, succ_idx| {
+ this.rwu_table.copy_packed(idx, succ_idx);
+ });
+ debug!("init_from_succ(ln={}, succ={})",
+ self.ln_str(ln), self.ln_str(succ_ln));
+ }
+
+ fn merge_from_succ(&mut self,
+ ln: LiveNode,
+ succ_ln: LiveNode,
+ first_merge: bool)
+ -> bool {
+ if ln == succ_ln { return false; }
+
+ let mut changed = false;
+ self.indices2(ln, succ_ln, |this, idx, succ_idx| {
+ let mut rwu = this.rwu_table.get(idx);
+ let succ_rwu = this.rwu_table.get(succ_idx);
+ if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() {
+ rwu.reader = succ_rwu.reader;
+ changed = true
+ }
+
+ if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() {
+ rwu.writer = succ_rwu.writer;
+ changed = true
+ }
+
+ if succ_rwu.used && !rwu.used {
+ rwu.used = true;
+ changed = true;
+ }
+
+ if changed {
+ this.rwu_table.assign_unpacked(idx, rwu);
+ }
+ });
+
+ debug!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
+ ln, self.ln_str(succ_ln), first_merge, changed);
+ return changed;
+ }
+
+ // Indicates that a local variable was *defined*; we know that no
+ // uses of the variable can precede the definition (resolve checks
+ // this) so we just clear out all the data.
+ fn define(&mut self, writer: LiveNode, var: Variable) {
+ let idx = self.idx(writer, var);
+ self.rwu_table.assign_inv_inv(idx);
+
+ debug!("{:?} defines {:?} (idx={}): {}", writer, var,
+ idx, self.ln_str(writer));
+ }
+
+ // Either read, write, or both depending on the acc bitset
+ fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) {
+ debug!("{:?} accesses[{:x}] {:?}: {}",
+ ln, acc, var, self.ln_str(ln));
+
+ let idx = self.idx(ln, var);
+ let mut rwu = self.rwu_table.get(idx);
+
+ if (acc & ACC_WRITE) != 0 {
+ rwu.reader = invalid_node();
+ rwu.writer = ln;
+ }
+
+ // Important: if we both read/write, must do read second
+ // or else the write will override.
+ if (acc & ACC_READ) != 0 {
+ rwu.reader = ln;
+ }
+
+ if (acc & ACC_USE) != 0 {
+ rwu.used = true;
+ }
+
+ self.rwu_table.assign_unpacked(idx, rwu);
+ }
+
+ fn compute(&mut self, body: &hir::Expr) -> LiveNode {
+ debug!("compute: using id for body, {}",
+ self.ir.tcx.hir().hir_to_pretty_string(body.hir_id));
+
+ // the fallthrough exit is only for those cases where we do not
+ // explicitly return:
+ let s = self.s;
+ self.init_from_succ(s.fallthrough_ln, s.exit_ln);
+ self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
+
+ let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
+
+ // hack to skip the loop unless debug! is enabled:
+ debug!("^^ liveness computation results for body {} (entry={:?})", {
+ for ln_idx in 0..self.ir.num_live_nodes {
+ debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
+ }
+ body.hir_id
+ },
+ entry_ln);
+
+ entry_ln
+ }
+
+ fn propagate_through_block(&mut self, blk: &hir::Block, succ: LiveNode)
+ -> LiveNode {
+ if blk.targeted_by_break {
+ self.break_ln.insert(blk.hir_id, succ);
+ }
+ let succ = self.propagate_through_opt_expr(blk.expr.as_ref().map(|e| &**e), succ);
+ blk.stmts.iter().rev().fold(succ, |succ, stmt| {
+ self.propagate_through_stmt(stmt, succ)
+ })
+ }
+
+ fn propagate_through_stmt(&mut self, stmt: &hir::Stmt, succ: LiveNode)
+ -> LiveNode {
+ match stmt.kind {
+ hir::StmtKind::Local(ref local) => {
+ // Note: we mark the variable as defined regardless of whether
+ // there is an initializer. Initially I had thought to only mark
+ // the live variable as defined if it was initialized, and then we
+ // could check for uninit variables just by scanning what is live
+ // at the start of the function. But that doesn't work so well for
+ // immutable variables defined in a loop:
+ // loop { let x; x = 5; }
+ // because the "assignment" loops back around and generates an error.
+ //
+ // So now we just check that variables defined w/o an
+ // initializer are not live at the point of their
+ // initialization, which is mildly more complex than checking
+ // once at the func header but otherwise equivalent.
+
+ let succ = self.propagate_through_opt_expr(local.init.as_ref().map(|e| &**e), succ);
+ self.define_bindings_in_pat(&local.pat, succ)
+ }
+ hir::StmtKind::Item(..) => succ,
+ hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
+ self.propagate_through_expr(&expr, succ)
+ }
+ }
+ }
+
+ fn propagate_through_exprs(&mut self, exprs: &[Expr], succ: LiveNode)
+ -> LiveNode {
+ exprs.iter().rev().fold(succ, |succ, expr| {
+ self.propagate_through_expr(&expr, succ)
+ })
+ }
+
+ fn propagate_through_opt_expr(&mut self,
+ opt_expr: Option<&Expr>,
+ succ: LiveNode)
+ -> LiveNode {
+ opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
+ }
+
+ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
+ -> LiveNode {
+ debug!("propagate_through_expr: {}", self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id));
+
+ match expr.kind {
+ // Interesting cases with control flow or which gen/kill
+ hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
+ self.access_path(expr.hir_id, path, succ, ACC_READ | ACC_USE)
+ }
+
+ hir::ExprKind::Field(ref e, _) => {
+ self.propagate_through_expr(&e, succ)
+ }
+
+ hir::ExprKind::Closure(..) => {
+ debug!("{} is an ExprKind::Closure",
+ self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id));
+
+ // the construction of a closure itself is not important,
+ // but we have to consider the closed over variables.
+ let caps = self.ir.capture_info_map.get(&expr.hir_id).cloned().unwrap_or_else(||
+ span_bug!(expr.span, "no registered caps"));
+
+ caps.iter().rev().fold(succ, |succ, cap| {
+ self.init_from_succ(cap.ln, succ);
+ let var = self.variable(cap.var_hid, expr.span);
+ self.acc(cap.ln, var, ACC_READ | ACC_USE);
+ cap.ln
+ })
+ }
+
+ // Note that labels have been resolved, so we don't need to look
+ // at the label ident
+ hir::ExprKind::Loop(ref blk, _, _) => {
+ self.propagate_through_loop(expr, &blk, succ)
+ }
+
+ hir::ExprKind::Match(ref e, ref arms, _) => {
+ //
+ // (e)
+ // |
+ // v
+ // (expr)
+ // / | \
+ // | | |
+ // v v v
+ // (..arms..)
+ // | | |
+ // v v v
+ // ( succ )
+ //
+ //
+ let ln = self.live_node(expr.hir_id, expr.span);
+ self.init_empty(ln, succ);
+ let mut first_merge = true;
+ for arm in arms {
+ let body_succ = self.propagate_through_expr(&arm.body, succ);
+
+ let guard_succ = self.propagate_through_opt_expr(
+ arm.guard.as_ref().map(|hir::Guard::If(e)| &**e),
+ body_succ
+ );
+ let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
+ self.merge_from_succ(ln, arm_succ, first_merge);
+ first_merge = false;
+ };
+ self.propagate_through_expr(&e, ln)
+ }
+
+ hir::ExprKind::Ret(ref o_e) => {
+ // ignore succ and subst exit_ln:
+ let exit_ln = self.s.exit_ln;
+ self.propagate_through_opt_expr(o_e.as_ref().map(|e| &**e), exit_ln)
+ }
+
+ hir::ExprKind::Break(label, ref opt_expr) => {
+ // Find which label this break jumps to
+ let target = match label.target_id {
+ Ok(hir_id) => self.break_ln.get(&hir_id),
+ Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
+ }.cloned();
+
+ // Now that we know the label we're going to,
+ // look it up in the break loop nodes table
+
+ match target {
+ Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
+ None => span_bug!(expr.span, "break to unknown label")
+ }
+ }
+
+ hir::ExprKind::Continue(label) => {
+ // Find which label this expr continues to
+ let sc = label.target_id.unwrap_or_else(|err|
+ span_bug!(expr.span, "loop scope error: {}", err));
+
+ // Now that we know the label we're going to,
+ // look it up in the continue loop nodes table
+ self.cont_ln.get(&sc).cloned().unwrap_or_else(||
+ span_bug!(expr.span, "continue to unknown label"))
+ }
+
+ hir::ExprKind::Assign(ref l, ref r) => {
+ // see comment on places in
+ // propagate_through_place_components()
+ let succ = self.write_place(&l, succ, ACC_WRITE);
+ let succ = self.propagate_through_place_components(&l, succ);
+ self.propagate_through_expr(&r, succ)
+ }
+
+ hir::ExprKind::AssignOp(_, ref l, ref r) => {
+ // an overloaded assign op is like a method call
+ if self.tables.is_method_call(expr) {
+ let succ = self.propagate_through_expr(&l, succ);
+ self.propagate_through_expr(&r, succ)
+ } else {
+ // see comment on places in
+ // propagate_through_place_components()
+ let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ);
+ let succ = self.propagate_through_expr(&r, succ);
+ self.propagate_through_place_components(&l, succ)
+ }
+ }
+
+ // Uninteresting cases: just propagate in rev exec order
+
+ hir::ExprKind::Array(ref exprs) => {
+ self.propagate_through_exprs(exprs, succ)
+ }
+
+ hir::ExprKind::Struct(_, ref fields, ref with_expr) => {
+ let succ = self.propagate_through_opt_expr(with_expr.as_ref().map(|e| &**e), succ);
+ fields.iter().rev().fold(succ, |succ, field| {
+ self.propagate_through_expr(&field.expr, succ)
+ })
+ }
+
+ hir::ExprKind::Call(ref f, ref args) => {
+ let m = self.ir.tcx.hir().get_module_parent(expr.hir_id);
+ let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
+ self.s.exit_ln
+ } else {
+ succ
+ };
+ let succ = self.propagate_through_exprs(args, succ);
+ self.propagate_through_expr(&f, succ)
+ }
+
+ hir::ExprKind::MethodCall(.., ref args) => {
+ let m = self.ir.tcx.hir().get_module_parent(expr.hir_id);
+ let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
+ self.s.exit_ln
+ } else {
+ succ
+ };
+
+ self.propagate_through_exprs(args, succ)
+ }
+
+ hir::ExprKind::Tup(ref exprs) => {
+ self.propagate_through_exprs(exprs, succ)
+ }
+
+ hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => {
+ let r_succ = self.propagate_through_expr(&r, succ);
+
+ let ln = self.live_node(expr.hir_id, expr.span);
+ self.init_from_succ(ln, succ);
+ self.merge_from_succ(ln, r_succ, false);
+
+ self.propagate_through_expr(&l, ln)
+ }
+
+ hir::ExprKind::Index(ref l, ref r) |
+ hir::ExprKind::Binary(_, ref l, ref r) => {
+ let r_succ = self.propagate_through_expr(&r, succ);
+ self.propagate_through_expr(&l, r_succ)
+ }
+
+ hir::ExprKind::Box(ref e) |
+ hir::ExprKind::AddrOf(_, ref e) |
+ hir::ExprKind::Cast(ref e, _) |
+ hir::ExprKind::Type(ref e, _) |
+ hir::ExprKind::DropTemps(ref e) |
+ hir::ExprKind::Unary(_, ref e) |
+ hir::ExprKind::Yield(ref e, _) |
+ hir::ExprKind::Repeat(ref e, _) => {
+ self.propagate_through_expr(&e, succ)
+ }
+
+ hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
+ let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
+ // see comment on places
+ // in propagate_through_place_components()
+ if o.is_indirect {
+ self.propagate_through_expr(output, succ)
+ } else {
+ let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
+ let succ = self.write_place(output, succ, acc);
+ self.propagate_through_place_components(output, succ)
+ }});
+
+ // Inputs are executed first. Propagate last because of rev order
+ self.propagate_through_exprs(inputs, succ)
+ }
+
+ hir::ExprKind::Lit(..) | hir::ExprKind::Err |
+ hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => {
+ succ
+ }
+
+ // Note that labels have been resolved, so we don't need to look
+ // at the label ident
+ hir::ExprKind::Block(ref blk, _) => {
+ self.propagate_through_block(&blk, succ)
+ }
+ }
+ }
+
+ fn propagate_through_place_components(&mut self,
+ expr: &Expr,
+ succ: LiveNode)
+ -> LiveNode {
+ // # Places
+ //
+ // In general, the full flow graph structure for an
+ // assignment/move/etc can be handled in one of two ways,
+ // depending on whether what is being assigned is a "tracked
+ // value" or not. A tracked value is basically a local
+ // variable or argument.
+ //
+ // The two kinds of graphs are:
+ //
+ // Tracked place Untracked place
+ // ----------------------++-----------------------
+ // ||
+ // | || |
+ // v || v
+ // (rvalue) || (rvalue)
+ // | || |
+ // v || v
+ // (write of place) || (place components)
+ // | || |
+ // v || v
+ // (succ) || (succ)
+ // ||
+ // ----------------------++-----------------------
+ //
+ // I will cover the two cases in turn:
+ //
+ // # Tracked places
+ //
+ // A tracked place is a local variable/argument `x`. In
+ // these cases, the link_node where the write occurs is linked
+ // to node id of `x`. The `write_place()` routine generates
+ // the contents of this node. There are no subcomponents to
+ // consider.
+ //
+ // # Non-tracked places
+ //
+ // These are places like `x[5]` or `x.f`. In that case, we
+ // basically ignore the value which is written to but generate
+ // reads for the components---`x` in these two examples. The
+ // components reads are generated by
+ // `propagate_through_place_components()` (this fn).
+ //
+ // # Illegal places
+ //
+ // It is still possible to observe assignments to non-places;
+ // these errors are detected in the later pass borrowck. We
+ // just ignore such cases and treat them as reads.
+
+ match expr.kind {
+ hir::ExprKind::Path(_) => succ,
+ hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ),
+ _ => self.propagate_through_expr(expr, succ)
+ }
+ }
+
+ // see comment on propagate_through_place()
+ fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32) -> LiveNode {
+ match expr.kind {
+ hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
+ self.access_path(expr.hir_id, path, succ, acc)
+ }
+
+ // We do not track other places, so just propagate through
+ // to their subcomponents. Also, it may happen that
+ // non-places occur here, because those are detected in the
+ // later pass borrowck.
+ _ => succ
+ }
+ }
+
+ fn access_var(&mut self, hir_id: HirId, var_hid: HirId, succ: LiveNode, acc: u32, span: Span)
+ -> LiveNode {
+ let ln = self.live_node(hir_id, span);
+ if acc != 0 {
+ self.init_from_succ(ln, succ);
+ let var = self.variable(var_hid, span);
+ self.acc(ln, var, acc);
+ }
+ ln
+ }
+
+ fn access_path(&mut self, hir_id: HirId, path: &hir::Path, succ: LiveNode, acc: u32)
+ -> LiveNode {
+ match path.res {
+ Res::Local(hid) => {
+ let upvars = self.ir.tcx.upvars(self.ir.body_owner);
+ if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) {
+ self.access_var(hir_id, hid, succ, acc, path.span)
+ } else {
+ succ
+ }
+ }
+ _ => succ
+ }
+ }
+
+ fn propagate_through_loop(
+ &mut self,
+ expr: &Expr,
+ body: &hir::Block,
+ succ: LiveNode
+ ) -> LiveNode {
+ /*
+ We model control flow like this:
+
+ (expr) <-+
+ | |
+ v |
+ (body) --+
+
+ Note that a `continue` expression targeting the `loop` will have a successor of `expr`.
+ Meanwhile, a `break` expression will have a successor of `succ`.
+ */
+
+ // first iteration:
+ let mut first_merge = true;
+ let ln = self.live_node(expr.hir_id, expr.span);
+ self.init_empty(ln, succ);
+ debug!("propagate_through_loop: using id for loop body {} {}",
+ expr.hir_id, self.ir.tcx.hir().hir_to_pretty_string(body.hir_id));
+
+ self.break_ln.insert(expr.hir_id, succ);
+
+ self.cont_ln.insert(expr.hir_id, ln);
+
+ let body_ln = self.propagate_through_block(body, ln);
+
+ // repeat until fixed point is reached:
+ while self.merge_from_succ(ln, body_ln, first_merge) {
+ first_merge = false;
+ assert_eq!(body_ln, self.propagate_through_block(body, ln));
+ }
+
+ ln
+ }
+}
+
+// _______________________________________________________________________
+// Checking for error conditions
+
+impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ NestedVisitorMap::None
+ }
+
+ fn visit_local(&mut self, local: &'tcx hir::Local) {
+ self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
+ if local.init.is_some() {
+ self.warn_about_dead_assign(spans, hir_id, ln, var);
+ }
+ });
+
+ intravisit::walk_local(self, local);
+ }
+
+ fn visit_expr(&mut self, ex: &'tcx Expr) {
+ check_expr(self, ex);
+ }
+
+ fn visit_arm(&mut self, arm: &'tcx hir::Arm) {
+ self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
+ intravisit::walk_arm(self, arm);
+ }
+}
+
+fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
+ match expr.kind {
+ hir::ExprKind::Assign(ref l, _) => {
+ this.check_place(&l);
+ }
+
+ hir::ExprKind::AssignOp(_, ref l, _) => {
+ if !this.tables.is_method_call(expr) {
+ this.check_place(&l);
+ }
+ }
+
+ hir::ExprKind::InlineAsm(ref ia, ref outputs, ref inputs) => {
+ for input in inputs {
+ this.visit_expr(input);
+ }
+
+ // Output operands must be places
+ for (o, output) in ia.outputs.iter().zip(outputs) {
+ if !o.is_indirect {
+ this.check_place(output);
+ }
+ this.visit_expr(output);
+ }
+ }
+
+ // no correctness conditions related to liveness
+ hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) |
+ hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) |
+ hir::ExprKind::Index(..) | hir::ExprKind::Field(..) |
+ hir::ExprKind::Array(..) | hir::ExprKind::Tup(..) | hir::ExprKind::Binary(..) |
+ hir::ExprKind::Cast(..) | hir::ExprKind::DropTemps(..) | hir::ExprKind::Unary(..) |
+ hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) |
+ hir::ExprKind::Lit(_) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) |
+ hir::ExprKind::Struct(..) | hir::ExprKind::Repeat(..) |
+ hir::ExprKind::Closure(..) | hir::ExprKind::Path(_) | hir::ExprKind::Yield(..) |
+ hir::ExprKind::Box(..) | hir::ExprKind::Type(..) | hir::ExprKind::Err => {}
+ }
+
+ intravisit::walk_expr(this, expr);
+}
+
+impl<'tcx> Liveness<'_, 'tcx> {
+ fn check_place(&mut self, expr: &'tcx Expr) {
+ match expr.kind {
+ hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
+ if let Res::Local(var_hid) = path.res {
+ let upvars = self.ir.tcx.upvars(self.ir.body_owner);
+ if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) {
+ // Assignment to an immutable variable or argument: only legal
+ // if there is no later assignment. If this local is actually
+ // mutable, then check for a reassignment to flag the mutability
+ // as being used.
+ let ln = self.live_node(expr.hir_id, expr.span);
+ let var = self.variable(var_hid, expr.span);
+ self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
+ }
+ }
+ }
+ _ => {
+ // For other kinds of places, no checks are required,
+ // and any embedded expressions are actually rvalues
+ intravisit::walk_expr(self, expr);
+ }
+ }
+ }
+
+ fn should_warn(&self, var: Variable) -> Option<String> {
+ let name = self.ir.variable_name(var);
+ if name.is_empty() || name.as_bytes()[0] == b'_' {
+ None
+ } else {
+ Some(name)
+ }
+ }
+
+ fn warn_about_unused_args(&self, body: &hir::Body, entry_ln: LiveNode) {
+ for p in &body.params {
+ self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
+ if self.live_on_entry(ln, var).is_none() {
+ self.report_dead_assign(hir_id, spans, var, true);
+ }
+ });
+ }
+ }
+
+ fn check_unused_vars_in_pat(
+ &self,
+ pat: &hir::Pat,
+ entry_ln: Option<LiveNode>,
+ on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
+ ) {
+ // In an or-pattern, only consider the variable; any later patterns must have the same
+ // bindings, and we also consider the first pattern to be the "authoritative" set of ids.
+ // However, we should take the spans of variables with the same name from the later
+ // patterns so the suggestions to prefix with underscores will apply to those too.
+ let mut vars: FxIndexMap<String, (LiveNode, Variable, HirId, Vec<Span>)> = <_>::default();
+
+ pat.each_binding(|_, hir_id, pat_sp, ident| {
+ let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp));
+ let var = self.variable(hir_id, ident.span);
+ vars.entry(self.ir.variable_name(var))
+ .and_modify(|(.., spans)| spans.push(ident.span))
+ .or_insert_with(|| (ln, var, hir_id, vec![ident.span]));
+ });
+
+ for (_, (ln, var, id, spans)) in vars {
+ if self.used_on_entry(ln, var) {
+ on_used_on_entry(spans, id, ln, var);
+ } else {
+ self.report_unused(spans, id, ln, var);
+ }
+ }
+ }
+
+ fn report_unused(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
+ if let Some(name) = self.should_warn(var).filter(|name| name != "self") {
+ // annoying: for parameters in funcs like `fn(x: i32)
+ // {ret}`, there is only one node, so asking about
+ // assigned_on_exit() is not meaningful.
+ let is_assigned = if ln == self.s.exit_ln {
+ false
+ } else {
+ self.assigned_on_exit(ln, var).is_some()
+ };
+
+ if is_assigned {
+ self.ir.tcx.lint_hir_note(
+ lint::builtin::UNUSED_VARIABLES,
+ hir_id,
+ spans,
+ &format!("variable `{}` is assigned to, but never used", name),
+ &format!("consider using `_{}` instead", name),
+ );
+ } else {
+ let mut err = self.ir.tcx.struct_span_lint_hir(
+ lint::builtin::UNUSED_VARIABLES,
+ hir_id,
+ spans.clone(),
+ &format!("unused variable: `{}`", name),
+ );
+
+ if self.ir.variable_is_shorthand(var) {
+ if let Node::Binding(pat) = self.ir.tcx.hir().get(hir_id) {
+ // Handle `ref` and `ref mut`.
+ let spans = spans.iter()
+ .map(|_span| (pat.span, format!("{}: _", name)))
+ .collect();
+
+ err.multipart_suggestion(
+ "try ignoring the field",
+ spans,
+ Applicability::MachineApplicable,
+ );
+ }
+ } else {
+ err.multipart_suggestion(
+ "consider prefixing with an underscore",
+ spans.iter().map(|span| (*span, format!("_{}", name))).collect(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ err.emit()
+ }
+ }
+ }
+
+ fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
+ if self.live_on_exit(ln, var).is_none() {
+ self.report_dead_assign(hir_id, spans, var, false);
+ }
+ }
+
+ fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
+ if let Some(name) = self.should_warn(var) {
+ if is_param {
+ self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
+ &format!("value passed to `{}` is never read", name))
+ .help("maybe it is overwritten before being read?")
+ .emit();
+ } else {
+ self.ir.tcx.struct_span_lint_hir(lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans,
+ &format!("value assigned to `{}` is never read", name))
+ .help("maybe it is overwritten before being read?")
+ .emit();
+ }
+ }
+ }
+}
syntax::register_diagnostics! {
E0445: r##"
-A private trait was used on a public type parameter bound. Erroneous code
-examples:
+A private trait was used on a public type parameter bound.
+
+Erroneous code examples:
```compile_fail,E0445
#![deny(private_in_public)]
"##,
E0446: r##"
-A private type was used in a public type signature. Erroneous code example:
+A private type was used in a public type signature.
+
+Erroneous code example:
```compile_fail,E0446
#![deny(private_in_public)]
E0447: r##"
#### Note: this error code is no longer emitted by the compiler.
-The `pub` keyword was used inside a function. Erroneous code example:
+The `pub` keyword was used inside a function.
+
+Erroneous code example:
```
fn foo() {
"##,
E0448: r##"
-The `pub` keyword was used inside a public enum. Erroneous code example:
+#### Note: this error code is no longer emitted by the compiler.
+
+The `pub` keyword was used inside a public enum.
+
+Erroneous code example:
```compile_fail
pub enum Foo {
"##,
E0451: r##"
-A struct constructor with private fields was invoked. Erroneous code example:
+A struct constructor with private fields was invoked.
+
+Erroneous code example:
```compile_fail,E0451
mod Bar {
use crate::resolve_imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
use crate::{path_names_to_string, KNOWN_TOOLS};
-use crate::{BindingError, CrateLint, LegacyScope, Module, ModuleOrUniformRoot};
+use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
use crate::{PathResult, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Segment};
type Res = def::Res<ast::NodeId>;
&self, span: Span, resolution_error: ResolutionError<'_>
) -> DiagnosticBuilder<'_> {
match resolution_error {
- ResolutionError::GenericParamsFromOuterFunction(outer_res) => {
+ ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
let mut err = struct_span_err!(self.session,
span,
E0401,
}
}
- // Try to retrieve the span of the function signature and generate a new message
- // with a local type or const parameter.
- let sugg_msg = &format!("try using a local generic parameter instead");
- if let Some((sugg_span, new_snippet)) = cm.generate_local_type_param_snippet(span) {
- // Suggest the modification to the user
- err.span_suggestion(
- sugg_span,
- sugg_msg,
- new_snippet,
- Applicability::MachineApplicable,
- );
- } else if let Some(sp) = cm.generate_fn_name_span(span) {
- err.span_label(sp,
- format!("try adding a local generic parameter in this method instead"));
- } else {
- err.help(&format!("try using a local generic parameter instead"));
+ if has_generic_params == HasGenericParams::Yes {
+ // Try to retrieve the span of the function signature and generate a new
+ // message with a local type or const parameter.
+ let sugg_msg = &format!("try using a local generic parameter instead");
+ if let Some((sugg_span, snippet)) = cm.generate_local_type_param_snippet(span) {
+ // Suggest the modification to the user
+ err.span_suggestion(
+ sugg_span,
+ sugg_msg,
+ snippet,
+ Applicability::MachineApplicable,
+ );
+ } else if let Some(sp) = cm.generate_fn_name_span(span) {
+ err.span_label(sp,
+ format!("try adding a local generic parameter in this method instead"));
+ } else {
+ err.help(&format!("try using a local generic parameter instead"));
+ }
}
err
in_module_is_extern)) = worklist.pop() {
// We have to visit module children in deterministic order to avoid
// instabilities in reported imports (#43552).
- in_module.for_each_child_stable(self, |this, ident, ns, name_binding| {
+ in_module.for_each_child(self, |this, ident, ns, name_binding| {
// avoid imports entirely
if name_binding.is_import() && !name_binding.is_extern_crate() { return; }
// avoid non-importable candidates as well
//! If you wonder why there's no `early.rs`, that's because it's split into three files -
//! `build_reduced_graph.rs`, `macros.rs` and `resolve_imports.rs`.
-use GenericParameters::*;
use RibKind::*;
use crate::{path_names_to_string, BindingError, CrateLint, LexicalScopeBinding};
binding_mode: BindingMode,
}
-#[derive(Copy, Clone)]
-enum GenericParameters<'a, 'b> {
- NoGenericParams,
- HasGenericParams(// Type parameters.
- &'b Generics,
-
- // The kind of the rib used for type parameters.
- RibKind<'a>),
-}
-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum PatternSource {
Match,
Or,
}
+/// Does this the item (from the item rib scope) allow generic parameters?
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+crate enum HasGenericParams { Yes, No }
+
/// The rib kind restricts certain accesses,
/// e.g. to a `Res::Local` of an outer item.
#[derive(Copy, Clone, Debug)]
FnItemRibKind,
/// We passed through an item scope. Disallow upvars.
- ItemRibKind,
+ ItemRibKind(HasGenericParams),
/// We're in a constant item. Can't refer to dynamic stuff.
ConstantItemRibKind,
| ModuleRibKind(_)
| MacroDefinition(_) => false,
AssocItemRibKind
- | ItemRibKind
+ | ItemRibKind(_)
| ForwardTyParamBanRibKind
| TyParamAsConstParamTy => true,
}
visit::walk_poly_trait_ref(self, tref, m);
}
fn visit_foreign_item(&mut self, foreign_item: &'tcx ForeignItem) {
- let generic_params = match foreign_item.kind {
+ match foreign_item.kind {
ForeignItemKind::Fn(_, ref generics) => {
- HasGenericParams(generics, ItemRibKind)
+ self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
+ visit::walk_foreign_item(this, foreign_item);
+ });
}
- ForeignItemKind::Static(..) => NoGenericParams,
- ForeignItemKind::Ty => NoGenericParams,
- ForeignItemKind::Macro(..) => NoGenericParams,
- };
- self.with_generic_param_rib(generic_params, |this| {
- visit::walk_foreign_item(this, foreign_item);
- });
+ ForeignItemKind::Static(..) => {
+ self.with_item_rib(HasGenericParams::No, |this| {
+ visit::walk_foreign_item(this, foreign_item);
+ });
+ }
+ ForeignItemKind::Ty | ForeignItemKind::Macro(..) => {
+ visit::walk_foreign_item(self, foreign_item);
+ }
+ }
}
fn visit_fn(&mut self, fn_kind: FnKind<'tcx>, declaration: &'tcx FnDecl, _: Span, _: NodeId) {
debug!("(resolving function) entering function");
fn resolve_adt(&mut self, item: &Item, generics: &Generics) {
debug!("resolve_adt");
self.with_current_self_item(item, |this| {
- this.with_generic_param_rib(HasGenericParams(generics, ItemRibKind), |this| {
+ this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
let item_def_id = this.r.definitions.local_def_id(item.id);
this.with_self_rib(Res::SelfTy(None, Some(item_def_id)), |this| {
visit::walk_item(this, item);
ItemKind::TyAlias(_, ref generics) |
ItemKind::OpaqueTy(_, ref generics) |
ItemKind::Fn(_, _, ref generics, _) => {
- self.with_generic_param_rib(
- HasGenericParams(generics, ItemRibKind),
- |this| visit::walk_item(this, item)
- );
+ self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes),
+ |this| visit::walk_item(this, item));
}
ItemKind::Enum(_, ref generics) |
ItemKind::Trait(.., ref generics, ref bounds, ref trait_items) => {
// Create a new rib for the trait-wide type parameters.
- self.with_generic_param_rib(HasGenericParams(generics, ItemRibKind), |this| {
+ self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
let local_def_id = this.r.definitions.local_def_id(item.id);
this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
this.visit_generics(generics);
for trait_item in trait_items {
this.with_trait_items(trait_items, |this| {
- let generic_params = HasGenericParams(
- &trait_item.generics,
- AssocItemRibKind,
- );
- this.with_generic_param_rib(generic_params, |this| {
- match trait_item.kind {
- TraitItemKind::Const(ref ty, ref default) => {
- this.visit_ty(ty);
-
- // Only impose the restrictions of
- // ConstRibKind for an actual constant
- // expression in a provided default.
- if let Some(ref expr) = *default{
- this.with_constant_rib(|this| {
- this.visit_expr(expr);
- });
+ this.with_generic_param_rib(&trait_item.generics, AssocItemRibKind,
+ |this| {
+ match trait_item.kind {
+ TraitItemKind::Const(ref ty, ref default) => {
+ this.visit_ty(ty);
+
+ // Only impose the restrictions of
+ // ConstRibKind for an actual constant
+ // expression in a provided default.
+ if let Some(ref expr) = *default{
+ this.with_constant_rib(|this| {
+ this.visit_expr(expr);
+ });
+ }
}
- }
- TraitItemKind::Method(_, _) => {
- visit::walk_trait_item(this, trait_item)
- }
- TraitItemKind::Type(..) => {
- visit::walk_trait_item(this, trait_item)
- }
- TraitItemKind::Macro(_) => {
- panic!("unexpanded macro in resolve!")
- }
- };
- });
+ TraitItemKind::Method(_, _) => {
+ visit::walk_trait_item(this, trait_item)
+ }
+ TraitItemKind::Type(..) => {
+ visit::walk_trait_item(this, trait_item)
+ }
+ TraitItemKind::Macro(_) => {
+ panic!("unexpanded macro in resolve!")
+ }
+ };
+ });
});
}
});
ItemKind::TraitAlias(ref generics, ref bounds) => {
// Create a new rib for the trait-wide type parameters.
- self.with_generic_param_rib(HasGenericParams(generics, ItemRibKind), |this| {
+ self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
let local_def_id = this.r.definitions.local_def_id(item.id);
this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
this.visit_generics(generics);
ItemKind::Static(ref ty, _, ref expr) |
ItemKind::Const(ref ty, ref expr) => {
debug!("resolve_item ItemKind::Const");
- self.with_item_rib(|this| {
+ self.with_item_rib(HasGenericParams::No, |this| {
this.visit_ty(ty);
this.with_constant_rib(|this| {
this.visit_expr(expr);
}
}
- fn with_generic_param_rib<'c, F>(&'c mut self, generic_params: GenericParameters<'a, 'c>, f: F)
+ fn with_generic_param_rib<'c, F>(&'c mut self, generics: &'c Generics, kind: RibKind<'a>, f: F)
where F: FnOnce(&mut Self)
{
debug!("with_generic_param_rib");
- match generic_params {
- HasGenericParams(generics, rib_kind) => {
- let mut function_type_rib = Rib::new(rib_kind);
- let mut function_value_rib = Rib::new(rib_kind);
- let mut seen_bindings = FxHashMap::default();
- // We also can't shadow bindings from the parent item
- if let AssocItemRibKind = rib_kind {
- let mut add_bindings_for_ns = |ns| {
- let parent_rib = self.ribs[ns].iter()
- .rfind(|rib| if let ItemRibKind = rib.kind { true } else { false })
- .expect("associated item outside of an item");
- seen_bindings.extend(
- parent_rib.bindings.iter().map(|(ident, _)| (*ident, ident.span)),
- );
- };
- add_bindings_for_ns(ValueNS);
- add_bindings_for_ns(TypeNS);
- }
- for param in &generics.params {
- match param.kind {
- GenericParamKind::Lifetime { .. } => {}
- GenericParamKind::Type { .. } => {
- let ident = param.ident.modern();
- debug!("with_generic_param_rib: {}", param.id);
-
- if seen_bindings.contains_key(&ident) {
- let span = seen_bindings.get(&ident).unwrap();
- let err = ResolutionError::NameAlreadyUsedInParameterList(
- ident.name,
- *span,
- );
- self.r.report_error(param.ident.span, err);
- }
- seen_bindings.entry(ident).or_insert(param.ident.span);
-
- // Plain insert (no renaming).
- let res = Res::Def(
- DefKind::TyParam,
- self.r.definitions.local_def_id(param.id),
- );
- function_type_rib.bindings.insert(ident, res);
- self.r.record_partial_res(param.id, PartialRes::new(res));
- }
- GenericParamKind::Const { .. } => {
- let ident = param.ident.modern();
- debug!("with_generic_param_rib: {}", param.id);
-
- if seen_bindings.contains_key(&ident) {
- let span = seen_bindings.get(&ident).unwrap();
- let err = ResolutionError::NameAlreadyUsedInParameterList(
- ident.name,
- *span,
- );
- self.r.report_error(param.ident.span, err);
- }
- seen_bindings.entry(ident).or_insert(param.ident.span);
-
- let res = Res::Def(
- DefKind::ConstParam,
- self.r.definitions.local_def_id(param.id),
- );
- function_value_rib.bindings.insert(ident, res);
- self.r.record_partial_res(param.id, PartialRes::new(res));
- }
- }
- }
- self.ribs[ValueNS].push(function_value_rib);
- self.ribs[TypeNS].push(function_type_rib);
+ let mut function_type_rib = Rib::new(kind);
+ let mut function_value_rib = Rib::new(kind);
+ let mut seen_bindings = FxHashMap::default();
+
+ // We also can't shadow bindings from the parent item
+ if let AssocItemRibKind = kind {
+ let mut add_bindings_for_ns = |ns| {
+ let parent_rib = self.ribs[ns].iter()
+ .rfind(|r| if let ItemRibKind(_) = r.kind { true } else { false })
+ .expect("associated item outside of an item");
+ seen_bindings.extend(
+ parent_rib.bindings.iter().map(|(ident, _)| (*ident, ident.span)),
+ );
+ };
+ add_bindings_for_ns(ValueNS);
+ add_bindings_for_ns(TypeNS);
+ }
+
+ for param in &generics.params {
+ if let GenericParamKind::Lifetime { .. } = param.kind {
+ continue;
}
- NoGenericParams => {
- // Nothing to do.
+ let def_kind = match param.kind {
+ GenericParamKind::Type { .. } => DefKind::TyParam,
+ GenericParamKind::Const { .. } => DefKind::ConstParam,
+ _ => unreachable!(),
+ };
+
+ let ident = param.ident.modern();
+ debug!("with_generic_param_rib: {}", param.id);
+
+ if seen_bindings.contains_key(&ident) {
+ let span = seen_bindings.get(&ident).unwrap();
+ let err = ResolutionError::NameAlreadyUsedInParameterList(
+ ident.name,
+ *span,
+ );
+ self.r.report_error(param.ident.span, err);
+ }
+ seen_bindings.entry(ident).or_insert(param.ident.span);
+
+ // Plain insert (no renaming).
+ let res = Res::Def(def_kind, self.r.definitions.local_def_id(param.id));
+
+ match param.kind {
+ GenericParamKind::Type { .. } => {
+ function_type_rib.bindings.insert(ident, res);
+ self.r.record_partial_res(param.id, PartialRes::new(res));
+ }
+ GenericParamKind::Const { .. } => {
+ function_value_rib.bindings.insert(ident, res);
+ self.r.record_partial_res(param.id, PartialRes::new(res));
+ }
+ _ => unreachable!(),
}
}
+ self.ribs[ValueNS].push(function_value_rib);
+ self.ribs[TypeNS].push(function_type_rib);
+
f(self);
- if let HasGenericParams(..) = generic_params {
- self.ribs[TypeNS].pop();
- self.ribs[ValueNS].pop();
- }
+ self.ribs[TypeNS].pop();
+ self.ribs[ValueNS].pop();
}
fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) {
self.label_ribs.pop();
}
- fn with_item_rib(&mut self, f: impl FnOnce(&mut Self)) {
- self.with_rib(ValueNS, ItemRibKind, |this| this.with_rib(TypeNS, ItemRibKind, f))
+ fn with_item_rib(&mut self, has_generic_params: HasGenericParams, f: impl FnOnce(&mut Self)) {
+ let kind = ItemRibKind(has_generic_params);
+ self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
}
fn with_constant_rib(&mut self, f: impl FnOnce(&mut Self)) {
impl_items: &[ImplItem]) {
debug!("resolve_implementation");
// If applicable, create a rib for the type parameters.
- self.with_generic_param_rib(HasGenericParams(generics, ItemRibKind), |this| {
+ self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
// Dummy self type for better errors if `Self` is used in the trait path.
this.with_self_rib(Res::SelfTy(None, None), |this| {
// Resolve the trait reference, if necessary.
debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
for impl_item in impl_items {
// We also need a new scope for the impl item type parameters.
- let generic_params = HasGenericParams(&impl_item.generics,
- AssocItemRibKind);
- this.with_generic_param_rib(generic_params, |this| {
+ this.with_generic_param_rib(&impl_item.generics,
+ AssocItemRibKind,
+ |this| {
use crate::ResolutionError::*;
match impl_item.kind {
ImplItemKind::Const(..) => {
// abort if the module is already found
if result.is_some() { break; }
- in_module.for_each_child_stable(self.r, |_, ident, _, name_binding| {
+ in_module.for_each_child(self.r, |_, ident, _, name_binding| {
// abort if the module is already found or if name_binding is private external
if result.is_some() || !name_binding.vis.is_visible_locally() {
return
fn collect_enum_variants(&mut self, def_id: DefId) -> Option<Vec<Path>> {
self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| {
let mut variants = Vec::new();
- enum_module.for_each_child_stable(self.r, |_, ident, _, name_binding| {
+ enum_module.for_each_child(self.r, |_, ident, _, name_binding| {
if let Res::Def(DefKind::Variant, _) = name_binding.res() {
let mut segms = enum_import_suggestion.path.segments.clone();
segms.push(ast::PathSegment::from_ident(ident));
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
-#![feature(inner_deref)]
#![feature(crate_visibility_modifier)]
#![feature(label_break_value)]
#![feature(mem_take)]
use std::collections::BTreeSet;
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::fx::FxIndexMap;
use diagnostics::{Suggestion, ImportSuggestion};
use diagnostics::{find_span_of_binding_until_next_binding, extend_span_to_previous_binding};
-use late::{PathSource, Rib, RibKind::*};
+use late::{HasGenericParams, PathSource, Rib, RibKind::*};
use resolve_imports::{ImportDirective, ImportDirectiveSubclass, NameResolution, ImportResolver};
use macros::{LegacyBinding, LegacyScope};
enum ResolutionError<'a> {
/// Error E0401: can't use type or const parameters from outer function.
- GenericParamsFromOuterFunction(Res),
+ GenericParamsFromOuterFunction(Res, HasGenericParams),
/// Error E0403: the name is already used for a type or const parameter in this generic
/// parameter list.
NameAlreadyUsedInParameterList(Name, Span),
}
}
-type Resolutions<'a> = RefCell<FxHashMap<(Ident, Namespace), &'a RefCell<NameResolution<'a>>>>;
+type Resolutions<'a> = RefCell<FxIndexMap<(Ident, Namespace), &'a RefCell<NameResolution<'a>>>>;
/// One node in the tree of modules.
pub struct ModuleData<'a> {
}
}
- fn for_each_child_stable<R, F>(&'a self, resolver: &mut R, mut f: F)
- where R: AsMut<Resolver<'a>>, F: FnMut(&mut R, Ident, Namespace, &'a NameBinding<'a>)
- {
- let resolutions = resolver.as_mut().resolutions(self).borrow();
- let mut resolutions = resolutions.iter().collect::<Vec<_>>();
- resolutions.sort_by_cached_key(|&(&(ident, ns), _)| (ident.as_str(), ns));
- for &(&(ident, ns), &resolution) in resolutions.iter() {
- resolution.borrow().binding.map(|binding| f(resolver, ident, ns, binding));
- }
- }
-
fn res(&self) -> Option<Res> {
match self.kind {
ModuleKind::Def(kind, def_id, _) => Some(Res::Def(kind, def_id)),
/// Entry point to crate resolution.
pub fn resolve_crate(&mut self, krate: &Crate) {
+ let _prof_timer =
+ self.session.prof.generic_activity("resolve_crate");
+
ImportResolver { r: self }.finalize_imports();
self.finalize_macro_resolutions();
ForwardTyParamBanRibKind | TyParamAsConstParamTy => {
// Nothing to do. Continue.
}
- ItemRibKind | FnItemRibKind | AssocItemRibKind => {
+ ItemRibKind(_) | FnItemRibKind | AssocItemRibKind => {
// This was an attempt to access an upvar inside a
// named function item. This is not allowed, so we
// report an error.
}
Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => {
for rib in ribs {
- match rib.kind {
+ let has_generic_params = match rib.kind {
NormalRibKind | AssocItemRibKind |
ModuleRibKind(..) | MacroDefinition(..) | ForwardTyParamBanRibKind |
ConstantItemRibKind | TyParamAsConstParamTy => {
// Nothing to do. Continue.
+ continue;
}
- ItemRibKind | FnItemRibKind => {
- // This was an attempt to use a type parameter outside its scope.
- if record_used {
- self.report_error(
- span, ResolutionError::GenericParamsFromOuterFunction(res)
- );
- }
- return Res::Err;
- }
+ // This was an attempt to use a type parameter outside its scope.
+ ItemRibKind(has_generic_params) => has_generic_params,
+ FnItemRibKind => HasGenericParams::Yes,
+ };
+
+ if record_used {
+ self.report_error(span, ResolutionError::GenericParamsFromOuterFunction(
+ res, has_generic_params));
}
+ return Res::Err;
}
}
Res::Def(DefKind::ConstParam, _) => {
ribs.next();
}
for rib in ribs {
- if let ItemRibKind | FnItemRibKind = rib.kind {
- // This was an attempt to use a const parameter outside its scope.
- if record_used {
- self.report_error(
- span, ResolutionError::GenericParamsFromOuterFunction(res)
- );
- }
- return Res::Err;
+ let has_generic_params = match rib.kind {
+ ItemRibKind(has_generic_params) => has_generic_params,
+ FnItemRibKind => HasGenericParams::Yes,
+ _ => continue,
+ };
+
+ // This was an attempt to use a const parameter outside its scope.
+ if record_used {
+ self.report_error(span, ResolutionError::GenericParamsFromOuterFunction(
+ res, has_generic_params));
}
+ return Res::Err;
}
}
_ => {}
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
-#![feature(inner_deref)]
#![recursion_limit="256"]
--- /dev/null
+use crate::abi::call::{FnType, ArgType, Uniform};
+use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods};
+
+// Data layout: e-p:32:32-i64:64-v128:32:128-n32-S128
+
+// See the https://github.com/kripken/emscripten-fastcomp-clang repository.
+// The class `EmscriptenABIInfo` in `/lib/CodeGen/TargetInfo.cpp` contains the ABI definitions.
+
+fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
+ where Ty: TyLayoutMethods<'a, C> + Copy,
+ C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
+{
+ if ret.layout.is_aggregate() {
+ if let Some(unit) = ret.layout.homogeneous_aggregate(cx).unit() {
+ let size = ret.layout.size;
+ if unit.size == size {
+ ret.cast_to(Uniform {
+ unit,
+ total: size
+ });
+ return;
+ }
+ }
+
+ ret.make_indirect();
+ }
+}
+
+fn classify_arg_ty<Ty>(arg: &mut ArgType<'_, Ty>) {
+ if arg.layout.is_aggregate() {
+ arg.make_indirect_byval();
+ }
+}
+
+pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
+ where Ty: TyLayoutMethods<'a, C> + Copy,
+ C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
+{
+ if !fty.ret.is_ignore() {
+ classify_ret_ty(cx, &mut fty.ret);
+ }
+
+ for arg in &mut fty.args {
+ if arg.is_ignore() { continue; }
+ classify_arg_ty(arg);
+ }
+}
mod aarch64;
mod amdgpu;
mod arm;
+mod asmjs;
mod hexagon;
mod mips;
mod mips64;
mod x86_64;
mod x86_win64;
mod wasm32;
-mod wasm32_bindgen_compat;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PassMode {
"powerpc" => powerpc::compute_abi_info(cx, self),
"powerpc64" => powerpc64::compute_abi_info(cx, self),
"s390x" => s390x::compute_abi_info(cx, self),
+ "asmjs" => asmjs::compute_abi_info(cx, self),
+ "wasm32" => {
+ if cx.target_spec().llvm_target.contains("emscripten") {
+ asmjs::compute_abi_info(cx, self)
+ } else {
+ wasm32::compute_abi_info(self)
+ }
+ }
"msp430" => msp430::compute_abi_info(self),
"sparc" => sparc::compute_abi_info(cx, self),
"sparc64" => sparc64::compute_abi_info(cx, self),
"hexagon" => hexagon::compute_abi_info(self),
"riscv32" => riscv::compute_abi_info(self, 32),
"riscv64" => riscv::compute_abi_info(self, 64),
- "wasm32" if cx.target_spec().target_os != "emscripten"
- => wasm32_bindgen_compat::compute_abi_info(self),
- "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self),
a => return Err(format!("unrecognized arch \"{}\" in target specification", a))
}
-use crate::abi::call::{FnType, ArgType, Uniform};
-use crate::abi::{HasDataLayout, LayoutOf, TyLayout, TyLayoutMethods};
+use crate::abi::call::{FnType, ArgType};
-fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgType<'a, Ty>) -> bool
- where Ty: TyLayoutMethods<'a, C> + Copy,
- C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
-{
- if val.layout.is_aggregate() {
- if let Some(unit) = val.layout.homogeneous_aggregate(cx).unit() {
- let size = val.layout.size;
- if unit.size == size {
- val.cast_to(Uniform {
- unit,
- total: size
- });
- return true;
- }
- }
- }
- false
-}
-
-
-fn classify_ret_ty<'a, Ty, C>(cx: &C, ret: &mut ArgType<'a, Ty>)
- where Ty: TyLayoutMethods<'a, C> + Copy,
- C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
-{
+fn classify_ret_ty<Ty>(ret: &mut ArgType<'_, Ty>) {
ret.extend_integer_width_to(32);
- if ret.layout.is_aggregate() {
- if !unwrap_trivial_aggregate(cx, ret) {
- ret.make_indirect();
- }
- }
}
-fn classify_arg_ty<'a, Ty, C>(cx: &C, arg: &mut ArgType<'a, Ty>)
- where Ty: TyLayoutMethods<'a, C> + Copy,
- C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
-{
+fn classify_arg_ty<Ty>(arg: &mut ArgType<'_, Ty>) {
arg.extend_integer_width_to(32);
- if arg.layout.is_aggregate() {
- if !unwrap_trivial_aggregate(cx, arg) {
- arg.make_indirect_byval();
- }
- }
}
-pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
- where Ty: TyLayoutMethods<'a, C> + Copy,
- C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
-{
+pub fn compute_abi_info<Ty>(fty: &mut FnType<'_, Ty>) {
if !fty.ret.is_ignore() {
- classify_ret_ty(cx, &mut fty.ret);
+ classify_ret_ty(&mut fty.ret);
}
for arg in &mut fty.args {
if arg.is_ignore() { continue; }
- classify_arg_ty(cx, arg);
+ classify_arg_ty(arg);
}
}
+++ /dev/null
-// This is not and has never been a correct C ABI for WebAssembly, but
-// for a long time this was the C ABI that Rust used. wasm-bindgen
-// depends on ABI details for this ABI and is incompatible with the
-// correct C ABI, so this ABI is being kept around until wasm-bindgen
-// can be fixed to work with the correct ABI. See #63649 for further
-// discussion.
-
-use crate::abi::call::{FnType, ArgType};
-
-fn classify_ret_ty<Ty>(ret: &mut ArgType<'_, Ty>) {
- ret.extend_integer_width_to(32);
-}
-
-fn classify_arg_ty<Ty>(arg: &mut ArgType<'_, Ty>) {
- arg.extend_integer_width_to(32);
-}
-
-pub fn compute_abi_info<Ty>(fty: &mut FnType<'_, Ty>) {
- if !fty.ret.is_ignore() {
- classify_ret_ty(&mut fty.ret);
- }
-
- for arg in &mut fty.args {
- if arg.is_ignore() { continue; }
- classify_arg_ty(arg);
- }
-}
-use super::{LinkerFlavor, Target, wasm32_unknown_emscripten};
+use super::{LinkArgs, LinkerFlavor, Target, TargetOptions};
pub fn target() -> Result<Target, String> {
- let mut target = wasm32_unknown_emscripten::target()?;
- target.options.post_link_args
- .entry(LinkerFlavor::Em)
- .or_default()
- .extend(vec!["-s".to_string(), "WASM=0".to_string()]);
- Ok(target)
+ let mut args = LinkArgs::new();
+ args.insert(LinkerFlavor::Em,
+ vec!["-s".to_string(),
+ "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(),
+ "-s".to_string(),
+ "ABORTING_MALLOC=0".to_string(),
+ "-s".to_string(),
+ "WASM=0".to_string()]);
+
+ let opts = TargetOptions {
+ dynamic_linking: false,
+ executables: true,
+ exe_suffix: ".js".to_string(),
+ linker_is_gnu: true,
+ allow_asm: false,
+ obj_is_bitcode: true,
+ is_like_emscripten: true,
+ max_atomic_width: Some(32),
+ post_link_args: args,
+ target_family: Some("unix".to_string()),
+ codegen_backend: "emscripten".to_string(),
+ .. Default::default()
+ };
+ Ok(Target {
+ llvm_target: "asmjs-unknown-emscripten".to_string(),
+ target_endian: "little".to_string(),
+ target_pointer_width: "32".to_string(),
+ target_c_int_width: "32".to_string(),
+ target_os: "emscripten".to_string(),
+ target_env: String::new(),
+ target_vendor: "unknown".to_string(),
+ data_layout: "e-p:32:32-i64:64-v128:32:128-n32-S128".to_string(),
+ arch: "asmjs".to_string(),
+ linker_flavor: LinkerFlavor::Em,
+ options: opts,
+ })
}
("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
("wasm32-unknown-unknown", wasm32_unknown_unknown),
("wasm32-wasi", wasm32_wasi),
+ ("wasm32-experimental-emscripten", wasm32_experimental_emscripten),
("thumbv6m-none-eabi", thumbv6m_none_eabi),
("thumbv7m-none-eabi", thumbv7m_none_eabi),
--- /dev/null
+use super::{LinkArgs, LinkerFlavor, Target, TargetOptions};
+
+pub fn target() -> Result<Target, String> {
+ let mut post_link_args = LinkArgs::new();
+ post_link_args.insert(LinkerFlavor::Em,
+ vec!["-s".to_string(),
+ "WASM=1".to_string(),
+ "-s".to_string(),
+ "ASSERTIONS=1".to_string(),
+ "-s".to_string(),
+ "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(),
+ "-g3".to_string()]);
+
+ let opts = TargetOptions {
+ dynamic_linking: false,
+ executables: true,
+ // Today emcc emits two files - a .js file to bootstrap and
+ // possibly interpret the wasm, and a .wasm file
+ exe_suffix: ".js".to_string(),
+ linker_is_gnu: true,
+ link_env: vec![("EMCC_WASM_BACKEND".to_string(), "1".to_string())],
+ allow_asm: false,
+ obj_is_bitcode: true,
+ is_like_emscripten: true,
+ max_atomic_width: Some(32),
+ post_link_args,
+ limit_rdylib_exports: false,
+ target_family: Some("unix".to_string()),
+ .. Default::default()
+ };
+ Ok(Target {
+ llvm_target: "wasm32-unknown-unknown".to_string(),
+ target_endian: "little".to_string(),
+ target_pointer_width: "32".to_string(),
+ target_c_int_width: "32".to_string(),
+ target_os: "emscripten".to_string(),
+ target_env: String::new(),
+ target_vendor: "unknown".to_string(),
+ data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(),
+ arch: "wasm32".to_string(),
+ linker_flavor: LinkerFlavor::Em,
+ options: opts,
+ })
+}
-use super::wasm32_base;
-use super::{LinkArgs, LinkerFlavor, Target, TargetOptions, PanicStrategy};
+use super::{LinkArgs, LinkerFlavor, Target, TargetOptions};
pub fn target() -> Result<Target, String> {
+ // FIXME(nikic) BINARYEN_TRAP_MODE=clamp is needed to avoid trapping in our
+ // -Zsaturating-float-casts implementation. This can be dropped if/when
+ // we have native fpto[su]i.sat intrinsics, or the implementation otherwise
+ // stops relying on non-trapping fpto[su]i.
let mut post_link_args = LinkArgs::new();
post_link_args.insert(LinkerFlavor::Em,
vec!["-s".to_string(),
- "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(),
- "-s".to_string(),
- "ASSERTIONS=1".to_string(),
+ "BINARYEN=1".to_string(),
"-s".to_string(),
- "DISABLE_EXCEPTION_CATCHING=1".to_string(),
+ "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(),
"-s".to_string(),
- "ABORTING_MALLOC=0".to_string(),
- // FIXME(tlively): Enable this linker option once libc type errors
- // are resolved. See https://github.com/rust-lang/libc/pull/1478.
- // "-Wl,--fatal-warnings".to_string(),
- ]);
+ "BINARYEN_TRAP_MODE='clamp'".to_string()]);
let opts = TargetOptions {
- // emcc emits two files - a .js file to instantiate the wasm and supply platform
- // functionality, and a .wasm file.
+ dynamic_linking: false,
+ executables: true,
+ // Today emcc emits two files - a .js file to bootstrap and
+ // possibly interpret the wasm, and a .wasm file
exe_suffix: ".js".to_string(),
- linker: None,
linker_is_gnu: true,
+ allow_asm: false,
+ obj_is_bitcode: true,
is_like_emscripten: true,
- // FIXME(tlively): Emscripten supports unwinding, but we would have to pass
- // -enable-emscripten-cxx-exceptions to LLVM at codegen time and merge
- // https://reviews.llvm.org/rG5c3cdef84b82464756bb571c13c31cf7773860c3to use it.
- panic_strategy: PanicStrategy::Abort,
+ max_atomic_width: Some(32),
post_link_args,
+ limit_rdylib_exports: false,
target_family: Some("unix".to_string()),
- .. wasm32_base::options()
+ codegen_backend: "emscripten".to_string(),
+ .. Default::default()
};
Ok(Target {
- llvm_target: "wasm32-unknown-emscripten".to_string(),
+ llvm_target: "asmjs-unknown-emscripten".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
target_os: "emscripten".to_string(),
target_env: String::new(),
target_vendor: "unknown".to_string(),
- data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(),
+ data_layout: "e-p:32:32-i64:64-v128:32:128-n32-S128".to_string(),
arch: "wasm32".to_string(),
linker_flavor: LinkerFlavor::Em,
options: opts,
// *do* incorporate the upvars here.
let constraint = DtorckConstraint {
- outlives: substs.upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
+ outlives: substs.as_generator().upvar_tys(def_id, tcx).map(|t| t.into()).collect(),
dtorck_types: vec![],
overflows: vec![],
};
tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalPredicateGoal<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
+ debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
tcx.infer_ctxt().enter_with_canonical(
DUMMY_SP,
&canonical_goal,
|ref infcx, goal, _canonical_inference_vars| {
+ debug!("evaluate_obligation: goal={:#?}", goal);
let ParamEnvAnd {
param_env,
value: predicate,
}
crate fn generator(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> {
- tcx.mk_generator(def_id, ty::GeneratorSubsts {
- substs: InternalSubsts::bound_vars_for_item(tcx, def_id),
- }, hir::GeneratorMovability::Movable)
+ tcx.mk_generator(
+ def_id,
+ InternalSubsts::bound_vars_for_item(tcx, def_id),
+ hir::GeneratorMovability::Movable
+ )
}
}
});
if let Some(GeneratorTypes { yield_ty, interior, movability }) = generator_types {
- let substs = ty::GeneratorSubsts { substs };
+ let generator_substs = substs.as_generator();
self.demand_eqtype(
expr.span,
yield_ty,
- substs.yield_ty(expr_def_id, self.tcx),
+ generator_substs.yield_ty(expr_def_id, self.tcx),
);
self.demand_eqtype(
expr.span,
liberated_sig.output(),
- substs.return_ty(expr_def_id, self.tcx),
+ generator_substs.return_ty(expr_def_id, self.tcx),
);
self.demand_eqtype(
expr.span,
interior,
- substs.witness(expr_def_id, self.tcx),
+ generator_substs.witness(expr_def_id, self.tcx),
);
return self.tcx.mk_generator(expr_def_id, substs, movability);
}
Err(e) => e
};
+ let expr = expr.peel_drop_temps();
let cause = self.misc(expr.span);
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
// If the span is from a macro, then it's hard to extract the text
// and make a good suggestion, so don't bother.
- let is_macro = sp.from_expansion();
+ let is_desugaring = match sp.desugaring_kind() {
+ Some(k) => sp.is_desugaring(k),
+ None => false
+ };
+ let is_macro = sp.from_expansion() && !is_desugaring;
+
+ // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
+ let expr = expr.peel_drop_temps();
match (&expr.kind, &expected.kind, &checked_ty.kind) {
(_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.kind, &check.kind) {
}
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
- let expr = match &expr.kind {
- ExprKind::DropTemps(expr) => expr,
- _ => expr,
- };
+ let expr = expr.peel_drop_temps();
+ self.suggest_ref_or_into(&mut err, expr, expected_ty, ty);
extend_err(&mut err);
// Error possibly reported in `check_assign` so avoid emitting error again.
err.emit_unless(self.is_assign_to_bool(expr, expected_ty));
// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
// forgotten to import a trait.
- IllegalSizedBound(Vec<DefId>),
+ IllegalSizedBound(Vec<DefId>, bool),
// Found a match, but the return type is wrong
BadReturnType,
segment,
);
+ let mut needs_mut = false;
+ if let ty::Ref(region, t_type, mutability) = self_ty.kind {
+ let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
+ ty: t_type,
+ mutbl: mutability.invert(),
+ });
+ match self.lookup_probe(
+ span,
+ segment.ident,
+ trait_type,
+ call_expr,
+ ProbeScope::TraitsInScope
+ ) {
+ Ok(ref new_pick) if *new_pick != pick => {
+ needs_mut = true;
+ }
+ _ => {}
+ }
+ }
+
if result.illegal_sized_bound {
// We probe again, taking all traits into account (not only those in scope).
- let candidates =
- match self.lookup_probe(span,
- segment.ident,
- self_ty,
- call_expr,
- ProbeScope::AllTraits) {
-
- // If we find a different result the caller probably forgot to import a trait.
- Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
- Err(Ambiguity(ref sources)) => {
- sources.iter()
- .filter_map(|source| {
- match *source {
- // Note: this cannot come from an inherent impl,
- // because the first probing succeeded.
- ImplSource(def) => self.tcx.trait_id_of_impl(def),
- TraitSource(_) => None,
- }
- })
- .collect()
+ let candidates = match self.lookup_probe(
+ span,
+ segment.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ ) {
+ // If we find a different result the caller probably forgot to import a trait.
+ Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
+ Err(Ambiguity(ref sources)) => sources.iter().filter_map(|source| {
+ match *source {
+ // Note: this cannot come from an inherent impl,
+ // because the first probing succeeded.
+ ImplSource(def) => self.tcx.trait_id_of_impl(def),
+ TraitSource(_) => None,
}
- _ => Vec::new(),
- };
+ }).collect(),
+ _ => Vec::new(),
+ };
- return Err(IllegalSizedBound(candidates));
+ return Err(IllegalSizedBound(candidates, needs_mut));
}
Ok(result.callee)
err.span_label(span, "this is an associated function, not a method");
}
if static_sources.len() == 1 {
+ let ty_str = if let Some(CandidateSource::ImplSource(
+ impl_did,
+ )) = static_sources.get(0) {
+ // When the "method" is resolved through dereferencing, we really want the
+ // original type that has the associated function for accurate suggestions.
+ // (#61411)
+ let ty = self.impl_self_ty(span, *impl_did).ty;
+ match (&ty.peel_refs().kind, &actual.peel_refs().kind) {
+ (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
+ // Use `actual` as it will have more `substs` filled in.
+ self.ty_to_value_string(actual.peel_refs())
+ }
+ _ => self.ty_to_value_string(ty.peel_refs()),
+ }
+ } else {
+ self.ty_to_value_string(actual.peel_refs())
+ };
if let SelfSource::MethodCall(expr) = source {
- err.span_suggestion(expr.span.to(span),
- "use associated function syntax instead",
- format!("{}::{}",
- self.ty_to_string(actual),
- item_name),
- Applicability::MachineApplicable);
+ err.span_suggestion(
+ expr.span.to(span),
+ "use associated function syntax instead",
+ format!("{}::{}", ty_str, item_name),
+ Applicability::MachineApplicable,
+ );
} else {
- err.help(&format!("try with `{}::{}`",
- self.ty_to_string(actual), item_name));
+ err.help(&format!(
+ "try with `{}::{}`",
+ ty_str,
+ item_name,
+ ));
}
report_candidates(span, &mut err, static_sources);
}
}
- if let Some(lev_candidate) = lev_candidate {
+ let mut fallback_span = true;
+ let msg = "remove this method call";
+ if item_name.as_str() == "as_str" && actual.peel_refs().is_str() {
+ if let SelfSource::MethodCall(expr) = source {
+ let call_expr = self.tcx.hir().expect_expr(
+ self.tcx.hir().get_parent_node(expr.hir_id),
+ );
+ if let Some(span) = call_expr.span.trim_start(expr.span) {
+ err.span_suggestion(
+ span,
+ msg,
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ fallback_span = false;
+ }
+ }
+ if fallback_span {
+ err.span_label(span, msg);
+ }
+ } else if let Some(lev_candidate) = lev_candidate {
let def_kind = lev_candidate.def_kind();
err.span_suggestion(
span,
err.emit();
}
- MethodError::IllegalSizedBound(candidates) => {
+ MethodError::IllegalSizedBound(candidates, needs_mut) => {
let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
let mut err = self.sess().struct_span_err(span, &msg);
if !candidates.is_empty() {
- let help = format!("{an}other candidate{s} {were} found in the following \
- trait{s}, perhaps add a `use` for {one_of_them}:",
- an = if candidates.len() == 1 {"an" } else { "" },
- s = pluralise!(candidates.len()),
- were = if candidates.len() == 1 { "was" } else { "were" },
- one_of_them = if candidates.len() == 1 {
- "it"
- } else {
- "one_of_them"
- });
+ let help = format!(
+ "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
+ add a `use` for {one_of_them}:",
+ an = if candidates.len() == 1 {"an" } else { "" },
+ s = pluralise!(candidates.len()),
+ were = if candidates.len() == 1 { "was" } else { "were" },
+ one_of_them = if candidates.len() == 1 {
+ "it"
+ } else {
+ "one_of_them"
+ },
+ );
self.suggest_use_candidates(&mut err, help, candidates);
}
+ if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind {
+ if needs_mut {
+ let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
+ ty: t_type,
+ mutbl: mutability.invert(),
+ });
+ err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty));
+ }
+ }
err.emit();
}
None
}
+ /// Print out the type for use in value namespace.
+ fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
+ match ty.kind {
+ ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)),
+ _ => self.ty_to_string(ty),
+ }
+ }
+
fn suggest_use_candidates(&self,
err: &mut DiagnosticBuilder<'_>,
mut msg: String,
impl_id: DefId,
impl_item: &hir::ImplItem,
) {
- let ancestors = trait_def.ancestors(tcx, impl_id);
-
let kind = match impl_item.kind {
hir::ImplItemKind::Const(..) => ty::AssocKind::Const,
hir::ImplItemKind::Method(..) => ty::AssocKind::Method,
hir::ImplItemKind::TyAlias(_) => ty::AssocKind::Type,
};
- let parent = ancestors.defs(tcx, trait_item.ident, kind, trait_def.def_id).nth(1)
- .map(|node_item| node_item.map(|parent| parent.defaultness));
+ let mut ancestor_impls = trait_def.ancestors(tcx, impl_id)
+ .skip(1)
+ .filter_map(|parent| {
+ if parent.is_from_trait() {
+ None
+ } else {
+ Some((parent, parent.item(tcx, trait_item.ident, kind, trait_def.def_id)))
+ }
+ })
+ .peekable();
- if let Some(parent) = parent {
- if tcx.impl_item_is_final(&parent) {
- report_forbidden_specialization(tcx, impl_item, parent.node.def_id());
- }
+ if ancestor_impls.peek().is_none() {
+ // No parent, nothing to specialize.
+ return;
}
+ let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
+ match parent_item {
+ // Parent impl exists, and contains the parent item we're trying to specialize, but
+ // doesn't mark it `default`.
+ Some(parent_item) if tcx.impl_item_is_final(&parent_item) => {
+ Some(Err(parent_impl.def_id()))
+ }
+
+ // Parent impl contains item and makes it specializable.
+ Some(_) => {
+ Some(Ok(()))
+ }
+
+ // Parent impl doesn't mention the item. This means it's inherited from the
+ // grandparent. In that case, if parent is a `default impl`, inherited items use the
+ // "defaultness" from the grandparent, else they are final.
+ None => if tcx.impl_is_default(parent_impl.def_id()) {
+ None
+ } else {
+ Some(Err(parent_impl.def_id()))
+ }
+ }
+ });
+
+ // If `opt_result` is `None`, we have only encoutered `default impl`s that don't contain the
+ // item. This is allowed, the item isn't actually getting specialized here.
+ let result = opt_result.unwrap_or(Ok(()));
+
+ if let Err(parent_impl) = result {
+ report_forbidden_specialization(tcx, impl_item, parent_impl);
+ }
}
fn check_impl_items_against_trait<'tcx>(
let associated_type_overridden = overridden_associated_type.is_some();
for trait_item in tcx.associated_items(impl_trait_ref.def_id) {
let is_implemented = trait_def.ancestors(tcx, impl_id)
- .defs(tcx, trait_item.ident, trait_item.kind, impl_trait_ref.def_id)
- .next()
+ .leaf_def(tcx, trait_item.ident, trait_item.kind)
.map(|node_item| !node_item.node.is_from_trait())
.unwrap_or(false);
pub fn suggest_mismatched_types_on_tail(
&self,
err: &mut DiagnosticBuilder<'tcx>,
- expression: &'tcx hir::Expr,
+ expr: &'tcx hir::Expr,
expected: Ty<'tcx>,
found: Ty<'tcx>,
cause_span: Span,
blk_id: hir::HirId,
) -> bool {
- self.suggest_missing_semicolon(err, expression, expected, cause_span);
+ let expr = expr.peel_drop_temps();
+ self.suggest_missing_semicolon(err, expr, expected, cause_span);
let mut pointing_at_return_type = false;
if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
pointing_at_return_type = self.suggest_missing_return_type(
err, &fn_decl, expected, found, can_suggest);
}
- self.suggest_ref_or_into(err, expression, expected, found);
- self.suggest_boxing_when_appropriate(err, expression, expected, found);
+ self.suggest_ref_or_into(err, expr, expected, found);
+ self.suggest_boxing_when_appropriate(err, expr, expected, found);
pointing_at_return_type
}
_ => None
};
check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig);
+
+ // Prohibits applying `#[track_caller]` to trait decls
+ for attr in &trait_item.attrs {
+ if attr.check_name(sym::track_caller) {
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0738,
+ "`#[track_caller]` is not supported in trait declarations."
+ ).emit();
+ }
+ }
}
pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) {
hir::ImplItemKind::Method(ref sig, _) => Some(sig),
_ => None
};
+
+ // Prohibits applying `#[track_caller]` to trait impls
+ if method_sig.is_some() {
+ let track_caller_attr = impl_item.attrs.iter()
+ .find(|a| a.check_name(sym::track_caller));
+ if let Some(tc_attr) = track_caller_attr {
+ let parent_hir_id = tcx.hir().get_parent_item(hir_id);
+ let containing_item = tcx.hir().expect_item(parent_hir_id);
+ let containing_impl_is_for_trait = match &containing_item.kind {
+ hir::ItemKind::Impl(_, _, _, _, tr, _, _) => tr.is_some(),
+ _ => bug!("parent of an ImplItem must be an Impl"),
+ };
+
+ if containing_impl_is_for_trait {
+ struct_span_err!(
+ tcx.sess,
+ tc_attr.span,
+ E0738,
+ "`#[track_caller]` is not supported in traits yet."
+ ).emit();
+ }
+ }
+ }
+
check_associated_item(tcx, impl_item.hir_id, impl_item.span, method_sig);
}
let whitelist = tcx.target_features_whitelist(LOCAL_CRATE);
let mut inline_span = None;
+ let mut link_ordinal_span = None;
for attr in attrs.iter() {
if attr.check_name(sym::cold) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
} else if attr.check_name(sym::thread_local) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
+ } else if attr.check_name(sym::track_caller) {
+ if tcx.fn_sig(id).abi() != abi::Abi::Rust {
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0737,
+ "rust ABI is required to use `#[track_caller]`"
+ ).emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
} else if attr.check_name(sym::export_name) {
if let Some(s) = attr.value_str() {
if s.as_str().contains("\0") {
}
} else if attr.check_name(sym::link_name) {
codegen_fn_attrs.link_name = attr.value_str();
+ } else if attr.check_name(sym::link_ordinal) {
+ link_ordinal_span = Some(attr.span);
+ if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
+ codegen_fn_attrs.link_ordinal = ordinal;
+ }
}
}
// purpose functions as they wouldn't have the right target features
// enabled. For that reason we also forbid #[inline(always)] as it can't be
// respected.
+
if codegen_fn_attrs.target_features.len() > 0 {
if codegen_fn_attrs.inline == InlineAttr::Always {
if let Some(span) = inline_span {
codegen_fn_attrs.export_name = Some(name);
codegen_fn_attrs.link_name = Some(name);
}
+ check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
// Internal symbols to the standard library all have no_mangle semantics in
// that they have defined symbol names present in the function name. This
codegen_fn_attrs
}
+
+fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<usize> {
+ use syntax::ast::{Lit, LitIntType, LitKind};
+ let meta_item_list = attr.meta_item_list();
+ let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
+ let sole_meta_list = match meta_item_list {
+ Some([item]) => item.literal(),
+ _ => None,
+ };
+ if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
+ if *ordinal <= std::usize::MAX as u128 {
+ Some(*ordinal as usize)
+ } else {
+ let msg = format!(
+ "ordinal value in `link_ordinal` is too large: `{}`",
+ &ordinal
+ );
+ tcx.sess.struct_span_err(attr.span, &msg)
+ .note("the value may not exceed `std::usize::MAX`")
+ .emit();
+ None
+ }
+ } else {
+ tcx.sess.struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
+ .note("an unsuffixed integer value, e.g., `1`, is expected")
+ .emit();
+ None
+ }
+}
+
+fn check_link_name_xor_ordinal(
+ tcx: TyCtxt<'_>,
+ codegen_fn_attrs: &CodegenFnAttrs,
+ inline_span: Option<Span>,
+) {
+ if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
+ return;
+ }
+ let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
+ if let Some(span) = inline_span {
+ tcx.sess.span_err(span, msg);
+ } else {
+ tcx.sess.err(msg);
+ }
+}
differs from the behavior for `&T`, which is always `Copy`).
"##,
-/*
E0205: r##"
+#### Note: this error code is no longer emitted by the compiler.
+
An attempt to implement the `Copy` trait for an enum failed because one of the
variants does not implement `Copy`. To fix this, you must implement `Copy` for
the mentioned variant. Note that this may not be possible, as in the example of
-```compile_fail,E0205
+```compile_fail,E0204
enum Foo {
Bar(Vec<u32>),
Baz,
Here's another example that will fail:
-```compile_fail,E0205
+```compile_fail,E0204
#[derive(Copy)]
enum Foo<'a> {
Bar(&'a mut bool),
This fails because `&mut T` is not `Copy`, even when `T` is `Copy` (this
differs from the behavior for `&T`, which is always `Copy`).
"##,
-*/
E0206: r##"
You can only implement `Copy` for a struct or enum. Both of the following
[RFC 1023]: https://github.com/rust-lang/rfcs/blob/master/text/1023-rebalancing-coherence.md
"##,
-/*
E0211: r##"
+#### Note: this error code is no longer emitted by the compiler.
+
You used a function or type which doesn't fit the requirements for where it was
used. Erroneous code examples:
}
```
-The second case example is a bit particular : the main function must always
+The second case example is a bit particular: the main function must always
have this definition:
```compile_fail
}
```
"##,
- */
E0220: r##"
You used an associated type which isn't defined in the trait.
[`CoerceUnsized`]: https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html
"##,
-/*
-// Associated consts can now be accessed through generic type parameters, and
-// this error is no longer emitted.
-//
-// FIXME: consider whether to leave it in the error index, or remove it entirely
-// as associated consts is not stabilized yet.
-
E0329: r##"
+#### Note: this error code is no longer emitted by the compiler.
+
An attempt was made to access an associated constant through either a generic
type parameter or `Self`. This is not supported yet. An example causing this
error is shown below:
struct MyStruct;
+impl Foo for MyStruct {
+ const BAR: f64 = 0f64;
+}
+
fn get_bar_good() -> f64 {
<MyStruct as Foo>::BAR
}
```
"##,
-*/
E0366: r##"
An attempt was made to implement `Drop` on a concrete specialization of a
The `Box<...>` ensures that the result is of known size,
and the pin is required to keep it in the same place in memory.
"##,
+
+E0737: r##"
+#[track_caller] requires functions to have the "Rust" ABI for implicitly
+receiving caller location. See [RFC 2091] for details on this and other
+restrictions.
+
+Erroneous code example:
+
+```compile_fail,E0737
+#![feature(track_caller)]
+
+#[track_caller]
+extern "C" fn foo() {}
+```
+
+[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md
+"##,
+
+E0738: r##"
+#[track_caller] cannot be used in traits yet. This is due to limitations in the
+compiler which are likely to be temporary. See [RFC 2091] for details on this
+and other restrictions.
+
+Erroneous example with a trait method implementation:
+
+```compile_fail,E0738
+#![feature(track_caller)]
+
+trait Foo {
+ fn bar(&self);
+}
+
+impl Foo for u64 {
+ #[track_caller]
+ fn bar(&self) {}
+}
+```
+
+Erroneous example with a blanket trait method implementation:
+
+```compile_fail,E0738
+#![feature(track_caller)]
+
+trait Foo {
+ #[track_caller]
+ fn bar(&self) {}
+ fn baz(&self);
+}
+```
+
+Erroneous example with a trait method declaration:
+
+```compile_fail,E0738
+#![feature(track_caller)]
+
+trait Foo {
+ fn bar(&self) {}
+
+ #[track_caller]
+ fn baz(&self);
+}
+```
+
+Note that while the compiler may be able to support the attribute in traits in
+the future, [RFC 2091] prohibits their implementation without a follow-up RFC.
+
+[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md
+"##,
+
;
// E0035, merged into E0087/E0089
// E0036, merged into E0087/E0089
// between structures with the same definition
// E0558, // replaced with a generic attribute input check
// E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
- E0564, // only named lifetimes are allowed in `impl Trait`,
+// E0564, // only named lifetimes are allowed in `impl Trait`,
// but `{}` was found in the type `{}`
E0587, // type has conflicting packed and align representation hints
E0588, // packed type cannot transitively contain a `[repr(align)]` type
E0634, // type has conflicting packed representaton hints
E0640, // infer outlives requirements
E0641, // cannot cast to/from a pointer with an unknown kind
- E0645, // trait aliases not finished
+// E0645, // trait aliases not finished
E0719, // duplicate values for associated type binding
E0722, // Malformed `#[optimize]` attribute
E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
#![feature(nll)]
#![feature(slice_patterns)]
#![feature(never_type)]
-#![feature(inner_deref)]
#![feature(mem_take)]
#![recursion_limit="256"]
[dependencies]
pulldown-cmark = { version = "0.5.3", default-features = false }
minifier = "0.0.33"
-rayon = { version = "0.2.0", package = "rustc-rayon" }
+rayon = { version = "0.3.0", package = "rustc-rayon" }
tempfile = "3"
("target_arch", Some(arch)) => match &*arch.as_str() {
"aarch64" => "AArch64",
"arm" => "ARM",
- "asmjs" => "JavaScript",
+ "asmjs" => "asm.js",
"mips" => "MIPS",
"mips64" => "MIPS-64",
"msp430" => "MSP430",
Item {
source: Span::empty(),
name: Some(kw.clone()),
- attrs: attrs,
+ attrs,
visibility: Public,
stability: get_stability(cx, def_id),
deprecation: get_deprecation(cx, def_id),
did: cx.tcx.hir().local_def_id(self.hir_id),
bounds: self.bounds.clean(cx),
default: default.clean(cx),
- synthetic: synthetic,
+ synthetic,
})
}
hir::GenericParamKind::Const { ref ty } => {
let is_spotlight = attrs.has_doc_flag(sym::spotlight);
Item {
name: Some(self.name.clean(cx)),
- attrs: attrs,
+ attrs,
source: self.whence.clean(cx),
def_id: cx.tcx.hir().local_def_id(self.id),
visibility: self.vis.clean(cx),
} else {
Some(l.clean(cx))
};
- BorrowedRef {lifetime: lifetime, mutability: m.mutbl.clean(cx),
+ BorrowedRef {lifetime, mutability: m.mutbl.clean(cx),
type_: box m.ty.clean(cx)}
}
TyKind::Slice(ref ty) => Slice(box ty.clean(cx)),
let path = external_path(cx, cx.tcx.item_name(did),
None, false, vec![], InternalSubsts::empty());
ResolvedPath {
- path: path,
+ path,
param_names: None,
- did: did,
+ did,
is_generic: false,
}
}
_ => false,
};
let did = register_res(&*cx, path.res);
- ResolvedPath { path: path, param_names: None, did: did, is_generic: is_generic }
+ ResolvedPath { path, param_names: None, did, is_generic }
}
pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId {
#![feature(crate_visibility_modifier)]
#![feature(const_fn)]
#![feature(drain_filter)]
-#![feature(inner_deref)]
#![feature(never_type)]
#![feature(mem_take)]
#![feature(unicode_internals)]
krate.version = crate_version;
f(Output {
- krate: krate,
- renderinfo: renderinfo,
+ krate,
+ renderinfo,
renderopts,
})
});
compiler_builtins = { version = "0.1.16" }
profiler_builtins = { path = "../libprofiler_builtins", optional = true }
unwind = { path = "../libunwind" }
-hashbrown = { version = "0.5.0", features = ['rustc-dep-of-std'] }
+hashbrown = { version = "0.6.1", default-features = false, features = ['rustc-dep-of-std'] }
[dependencies.backtrace_rs]
package = "backtrace"
Vacant(entry) => Vacant(entry),
}
}
+
+ /// Sets the value of the entry, and returns an OccupiedEntry.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(entry_insert)]
+ /// use std::collections::HashMap;
+ ///
+ /// let mut map: HashMap<&str, String> = HashMap::new();
+ /// let entry = map.entry("poneyland").insert("hoho".to_string());
+ ///
+ /// assert_eq!(entry.key(), &"poneyland");
+ /// ```
+ #[inline]
+ #[unstable(feature = "entry_insert", issue = "65225")]
+ pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V> {
+ match self {
+ Occupied(mut entry) => {
+ entry.insert(value);
+ entry
+ },
+ Vacant(entry) => entry.insert_entry(value),
+ }
+ }
}
impl<'a, K, V: Default> Entry<'a, K, V> {
pub fn insert(self, value: V) -> &'a mut V {
self.base.insert(value)
}
+
+ /// Sets the value of the entry with the VacantEntry's key,
+ /// and returns an OccupiedEntry.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::HashMap;
+ /// use std::collections::hash_map::Entry;
+ ///
+ /// let mut map: HashMap<&str, u32> = HashMap::new();
+ ///
+ /// if let Entry::Vacant(o) = map.entry("poneyland") {
+ /// o.insert(37);
+ /// }
+ /// assert_eq!(map["poneyland"], 37);
+ /// ```
+ #[inline]
+ fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> {
+ let base = self.base.insert_entry(value);
+ OccupiedEntry { base }
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg(windows)]
let invalid_options = 87; // ERROR_INVALID_PARAMETER
- #[cfg(unix)]
+ #[cfg(all(unix, not(target_os = "vxworks")))]
let invalid_options = "Invalid argument";
+ #[cfg(target_os = "vxworks")]
+ let invalid_options = "invalid argument";
// Test various combinations of creation modes and access modes.
//
/// # Examples
///
/// ```no_run
- /// #![feature(udp_peer_addr)]
/// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
///
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
/// [`NotConnected`]: ../../std/io/enum.ErrorKind.html#variant.NotConnected
///
/// ```no_run
- /// #![feature(udp_peer_addr)]
/// use std::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address");
/// assert_eq!(socket.peer_addr().unwrap_err().kind(),
/// ::std::io::ErrorKind::NotConnected);
/// ```
- #[unstable(feature = "udp_peer_addr", issue = "59127")]
+ #[stable(feature = "udp_peer_addr", since = "1.40.0")]
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.0.peer_addr()
}
use crate::mem;
use crate::ptr;
use crate::raw;
+use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sys::stdio::panic_output;
use crate::sys_common::rwlock::RWLock;
-use crate::sys_common::{thread_info, util, backtrace};
+use crate::sys_common::{thread_info, util};
+use crate::sys_common::backtrace::{self, RustBacktrace};
use crate::thread;
#[cfg(not(test))]
fn default_hook(info: &PanicInfo<'_>) {
// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
- let log_backtrace = if cfg!(feature = "backtrace") {
- let panics = update_panic_count(0);
-
- if panics >= 2 {
- Some(backtrace_rs::PrintFmt::Full)
- } else {
- backtrace::log_enabled()
- }
+ let backtrace_env = if update_panic_count(0) >= 2 {
+ RustBacktrace::Print(backtrace_rs::PrintFmt::Full)
} else {
- None
+ backtrace::rust_backtrace_env()
};
// The current implementation always returns `Some`.
let _ = writeln!(err, "thread '{}' panicked at '{}', {}",
name, msg, location);
- if cfg!(feature = "backtrace") {
- use crate::sync::atomic::{AtomicBool, Ordering};
-
- static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
+ static FIRST_PANIC: AtomicBool = AtomicBool::new(true);
- if let Some(format) = log_backtrace {
- let _ = backtrace::print(err, format);
- } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
- let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
- environment variable to display a backtrace.");
+ match backtrace_env {
+ RustBacktrace::Print(format) => drop(backtrace::print(err, format)),
+ RustBacktrace::Disabled => {}
+ RustBacktrace::RuntimeDisabled => {
+ if FIRST_PANIC.swap(false, Ordering::SeqCst) {
+ let _ = writeln!(err, "note: run with `RUST_BACKTRACE=1` \
+ environment variable to display a backtrace.");
+ }
}
}
};
sys::args::init(argc, argv);
// Let's run some code!
- #[cfg(feature = "backtrace")]
let exit_code = panic::catch_unwind(|| {
sys_common::backtrace::__rust_begin_short_backtrace(move || main())
});
- #[cfg(not(feature = "backtrace"))]
- let exit_code = panic::catch_unwind(move || main());
sys_common::cleanup();
exit_code.unwrap_or(101) as isize
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
-#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "hermit", target_os = "redox",
- target_os = "emscripten"))]
+#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "hermit", target_os = "redox"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
use crate::mem;
use crate::sys_common::thread_local::register_dtor_fallback;
let r = libc::pthread_rwlock_rdlock(self.inner.get());
if r == libc::EAGAIN {
panic!("rwlock maximum reader count exceeded");
- } else if r == libc::EDEADLK || *self.write_locked.get() {
+ } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
if r == 0 {
self.raw_unlock();
}
use crate::borrow::Cow;
use crate::io::prelude::*;
use crate::path::{self, Path, PathBuf};
+use crate::sync::atomic::{self, Ordering};
use crate::sys::mutex::Mutex;
use backtrace_rs::{BacktraceFmt, BytesOrWideString, PrintFmt};
Ok(())
}
-/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
-#[inline(never)]
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
where
F: FnOnce() -> T,
f()
}
+pub enum RustBacktrace {
+ Print(PrintFmt),
+ Disabled,
+ RuntimeDisabled,
+}
+
// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
-pub fn log_enabled() -> Option<PrintFmt> {
- use crate::sync::atomic::{self, Ordering};
+pub fn rust_backtrace_env() -> RustBacktrace {
+ // If the `backtrace` feature of this crate isn't enabled quickly return
+ // `None` so this can be constant propagated all over the place to turn
+ // optimize away callers.
+ if !cfg!(feature = "backtrace") {
+ return RustBacktrace::Disabled;
+ }
// Setting environment variables for Fuchsia components isn't a standard
// or easily supported workflow. For now, always display backtraces.
if cfg!(target_os = "fuchsia") {
- return Some(PrintFmt::Full);
+ return RustBacktrace::Print(PrintFmt::Full);
}
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
match ENABLED.load(Ordering::SeqCst) {
0 => {}
- 1 => return None,
- 2 => return Some(PrintFmt::Short),
- _ => return Some(PrintFmt::Full),
+ 1 => return RustBacktrace::RuntimeDisabled,
+ 2 => return RustBacktrace::Print(PrintFmt::Short),
+ _ => return RustBacktrace::Print(PrintFmt::Full),
}
- let val = env::var_os("RUST_BACKTRACE").and_then(|x| {
- if &x == "0" {
- None
- } else if &x == "full" {
- Some(PrintFmt::Full)
- } else {
- Some(PrintFmt::Short)
- }
- });
- ENABLED.store(
- match val {
- Some(v) => v as isize,
- None => 1,
- },
- Ordering::SeqCst,
- );
- val
+ let (format, cache) = env::var_os("RUST_BACKTRACE")
+ .map(|x| {
+ if &x == "0" {
+ (RustBacktrace::RuntimeDisabled, 1)
+ } else if &x == "full" {
+ (RustBacktrace::Print(PrintFmt::Full), 3)
+ } else {
+ (RustBacktrace::Print(PrintFmt::Short), 2)
+ }
+ })
+ .unwrap_or((RustBacktrace::RuntimeDisabled, 1));
+ ENABLED.store(cache, Ordering::SeqCst);
+ format
}
/// Prints the filename of the backtrace frame.
}
thread_info::set(imp::guard::current(), their_thread);
- #[cfg(feature = "backtrace")]
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
}));
- #[cfg(not(feature = "backtrace"))]
- let try_result = panic::catch_unwind(panic::AssertUnwindSafe(f));
*their_packet.get() = Some(try_result);
};
use crate::cmp;
use crate::error::Error;
use crate::fmt;
-use crate::ops::{Add, Sub, AddAssign, SubAssign};
+use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::sys::time;
-use crate::sys_common::FromInner;
use crate::sys_common::mutex::Mutex;
+use crate::sys_common::FromInner;
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;
// * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar
// Firefox bug
//
- // It simply seems that this it just happens so that a lot in the wild
- // we're seeing panics across various platforms where consecutive calls
+ // It seems that this just happens a lot in the wild.
+ // We're seeing panics across various platforms where consecutive calls
// to `Instant::now`, such as via the `elapsed` function, are panicking
// as they're going backwards. Placed here is a last-ditch effort to try
// to fix things up. We keep a global "latest now" instance which is
// returned instead of what the OS says if the OS goes backwards.
//
- // To hopefully mitigate the impact of this though a few platforms are
+ // To hopefully mitigate the impact of this, a few platforms are
// whitelisted as "these at least haven't gone backwards yet".
if time::Instant::actually_monotonic() {
- return Instant(os_now)
+ return Instant(os_now);
}
static LOCK: Mutex = Mutex::new();
///
/// [`checked_add`]: ../../std/time/struct.Instant.html#method.checked_add
fn add(self, other: Duration) -> Instant {
- self.checked_add(other)
- .expect("overflow when adding duration to instant")
+ self.checked_add(other).expect("overflow when adding duration to instant")
}
}
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
- self.checked_sub(other)
- .expect("overflow when subtracting duration from instant")
+ self.checked_sub(other).expect("overflow when subtracting duration from instant")
}
}
/// println!("{:?}", difference);
/// ```
#[stable(feature = "time2", since = "1.8.0")]
- pub fn duration_since(&self, earlier: SystemTime)
- -> Result<Duration, SystemTimeError> {
+ pub fn duration_since(&self, earlier: SystemTime) -> Result<Duration, SystemTimeError> {
self.0.sub_time(&earlier.0).map_err(SystemTimeError)
}
///
/// [`checked_add`]: ../../std/time/struct.SystemTime.html#method.checked_add
fn add(self, dur: Duration) -> SystemTime {
- self.checked_add(dur)
- .expect("overflow when adding duration to instant")
+ self.checked_add(dur).expect("overflow when adding duration to instant")
}
}
type Output = SystemTime;
fn sub(self, dur: Duration) -> SystemTime {
- self.checked_sub(dur)
- .expect("overflow when subtracting duration from instant")
+ self.checked_sub(dur).expect("overflow when subtracting duration from instant")
}
}
#[stable(feature = "time2", since = "1.8.0")]
impl Error for SystemTimeError {
- fn description(&self) -> &str { "other time was not earlier than self" }
+ fn description(&self) -> &str {
+ "other time was not earlier than self"
+ }
}
#[stable(feature = "time2", since = "1.8.0")]
#[cfg(test)]
mod tests {
- use super::{Instant, SystemTime, Duration, UNIX_EPOCH};
+ use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
macro_rules! assert_almost_eq {
- ($a:expr, $b:expr) => ({
+ ($a:expr, $b:expr) => {{
let (a, b) = ($a, $b);
if a != b {
- let (a, b) = if a > b {(a, b)} else {(b, a)};
- assert!(a - Duration::new(0, 1000) <= b,
- "{:?} is not almost equal to {:?}", a, b);
+ let (a, b) = if a > b { (a, b) } else { (b, a) };
+ assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b);
}
- })
+ }};
}
#[test]
fn instant_saturating_duration_since_nopanic() {
let a = Instant::now();
let ret = (a - Duration::new(1, 0)).saturating_duration_since(a);
- assert_eq!(ret, Duration::new(0,0));
+ assert_eq!(ret, Duration::new(0, 0));
}
#[test]
let second = Duration::new(1, 0);
assert_almost_eq!(a.duration_since(a - second).unwrap(), second);
- assert_almost_eq!(a.duration_since(a + second).unwrap_err()
- .duration(), second);
+ assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second);
assert_almost_eq!(a - second + second, a);
assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a);
let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0);
- let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000)
- + Duration::new(0, 500_000_000);
+ let one_second_from_epoch2 =
+ UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000);
assert_eq!(one_second_from_epoch, one_second_from_epoch2);
// checked_add_duration will not panic on overflow
pub enum BufferedEarlyLintId {
IllFormedAttributeInput,
MetaVariableMisuse,
+ IncompleteInclude,
}
/// Stores buffered lint info which can later be passed to `librustc`.
```
"##,
+E0551: r##"
+An invalid meta-item was used inside an attribute.
+
+Erroneous code example:
+
+```compile_fail,E0551
+#[deprecated(note)] // error!
+fn i_am_deprecated() {}
+```
+
+Meta items are the key-value pairs inside of an attribute. To fix this issue,
+you need to give a value to the `note` key. Example:
+
+```
+#[deprecated(note = "because")] // ok!
+fn i_am_deprecated() {}
+```
+"##,
+
E0552: r##"
A unrecognized representation attribute was used.
// rustc_deprecated attribute must be paired with either stable or unstable
// attribute
E0549,
- E0551, // incorrect meta item
E0553, // multiple rustc_const_unstable attributes
// E0555, // replaced with a generic attribute input check
E0584, // file for module `..` found at both .. and ..
/// Allows the use of or-patterns (e.g., `0 | 1`).
(active, or_patterns, "1.38.0", Some(54883), None),
+ /// Allows the definition of `const extern fn` and `const unsafe extern fn`.
+ (active, const_extern_fn, "1.40.0", Some(64926), None),
+
+ // Allows the use of raw-dylibs (RFC 2627).
+ (active, raw_dylib, "1.40.0", Some(58713), None),
+
+ /// Enable accurate caller location reporting during panic (RFC 2091).
+ (active, track_caller, "1.40.0", Some(47809), None),
+
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
sym::const_generics,
sym::or_patterns,
sym::let_chains,
+ sym::raw_dylib,
+ sym::track_caller,
];
"the `link_args` attribute is experimental and not portable across platforms, \
it is recommended to use `#[link(name = \"foo\")] instead",
),
+ gated!(
+ link_ordinal, Whitelisted, template!(List: "ordinal"), raw_dylib,
+ experimental!(link_ordinal)
+ ),
// Plugins:
(
),
gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)),
+ gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)),
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
gate_all!(async_closure, "async closures are unstable");
gate_all!(yields, generators, "yield syntax is experimental");
gate_all!(or_patterns, "or-patterns syntax is experimental");
+ gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
visit::walk_crate(&mut visitor, krate);
}
use log::{debug, trace};
use std::mem;
+const TURBOFISH: &'static str = "use `::<...>` instead of `<...>` to specify type arguments";
/// Creates a placeholder argument.
crate fn dummy_arg(ident: Ident) -> Param {
let pat = P(Pat {
}
/// Produces an error if comparison operators are chained (RFC #558).
- /// We only need to check the LHS, not the RHS, because all comparison ops
- /// have same precedence and are left-associative.
- crate fn check_no_chained_comparison(&self, lhs: &Expr, outer_op: &AssocOp) -> PResult<'a, ()> {
- debug_assert!(outer_op.is_comparison(),
- "check_no_chained_comparison: {:?} is not comparison",
- outer_op);
+ /// We only need to check the LHS, not the RHS, because all comparison ops have same
+ /// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
+ ///
+ /// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
+ /// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
+ /// case.
+ ///
+ /// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
+ /// associative we can infer that we have:
+ ///
+ /// outer_op
+ /// / \
+ /// inner_op r2
+ /// / \
+ /// l1 r1
+ crate fn check_no_chained_comparison(
+ &mut self,
+ lhs: &Expr,
+ outer_op: &AssocOp,
+ ) -> PResult<'a, Option<P<Expr>>> {
+ debug_assert!(
+ outer_op.is_comparison(),
+ "check_no_chained_comparison: {:?} is not comparison",
+ outer_op,
+ );
+
+ let mk_err_expr = |this: &Self, span| {
+ Ok(Some(this.mk_expr(span, ExprKind::Err, ThinVec::new())))
+ };
+
match lhs.kind {
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
// Respan to include both operators.
- let op_span = op.span.to(self.token.span);
+ let op_span = op.span.to(self.prev_span);
let mut err = self.struct_span_err(
op_span,
"chained comparison operators require parentheses",
);
+
+ let suggest = |err: &mut DiagnosticBuilder<'_>| {
+ err.span_suggestion_verbose(
+ op_span.shrink_to_lo(),
+ TURBOFISH,
+ "::".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ };
+
if op.node == BinOpKind::Lt &&
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
*outer_op == AssocOp::Greater // even in a case like the following:
{ // Foo<Bar<Baz<Qux, ()>>>
- err.help(
- "use `::<...>` instead of `<...>` if you meant to specify type arguments");
- err.help("or use `(...)` if you meant to specify fn arguments");
- // These cases cause too many knock-down errors, bail out (#61329).
- return Err(err);
+ if *outer_op == AssocOp::Less {
+ let snapshot = self.clone();
+ self.bump();
+ // So far we have parsed `foo<bar<`, consume the rest of the type args.
+ let modifiers = [
+ (token::Lt, 1),
+ (token::Gt, -1),
+ (token::BinOp(token::Shr), -2),
+ ];
+ self.consume_tts(1, &modifiers[..]);
+
+ if !&[
+ token::OpenDelim(token::Paren),
+ token::ModSep,
+ ].contains(&self.token.kind) {
+ // We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
+ // parser and bail out.
+ mem::replace(self, snapshot.clone());
+ }
+ }
+ return if token::ModSep == self.token.kind {
+ // We have some certainty that this was a bad turbofish at this point.
+ // `foo< bar >::`
+ suggest(&mut err);
+
+ let snapshot = self.clone();
+ self.bump(); // `::`
+
+ // Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
+ match self.parse_expr() {
+ Ok(_) => {
+ // 99% certain that the suggestion is correct, continue parsing.
+ err.emit();
+ // FIXME: actually check that the two expressions in the binop are
+ // paths and resynthesize new fn call expression instead of using
+ // `ExprKind::Err` placeholder.
+ mk_err_expr(self, lhs.span.to(self.prev_span))
+ }
+ Err(mut expr_err) => {
+ expr_err.cancel();
+ // Not entirely sure now, but we bubble the error up with the
+ // suggestion.
+ mem::replace(self, snapshot);
+ Err(err)
+ }
+ }
+ } else if token::OpenDelim(token::Paren) == self.token.kind {
+ // We have high certainty that this was a bad turbofish at this point.
+ // `foo< bar >(`
+ suggest(&mut err);
+ // Consume the fn call arguments.
+ match self.consume_fn_args() {
+ Err(()) => Err(err),
+ Ok(()) => {
+ err.emit();
+ // FIXME: actually check that the two expressions in the binop are
+ // paths and resynthesize new fn call expression instead of using
+ // `ExprKind::Err` placeholder.
+ mk_err_expr(self, lhs.span.to(self.prev_span))
+ }
+ }
+ } else {
+ // All we know is that this is `foo < bar >` and *nothing* else. Try to
+ // be helpful, but don't attempt to recover.
+ err.help(TURBOFISH);
+ err.help("or use `(...)` if you meant to specify fn arguments");
+ // These cases cause too many knock-down errors, bail out (#61329).
+ Err(err)
+ };
}
err.emit();
}
_ => {}
}
- Ok(())
+ Ok(None)
+ }
+
+ fn consume_fn_args(&mut self) -> Result<(), ()> {
+ let snapshot = self.clone();
+ self.bump(); // `(`
+
+ // Consume the fn call arguments.
+ let modifiers = [
+ (token::OpenDelim(token::Paren), 1),
+ (token::CloseDelim(token::Paren), -1),
+ ];
+ self.consume_tts(1, &modifiers[..]);
+
+ if self.token.kind == token::Eof {
+ // Not entirely sure that what we consumed were fn arguments, rollback.
+ mem::replace(self, snapshot);
+ Err(())
+ } else {
+ // 99% certain that the suggestion is correct, continue parsing.
+ Ok(())
+ }
}
crate fn maybe_report_ambiguous_plus(
err
}
+ fn consume_tts(
+ &mut self,
+ mut acc: i64, // `i64` because malformed code can have more closing delims than opening.
+ // Not using `FxHashMap` due to `token::TokenKind: !Eq + !Hash`.
+ modifier: &[(token::TokenKind, i64)],
+ ) {
+ while acc > 0 {
+ if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) {
+ acc += *val;
+ }
+ if self.token.kind == token::Eof {
+ break;
+ }
+ self.bump();
+ }
+ }
+
/// Replace duplicated recovered parameters with `_` pattern to avoid unecessary errors.
///
/// This is necessary because at this point we don't know whether we parsed a function with
pub yields: Lock<Vec<Span>>,
/// Spans collected for gating `or_patterns`, e.g. `Some(Foo | Bar)`.
pub or_patterns: Lock<Vec<Span>>,
+ /// Spans collected for gating `const_extern_fn`, e.g. `const extern fn foo`.
+ pub const_extern_fn: Lock<Vec<Span>>,
}
/// Info about a parsing session.
mod generics;
use crate::ast::{
- self, DUMMY_NODE_ID, AttrStyle, Attribute, BindingMode, CrateSugar, FnDecl, Ident,
+ self, DUMMY_NODE_ID, AttrStyle, Attribute, BindingMode, CrateSugar, Ident,
IsAsync, MacDelimiter, Mutability, Param, StrStyle, SelfKind, TyKind, Visibility,
VisibilityKind, Unsafety,
};
Ignore,
}
+/// The parsing configuration used to parse a parameter list (see `parse_fn_params`).
+struct ParamCfg {
+ /// Is `self` is allowed as the first parameter?
+ is_self_allowed: bool,
+ /// Is `...` allowed as the tail of the parameter list?
+ allow_c_variadic: bool,
+ /// `is_name_required` decides if, per-parameter,
+ /// the parameter must have a pattern or just a type.
+ is_name_required: fn(&token::Token) -> bool,
+}
+
/// Like `maybe_whole_expr`, but for things other than expressions.
#[macro_export]
macro_rules! maybe_whole {
res
}
- fn parse_fn_params(
- &mut self,
- named_params: bool,
- allow_c_variadic: bool,
- ) -> PResult<'a, Vec<Param>> {
+ /// Parses the parameter list of a function, including the `(` and `)` delimiters.
+ fn parse_fn_params(&mut self, mut cfg: ParamCfg) -> PResult<'a, Vec<Param>> {
let sp = self.token.span;
- let do_not_enforce_named_params_for_c_variadic = |token: &token::Token| {
- match token.kind {
- token::DotDotDot => false,
- _ => named_params,
- }
- };
+ let is_trait_item = cfg.is_self_allowed;
let mut c_variadic = false;
+ // Parse the arguments, starting out with `self` being possibly allowed...
let (params, _) = self.parse_paren_comma_seq(|p| {
- match p.parse_param_general(
- false,
- false,
- allow_c_variadic,
- do_not_enforce_named_params_for_c_variadic,
- ) {
+ let param = p.parse_param_general(&cfg, is_trait_item);
+ // ...now that we've parsed the first argument, `self` is no longer allowed.
+ cfg.is_self_allowed = false;
+
+ match param {
Ok(param) => Ok(
if let TyKind::CVarArgs = param.ty.kind {
c_variadic = true;
}
})?;
- let params: Vec<_> = params.into_iter().filter_map(|x| x).collect();
+ let mut params: Vec<_> = params.into_iter().filter_map(|x| x).collect();
+
+ // Replace duplicated recovered params with `_` pattern to avoid unecessary errors.
+ self.deduplicate_recovered_params_names(&mut params);
if c_variadic && params.len() <= 1 {
self.span_err(
Ok(params)
}
- /// Parses the parameter list and result type of a function that may have a `self` parameter.
- fn parse_fn_decl_with_self(
- &mut self,
- is_name_required: impl Copy + Fn(&token::Token) -> bool,
- ) -> PResult<'a, P<FnDecl>> {
- // Parse the arguments, starting out with `self` being allowed...
- let mut is_self_allowed = true;
- let (mut inputs, _): (Vec<_>, _) = self.parse_paren_comma_seq(|p| {
- let res = p.parse_param_general(is_self_allowed, true, false, is_name_required);
- // ...but now that we've parsed the first argument, `self` is no longer allowed.
- is_self_allowed = false;
- res
- })?;
-
- // Replace duplicated recovered params with `_` pattern to avoid unecessary errors.
- self.deduplicate_recovered_params_names(&mut inputs);
-
- Ok(P(FnDecl {
- inputs,
- output: self.parse_ret_ty(true)?,
- }))
- }
-
/// Skips unexpected attributes and doc comments in this position and emits an appropriate
/// error.
/// This version of parse param doesn't necessarily require identifier names.
- fn parse_param_general(
- &mut self,
- is_self_allowed: bool,
- is_trait_item: bool,
- allow_c_variadic: bool,
- is_name_required: impl Fn(&token::Token) -> bool,
- ) -> PResult<'a, Param> {
+ fn parse_param_general(&mut self, cfg: &ParamCfg, is_trait_item: bool) -> PResult<'a, Param> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
// Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
if let Some(mut param) = self.parse_self_param()? {
param.attrs = attrs.into();
- return if is_self_allowed {
+ return if cfg.is_self_allowed {
Ok(param)
} else {
self.recover_bad_self_param(param, is_trait_item)
};
}
- let is_name_required = is_name_required(&self.token);
+ let is_name_required = match self.token.kind {
+ token::DotDotDot => false,
+ _ => (cfg.is_name_required)(&self.token),
+ };
let (pat, ty) = if is_name_required || self.is_named_param() {
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
let pat = self.parse_fn_param_pat()?;
if let Err(mut err) = self.expect(&token::Colon) {
- if let Some(ident) = self.parameter_without_type(
+ return if let Some(ident) = self.parameter_without_type(
&mut err,
pat,
is_name_required,
- is_self_allowed,
+ cfg.is_self_allowed,
is_trait_item,
) {
err.emit();
- return Ok(dummy_arg(ident));
+ Ok(dummy_arg(ident))
} else {
- return Err(err);
- }
+ Err(err)
+ };
}
self.eat_incorrect_doc_comment_for_param_type();
- (pat, self.parse_ty_common(true, true, allow_c_variadic)?)
+ (pat, self.parse_ty_common(true, true, cfg.allow_c_variadic)?)
} else {
debug!("parse_param_general ident_to_pat");
let parser_snapshot_before_ty = self.clone();
self.eat_incorrect_doc_comment_for_param_type();
- let mut ty = self.parse_ty_common(true, true, allow_c_variadic);
+ let mut ty = self.parse_ty_common(true, true, cfg.allow_c_variadic);
if ty.is_ok() && self.token != token::Comma &&
self.token != token::CloseDelim(token::Paren) {
// This wasn't actually a type, but a pattern looking like a type,
Ok(())
}
+ /// Parses `extern` followed by an optional ABI string, or nothing.
+ fn parse_extern_abi(&mut self) -> PResult<'a, Abi> {
+ if self.eat_keyword(kw::Extern) {
+ Ok(self.parse_opt_abi()?.unwrap_or(Abi::C))
+ } else {
+ Ok(Abi::Rust)
+ }
+ }
+
/// Parses a string as an ABI spec on an extern type or module. Consumes
/// the `extern` keyword, if one is found.
fn parse_opt_abi(&mut self) -> PResult<'a, Option<Abi>> {
self.bump();
if op.is_comparison() {
- self.check_no_chained_comparison(&lhs, &op)?;
+ if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? {
+ return Ok(expr);
+ }
}
// Special cases:
if op == AssocOp::As {
-use super::{Parser, PResult, PathStyle, SemiColonMode, BlockMode};
+use super::{Parser, PResult, PathStyle, SemiColonMode, BlockMode, ParamCfg};
use crate::maybe_whole;
use crate::ptr::P;
use crate::ast::{
self, DUMMY_NODE_ID, Ident, Attribute, AttrStyle,
- Item, ItemKind, ImplItem, TraitItem, TraitItemKind,
+ Item, ItemKind, ImplItem, ImplItemKind, TraitItem, TraitItemKind,
UseTree, UseTreeKind, PathSegment,
IsAuto, Constness, IsAsync, Unsafety, Defaultness,
Visibility, VisibilityKind, Mutability, FnDecl, FnHeader, MethodSig, Block,
let lo = self.token.span;
- let visibility = self.parse_visibility(false)?;
+ let vis = self.parse_visibility(false)?;
if self.eat_keyword(kw::Use) {
// USE ITEM
self.expect(&token::Semi)?;
let span = lo.to(self.prev_span);
- let item =
- self.mk_item(span, Ident::invalid(), item_, visibility, attrs);
+ let item = self.mk_item(span, Ident::invalid(), item_, vis, attrs);
return Ok(Some(item));
}
if self.eat_keyword(kw::Extern) {
let extern_sp = self.prev_span;
if self.eat_keyword(kw::Crate) {
- return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?));
+ return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
}
let opt_abi = self.parse_opt_abi()?;
constness: respan(fn_span, Constness::NotConst),
abi: opt_abi.unwrap_or(Abi::C),
};
- return self.parse_item_fn(lo, visibility, attrs, header);
+ return self.parse_item_fn(lo, vis, attrs, header);
} else if self.check(&token::OpenDelim(token::Brace)) {
return Ok(Some(
- self.parse_item_foreign_mod(lo, opt_abi, visibility, attrs, extern_sp)?,
+ self.parse_item_foreign_mod(lo, opt_abi, vis, attrs, extern_sp)?,
));
}
self.bump();
// STATIC ITEM
let m = self.parse_mutability();
- let (ident, item_, extra_attrs) = self.parse_item_const(Some(m))?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_const(Some(m))?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.eat_keyword(kw::Const) {
let const_span = self.prev_span;
- if self.check_keyword(kw::Fn)
- || (self.check_keyword(kw::Unsafe)
- && self.is_keyword_ahead(1, &[kw::Fn])) {
+ if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
// CONST FUNCTION ITEM
let unsafety = self.parse_unsafety();
- self.bump();
+
+ if self.check_keyword(kw::Extern) {
+ self.sess.gated_spans.const_extern_fn.borrow_mut().push(
+ lo.to(self.token.span)
+ );
+ }
+ let abi = self.parse_extern_abi()?;
+ self.bump(); // `fn`
+
let header = FnHeader {
unsafety,
asyncness: respan(const_span, IsAsync::NotAsync),
constness: respan(const_span, Constness::Const),
- abi: Abi::Rust,
+ abi,
};
- return self.parse_item_fn(lo, visibility, attrs, header);
+ return self.parse_item_fn(lo, vis, attrs, header);
}
// CONST ITEM
)
.emit();
}
- let (ident, item_, extra_attrs) = self.parse_item_const(None)?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+
+ let info = self.parse_item_const(None)?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
// Parses `async unsafe? fn`.
constness: respan(fn_span, Constness::NotConst),
abi: Abi::Rust,
};
- return self.parse_item_fn(lo, visibility, attrs, header);
+ return self.parse_item_fn(lo, vis, attrs, header);
}
}
+
if self.check_keyword(kw::Unsafe) &&
self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
{
// UNSAFE TRAIT ITEM
self.bump(); // `unsafe`
- let is_auto = if self.eat_keyword(kw::Trait) {
- IsAuto::No
- } else {
- self.expect_keyword(kw::Auto)?;
- self.expect_keyword(kw::Trait)?;
- IsAuto::Yes
- };
- let (ident, item_, extra_attrs) = self.parse_item_trait(is_auto, Unsafety::Unsafe)?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_trait(Unsafety::Unsafe)?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.check_keyword(kw::Impl) ||
self.check_keyword(kw::Unsafe) &&
self.is_keyword_ahead(1, &[kw::Impl]) ||
self.check_keyword(kw::Default) &&
- self.is_keyword_ahead(1, &[kw::Impl, kw::Unsafe]) {
+ self.is_keyword_ahead(1, &[kw::Impl, kw::Unsafe])
+ {
// IMPL ITEM
let defaultness = self.parse_defaultness();
let unsafety = self.parse_unsafety();
self.expect_keyword(kw::Impl)?;
- let (ident, item_, extra_attrs) = self.parse_item_impl(unsafety, defaultness)?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_impl(unsafety, defaultness)?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.check_keyword(kw::Fn) {
// FUNCTION ITEM
self.bump();
constness: respan(fn_span, Constness::NotConst),
abi: Abi::Rust,
};
- return self.parse_item_fn(lo, visibility, attrs, header);
+ return self.parse_item_fn(lo, vis, attrs, header);
}
+
if self.check_keyword(kw::Unsafe)
- && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace)) {
+ && self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace))
+ {
// UNSAFE FUNCTION ITEM
self.bump(); // `unsafe`
// `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
self.check(&token::OpenDelim(token::Brace));
- let abi = if self.eat_keyword(kw::Extern) {
- self.parse_opt_abi()?.unwrap_or(Abi::C)
- } else {
- Abi::Rust
- };
+ let abi = self.parse_extern_abi()?;
self.expect_keyword(kw::Fn)?;
let fn_span = self.prev_span;
let header = FnHeader {
constness: respan(fn_span, Constness::NotConst),
abi,
};
- return self.parse_item_fn(lo, visibility, attrs, header);
+ return self.parse_item_fn(lo, vis, attrs, header);
}
+
if self.eat_keyword(kw::Mod) {
// MODULE ITEM
- let (ident, item_, extra_attrs) = self.parse_item_mod(&attrs[..])?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_mod(&attrs[..])?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if let Some(type_) = self.eat_type() {
let (ident, alias, generics) = type_?;
// TYPE ITEM
AliasKind::OpaqueTy(bounds) => ItemKind::OpaqueTy(bounds, generics),
};
let span = lo.to(self.prev_span);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ return Ok(Some(self.mk_item(span, ident, item_, vis, attrs)));
}
+
if self.eat_keyword(kw::Enum) {
// ENUM ITEM
- let (ident, item_, extra_attrs) = self.parse_item_enum()?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_enum()?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.check_keyword(kw::Trait)
|| (self.check_keyword(kw::Auto)
&& self.is_keyword_ahead(1, &[kw::Trait]))
{
- let is_auto = if self.eat_keyword(kw::Trait) {
- IsAuto::No
- } else {
- self.expect_keyword(kw::Auto)?;
- self.expect_keyword(kw::Trait)?;
- IsAuto::Yes
- };
// TRAIT ITEM
- let (ident, item_, extra_attrs) = self.parse_item_trait(is_auto, Unsafety::Normal)?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_trait(Unsafety::Normal)?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.eat_keyword(kw::Struct) {
// STRUCT ITEM
- let (ident, item_, extra_attrs) = self.parse_item_struct()?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_struct()?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
+
if self.is_union_item() {
// UNION ITEM
self.bump();
- let (ident, item_, extra_attrs) = self.parse_item_union()?;
- let span = lo.to(self.prev_span);
- let attrs = maybe_append(attrs, extra_attrs);
- return Ok(Some(self.mk_item(span, ident, item_, visibility, attrs)));
+ let info = self.parse_item_union()?;
+ return self.mk_item_with_info(attrs, lo, vis, info);
}
- if let Some(macro_def) = self.eat_macro_def(&attrs, &visibility, lo)? {
+
+ if let Some(macro_def) = self.eat_macro_def(&attrs, &vis, lo)? {
return Ok(Some(macro_def));
}
// Verify whether we have encountered a struct or method definition where the user forgot to
// add the `struct` or `fn` keyword after writing `pub`: `pub S {}`
- if visibility.node.is_pub() &&
+ if vis.node.is_pub() &&
self.check_ident() &&
self.look_ahead(1, |t| *t != token::Not)
{
return Err(err);
}
}
- self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, visibility)
+ self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, vis)
+ }
+
+ fn mk_item_with_info(
+ &self,
+ attrs: Vec<Attribute>,
+ lo: Span,
+ vis: Visibility,
+ info: ItemInfo,
+ ) -> PResult<'a, Option<P<Item>>> {
+ let (ident, item, extra_attrs) = info;
+ let span = lo.to(self.prev_span);
+ let attrs = maybe_append(attrs, extra_attrs);
+ Ok(Some(self.mk_item(span, ident, item, vis, attrs)))
}
fn recover_first_param(&mut self) -> &'static str {
};
(name, kind, generics)
} else if self.is_const_item() {
- // This parses the grammar:
- // ImplItemConst = "const" Ident ":" Ty "=" Expr ";"
- self.expect_keyword(kw::Const)?;
- let name = self.parse_ident()?;
- self.expect(&token::Colon)?;
- let typ = self.parse_ty()?;
- self.expect(&token::Eq)?;
- let expr = self.parse_expr()?;
- self.expect(&token::Semi)?;
- (name, ast::ImplItemKind::Const(typ, expr), Generics::default())
+ self.parse_impl_const()?
} else {
let (name, inner_attrs, generics, kind) = self.parse_impl_method(&vis, at_end)?;
attrs.extend(inner_attrs);
!self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe])
}
+ /// This parses the grammar:
+ /// ImplItemConst = "const" Ident ":" Ty "=" Expr ";"
+ fn parse_impl_const(&mut self) -> PResult<'a, (Ident, ImplItemKind, Generics)> {
+ self.expect_keyword(kw::Const)?;
+ let name = self.parse_ident()?;
+ self.expect(&token::Colon)?;
+ let typ = self.parse_ty()?;
+ self.expect(&token::Eq)?;
+ let expr = self.parse_expr()?;
+ self.expect(&token::Semi)?;
+ Ok((name, ImplItemKind::Const(typ, expr), Generics::default()))
+ }
+
/// Parses a method or a macro invocation in a trait impl.
fn parse_impl_method(
&mut self,
vis: &Visibility,
at_end: &mut bool
- ) -> PResult<'a, (Ident, Vec<Attribute>, Generics, ast::ImplItemKind)> {
+ ) -> PResult<'a, (Ident, Vec<Attribute>, Generics, ImplItemKind)> {
// FIXME: code copied from `parse_macro_use_or_failure` -- use abstraction!
if let Some(mac) = self.parse_assoc_macro_invoc("impl", Some(vis), at_end)? {
// method macro
/// of a method. The body is not parsed as that differs between `trait`s and `impl`s.
fn parse_method_sig(
&mut self,
- is_name_required: impl Copy + Fn(&token::Token) -> bool,
+ is_name_required: fn(&token::Token) -> bool,
) -> PResult<'a, (Ident, MethodSig, Generics)> {
let header = self.parse_fn_front_matter()?;
- let (ident, mut generics) = self.parse_fn_header()?;
- let decl = self.parse_fn_decl_with_self(is_name_required)?;
- let sig = MethodSig { header, decl };
- generics.where_clause = self.parse_where_clause()?;
- Ok((ident, sig, generics))
+ let (ident, decl, generics) = self.parse_fn_sig(ParamCfg {
+ is_self_allowed: true,
+ allow_c_variadic: false,
+ is_name_required,
+ })?;
+ Ok((ident, MethodSig { header, decl }, generics))
}
/// Parses all the "front matter" for a `fn` declaration, up to
let (constness, unsafety, abi) = if is_const_fn {
(respan(const_span, Constness::Const), unsafety, Abi::Rust)
} else {
- let abi = if self.eat_keyword(kw::Extern) {
- self.parse_opt_abi()?.unwrap_or(Abi::C)
- } else {
- Abi::Rust
- };
+ let abi = self.parse_extern_abi()?;
(respan(self.prev_span, Constness::NotConst), unsafety, abi)
};
if !self.eat_keyword(kw::Fn) {
Ok(FnHeader { constness, unsafety, asyncness, abi })
}
- /// Parses `trait Foo { ... }` or `trait Foo = Bar;`.
- fn parse_item_trait(&mut self, is_auto: IsAuto, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
+ /// Parses `auto? trait Foo { ... }` or `trait Foo = Bar;`.
+ fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
+ // Parse optional `auto` prefix.
+ let is_auto = if self.eat_keyword(kw::Auto) {
+ IsAuto::Yes
+ } else {
+ IsAuto::No
+ };
+
+ self.expect_keyword(kw::Trait)?;
let ident = self.parse_ident()?;
let mut tps = self.parse_generics()?;
Ok(item)
}
- fn parse_trait_item_(&mut self,
- at_end: &mut bool,
- mut attrs: Vec<Attribute>) -> PResult<'a, TraitItem> {
+ fn parse_trait_item_(
+ &mut self,
+ at_end: &mut bool,
+ mut attrs: Vec<Attribute>,
+ ) -> PResult<'a, TraitItem> {
let lo = self.token.span;
self.eat_bad_pub();
let (name, kind, generics) = if self.eat_keyword(kw::Type) {
self.parse_trait_item_assoc_ty()?
} else if self.is_const_item() {
- self.expect_keyword(kw::Const)?;
- let ident = self.parse_ident()?;
- self.expect(&token::Colon)?;
- let ty = self.parse_ty()?;
- let default = if self.eat(&token::Eq) {
- let expr = self.parse_expr()?;
- self.expect(&token::Semi)?;
- Some(expr)
- } else {
- self.expect(&token::Semi)?;
- None
- };
- (ident, TraitItemKind::Const(ty, default), Generics::default())
+ self.parse_trait_item_const()?
} else if let Some(mac) = self.parse_assoc_macro_invoc("trait", None, &mut false)? {
// trait item macro.
- (Ident::invalid(), ast::TraitItemKind::Macro(mac), Generics::default())
+ (Ident::invalid(), TraitItemKind::Macro(mac), Generics::default())
} else {
// This is somewhat dubious; We don't want to allow
// argument names to be left off if there is a definition...
// We don't allow argument names to be left off in edition 2018.
let (ident, sig, generics) = self.parse_method_sig(|t| t.span.rust_2018())?;
let body = self.parse_trait_method_body(at_end, &mut attrs)?;
- (ident, ast::TraitItemKind::Method(sig, body), generics)
+ (ident, TraitItemKind::Method(sig, body), generics)
};
Ok(TraitItem {
})
}
+ fn parse_trait_item_const(&mut self) -> PResult<'a, (Ident, TraitItemKind, Generics)> {
+ self.expect_keyword(kw::Const)?;
+ let ident = self.parse_ident()?;
+ self.expect(&token::Colon)?;
+ let ty = self.parse_ty()?;
+ let default = if self.eat(&token::Eq) {
+ Some(self.parse_expr()?)
+ } else {
+ None
+ };
+ self.expect(&token::Semi)?;
+ Ok((ident, TraitItemKind::Const(ty, default), Generics::default()))
+ }
+
/// Parse the "body" of a method in a trait item definition.
/// This can either be `;` when there's no body,
/// or e.g. a block when the method is a provided one.
/// Parses the following grammar:
///
/// TraitItemAssocTy = Ident ["<"...">"] [":" [GenericBounds]] ["where" ...] ["=" Ty]
- fn parse_trait_item_assoc_ty(&mut self)
- -> PResult<'a, (Ident, TraitItemKind, Generics)> {
+ fn parse_trait_item_assoc_ty(&mut self) -> PResult<'a, (Ident, TraitItemKind, Generics)> {
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
);
}
- if self.eat(&token::BinOp(token::Star)) {
- UseTreeKind::Glob
- } else {
- UseTreeKind::Nested(self.parse_use_tree_list()?)
- }
+ self.parse_use_tree_glob_or_nested()?
} else {
// `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;`
prefix = self.parse_path(PathStyle::Mod)?;
if self.eat(&token::ModSep) {
- if self.eat(&token::BinOp(token::Star)) {
- UseTreeKind::Glob
- } else {
- UseTreeKind::Nested(self.parse_use_tree_list()?)
- }
+ self.parse_use_tree_glob_or_nested()?
} else {
UseTreeKind::Simple(self.parse_rename()?, DUMMY_NODE_ID, DUMMY_NODE_ID)
}
Ok(UseTree { prefix, kind, span: lo.to(self.prev_span) })
}
+ /// Parses `*` or `{...}`.
+ fn parse_use_tree_glob_or_nested(&mut self) -> PResult<'a, UseTreeKind> {
+ Ok(if self.eat(&token::BinOp(token::Star)) {
+ UseTreeKind::Glob
+ } else {
+ UseTreeKind::Nested(self.parse_use_tree_list()?)
+ })
+ }
+
/// Parses a `UseTreeKind::Nested(list)`.
///
/// ```
attrs: Vec<Attribute>,
header: FnHeader,
) -> PResult<'a, Option<P<Item>>> {
- let allow_c_variadic = header.abi == Abi::C && header.unsafety == Unsafety::Unsafe;
- let (ident, decl, generics) = self.parse_fn_sig(allow_c_variadic)?;
+ let (ident, decl, generics) = self.parse_fn_sig(ParamCfg {
+ is_self_allowed: false,
+ allow_c_variadic: header.abi == Abi::C && header.unsafety == Unsafety::Unsafe,
+ is_name_required: |_| true,
+ })?;
let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
- let span = lo.to(self.prev_span);
let kind = ItemKind::Fn(decl, header, generics, body);
- let attrs = maybe_append(attrs, Some(inner_attrs));
- Ok(Some(self.mk_item(span, ident, kind, vis, attrs)))
+ self.mk_item_with_info(attrs, lo, vis, (ident, kind, Some(inner_attrs)))
}
/// Parse the "signature", including the identifier, parameters, and generics of a function.
- fn parse_fn_sig(
- &mut self,
- allow_c_variadic: bool,
- ) -> PResult<'a, (Ident, P<FnDecl>, Generics)> {
- let (ident, mut generics) = self.parse_fn_header()?;
- let decl = self.parse_fn_decl(allow_c_variadic)?;
+ fn parse_fn_sig(&mut self, cfg: ParamCfg) -> PResult<'a, (Ident, P<FnDecl>, Generics)> {
+ let ident = self.parse_ident()?;
+ let mut generics = self.parse_generics()?;
+ let decl = self.parse_fn_decl(cfg, true)?;
generics.where_clause = self.parse_where_clause()?;
Ok((ident, decl, generics))
}
- /// Parses the name and optional generic types of a function header.
- fn parse_fn_header(&mut self) -> PResult<'a, (Ident, Generics)> {
- let id = self.parse_ident()?;
- let generics = self.parse_generics()?;
- Ok((id, generics))
- }
-
/// Parses the parameter list and result type of a function declaration.
- fn parse_fn_decl(&mut self, allow_c_variadic: bool) -> PResult<'a, P<FnDecl>> {
+ pub(super) fn parse_fn_decl(
+ &mut self,
+ cfg: ParamCfg,
+ ret_allow_plus: bool,
+ ) -> PResult<'a, P<FnDecl>> {
Ok(P(FnDecl {
- inputs: self.parse_fn_params(true, allow_c_variadic)?,
- output: self.parse_ret_ty(true)?,
+ inputs: self.parse_fn_params(cfg)?,
+ output: self.parse_ret_ty(ret_allow_plus)?,
}))
}
// Treat `const` as `static` for error recovery, but don't add it to expected tokens.
if self.check_keyword(kw::Static) || self.token.is_keyword(kw::Const) {
if self.token.is_keyword(kw::Const) {
- self.diagnostic()
- .struct_span_err(self.token.span, "extern items cannot be `const`")
- .span_suggestion(
+ let mut err = self
+ .struct_span_err(self.token.span, "extern items cannot be `const`");
+
+
+ // The user wrote 'const fn'
+ if self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe]) {
+ err.emit();
+ // Consume `const`
+ self.bump();
+ // Consume `unsafe` if present, since `extern` blocks
+ // don't allow it. This will leave behind a plain 'fn'
+ self.eat_keyword(kw::Unsafe);
+ // Treat 'const fn` as a plain `fn` for error recovery purposes.
+ // We've already emitted an error, so compilation is guaranteed
+ // to fail
+ return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?);
+ }
+ err.span_suggestion(
self.token.span,
"try using a static value",
"static".to_owned(),
Applicability::MachineApplicable
- ).emit();
+ );
+ err.emit();
}
self.bump(); // `static` or `const`
return Ok(self.parse_item_foreign_static(visibility, lo, attrs)?);
extern_sp: Span,
) -> PResult<'a, ForeignItem> {
self.expect_keyword(kw::Fn)?;
- let (ident, decl, generics) = self.parse_fn_sig(true)?;
+ let (ident, decl, generics) = self.parse_fn_sig(super::ParamCfg {
+ is_self_allowed: false,
+ allow_c_variadic: true,
+ is_name_required: |_| true,
+ })?;
let span = lo.to(self.token.span);
self.parse_semi_or_incorrect_foreign_fn_body(&ident, extern_sp)?;
Ok(ast::ForeignItem {
use crate::ptr::P;
use crate::ast::{self, Attribute, Pat, PatKind, FieldPat, RangeEnd, RangeSyntax, Mac};
use crate::ast::{BindingMode, Ident, Mutability, Path, QSelf, Expr, ExprKind};
-use crate::mut_visit::{noop_visit_pat, MutVisitor};
+use crate::mut_visit::{noop_visit_pat, noop_visit_mac, MutVisitor};
use crate::parse::token::{self};
use crate::print::pprust;
use crate::source_map::{respan, Span, Spanned};
fn make_all_value_bindings_mutable(pat: &mut P<Pat>) -> bool {
struct AddMut(bool);
impl MutVisitor for AddMut {
+ fn visit_mac(&mut self, mac: &mut Mac) {
+ noop_visit_mac(mac, self);
+ }
+
fn visit_pat(&mut self, pat: &mut P<Pat>) {
if let PatKind::Ident(BindingMode::ByValue(ref mut m @ Mutability::Immutable), ..)
= pat.kind
use crate::ptr::P;
use crate::ast::{self, Ty, TyKind, MutTy, BareFnTy, FunctionRetTy, GenericParam, Lifetime, Ident};
use crate::ast::{TraitBoundModifier, TraitObjectSyntax, GenericBound, GenericBounds, PolyTraitRef};
-use crate::ast::{Mutability, AnonConst, FnDecl, Mac};
+use crate::ast::{Mutability, AnonConst, Mac};
use crate::parse::token::{self, Token};
use crate::source_map::Span;
use crate::symbol::{kw};
-use rustc_target::spec::abi::Abi;
-
use errors::{Applicability, pluralise};
/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
*/
let unsafety = self.parse_unsafety();
- let abi = if self.eat_keyword(kw::Extern) {
- self.parse_opt_abi()?.unwrap_or(Abi::C)
- } else {
- Abi::Rust
- };
-
+ let abi = self.parse_extern_abi()?;
self.expect_keyword(kw::Fn)?;
- let inputs = self.parse_fn_params(false, true)?;
- let ret_ty = self.parse_ret_ty(false)?;
- let decl = P(FnDecl {
- inputs,
- output: ret_ty,
- });
+ let cfg = super::ParamCfg {
+ is_self_allowed: false,
+ allow_c_variadic: true,
+ is_name_required: |_| false,
+ };
+ let decl = self.parse_fn_decl(cfg, false)?;
Ok(TyKind::BareFn(P(BareFnTy {
abi,
unsafety,
0 => TokenStream::empty(),
1 => streams.pop().unwrap(),
_ => {
- // rust-lang/rust#57735: pre-allocate vector to avoid
- // quadratic blow-up due to on-the-fly reallocations.
- let tree_count = streams.iter()
- .map(|ts| match &ts.0 { None => 0, Some(s) => s.len() })
+ // We are going to extend the first stream in `streams` with
+ // the elements from the subsequent streams. This requires
+ // using `make_mut()` on the first stream, and in practice this
+ // doesn't cause cloning 99.9% of the time.
+ //
+ // One very common use case is when `streams` has two elements,
+ // where the first stream has any number of elements within
+ // (often 1, but sometimes many more) and the second stream has
+ // a single element within.
+
+ // Determine how much the first stream will be extended.
+ // Needed to avoid quadratic blow up from on-the-fly
+ // reallocations (#57735).
+ let num_appends = streams.iter()
+ .skip(1)
+ .map(|ts| ts.len())
.sum();
- let mut vec = Vec::with_capacity(tree_count);
- for stream in streams {
- match stream.0 {
- None => {},
- Some(stream2) => vec.extend(stream2.iter().cloned()),
+ // Get the first stream. If it's `None`, create an empty
+ // stream.
+ let mut iter = streams.drain();
+ let mut first_stream_lrc = match iter.next().unwrap().0 {
+ Some(first_stream_lrc) => first_stream_lrc,
+ None => Lrc::new(vec![]),
+ };
+
+ // Append the elements to the first stream, after reserving
+ // space for them.
+ let first_vec_mut = Lrc::make_mut(&mut first_stream_lrc);
+ first_vec_mut.reserve(num_appends);
+ for stream in iter {
+ if let Some(stream) = stream.0 {
+ first_vec_mut.extend(stream.iter().cloned());
}
}
- TokenStream::new(vec)
+
+ // Create the final `TokenStream`.
+ match first_vec_mut.len() {
+ 0 => TokenStream(None),
+ _ => TokenStream(Some(first_stream_lrc)),
+ }
}
}
}
.collect())
}))
}
-
- fn first_tree_and_joint(&self) -> Option<TreeAndJoint> {
- self.0.as_ref().map(|stream| {
- stream.first().unwrap().clone()
- })
- }
-
- fn last_tree_if_joint(&self) -> Option<TokenTree> {
- match self.0 {
- None => None,
- Some(ref stream) => {
- if let (tree, Joint) = stream.last().unwrap() {
- Some(tree.clone())
- } else {
- None
- }
- }
- }
- }
}
// 99.5%+ of the time we have 1 or 2 elements in this vector.
}
pub fn push<T: Into<TokenStream>>(&mut self, stream: T) {
- let stream = stream.into();
- let last_tree_if_joint = self.0.last().and_then(TokenStream::last_tree_if_joint);
- if let Some(TokenTree::Token(last_token)) = last_tree_if_joint {
- if let Some((TokenTree::Token(token), is_joint)) = stream.first_tree_and_joint() {
- if let Some(glued_tok) = last_token.glue(&token) {
- let last_stream = self.0.pop().unwrap();
- self.push_all_but_last_tree(&last_stream);
- let glued_tt = TokenTree::Token(glued_tok);
- let glued_tokenstream = TokenStream::new(vec![(glued_tt, is_joint)]);
- self.0.push(glued_tokenstream);
- self.push_all_but_first_tree(&stream);
- return
+ let mut stream = stream.into();
+
+ // If `self` is not empty and the last tree within the last stream is a
+ // token tree marked with `Joint`...
+ if let Some(TokenStream(Some(ref mut last_stream_lrc))) = self.0.last_mut() {
+ if let Some((TokenTree::Token(last_token), Joint)) = last_stream_lrc.last() {
+
+ // ...and `stream` is not empty and the first tree within it is
+ // a token tree...
+ if let TokenStream(Some(ref mut stream_lrc)) = stream {
+ if let Some((TokenTree::Token(token), is_joint)) = stream_lrc.first() {
+
+ // ...and the two tokens can be glued together...
+ if let Some(glued_tok) = last_token.glue(&token) {
+
+ // ...then do so, by overwriting the last token
+ // tree in `self` and removing the first token tree
+ // from `stream`. This requires using `make_mut()`
+ // on the last stream in `self` and on `stream`,
+ // and in practice this doesn't cause cloning 99.9%
+ // of the time.
+
+ // Overwrite the last token tree with the merged
+ // token.
+ let last_vec_mut = Lrc::make_mut(last_stream_lrc);
+ *last_vec_mut.last_mut().unwrap() =
+ (TokenTree::Token(glued_tok), *is_joint);
+
+ // Remove the first token tree from `stream`. (This
+ // is almost always the only tree in `stream`.)
+ let stream_vec_mut = Lrc::make_mut(stream_lrc);
+ stream_vec_mut.remove(0);
+
+ // Don't push `stream` if it's empty -- that could
+ // block subsequent token gluing, by getting
+ // between two token trees that should be glued
+ // together.
+ if !stream.is_empty() {
+ self.0.push(stream);
+ }
+ return;
+ }
+ }
}
}
}
pub fn build(self) -> TokenStream {
TokenStream::from_streams(self.0)
}
-
- fn push_all_but_last_tree(&mut self, stream: &TokenStream) {
- if let Some(ref streams) = stream.0 {
- let len = streams.len();
- match len {
- 1 => {}
- _ => self.0.push(TokenStream(Some(Lrc::new(streams[0 .. len - 1].to_vec())))),
- }
- }
- }
-
- fn push_all_but_first_tree(&mut self, stream: &TokenStream) {
- if let Some(ref streams) = stream.0 {
- let len = streams.len();
- match len {
- 1 => {}
- _ => self.0.push(TokenStream(Some(Lrc::new(streams[1 .. len].to_vec())))),
- }
- }
- }
}
#[derive(Clone)]
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::tokenstream::TokenStream;
+use syntax::early_buffered_lints::BufferedEarlyLintId;
use smallvec::SmallVec;
use syntax_pos::{self, Pos, Span};
}
impl<'a> base::MacResult for ExpandResult<'a> {
fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
- Some(panictry!(self.p.parse_expr()))
+ let r = panictry!(self.p.parse_expr());
+ if self.p.token != token::Eof {
+ self.p.sess.buffer_lint(
+ BufferedEarlyLintId::IncompleteInclude,
+ self.p.token.span,
+ ast::CRATE_NODE_ID,
+ "include macro expected single expression in source",
+ );
+ }
+ Some(r)
}
fn make_items(mut self: Box<ExpandResult<'a>>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
console,
const_compare_raw_pointers,
const_constructor,
+ const_extern_fn,
const_fn,
const_fn_union,
const_generics,
link_cfg,
link_llvm_intrinsics,
link_name,
+ link_ordinal,
link_section,
LintPass,
lint_reasons,
RangeInclusive,
RangeTo,
RangeToInclusive,
+ raw_dylib,
raw_identifiers,
Ready,
reason,
tool_attributes,
tool_lints,
trace_macros,
+ track_caller,
trait_alias,
transmute,
transparent,
) {
let TestDescAndFn { desc, testfn } = test;
- // FIXME: Re-enable emscripten once it can catch panics again
- let ignore_because_no_process_support = desc.should_panic != ShouldPanic::No
- && (cfg!(target_arch = "wasm32") || cfg!(target_os = "emscripten"));
+ let ignore_because_no_process_support = cfg!(target_arch = "wasm32")
+ && !cfg!(target_os = "emscripten")
+ && desc.should_panic != ShouldPanic::No;
if force_ignore || desc.ignore || ignore_because_no_process_support {
monitor_ch.send((desc, TrIgnored, None, Vec::new())).unwrap();
use crate::test::{
filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy,
- ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts,
+ ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TrFailedMsg,
TrIgnored, TrOk,
};
use std::sync::mpsc::channel;
assert!(res == TrIgnored);
}
-// FIXME: Re-enable emscripten once it can catch panics again
#[test]
-#[cfg(not(target_os = "emscripten"))]
fn test_should_panic() {
fn f() {
panic!();
assert!(res == TrOk);
}
-// FIXME: Re-enable emscripten once it can catch panics again
#[test]
-#[cfg(not(target_os = "emscripten"))]
fn test_should_panic_good_message() {
fn f() {
panic!("an error message");
assert!(res == TrOk);
}
-// FIXME: Re-enable emscripten once it can catch panics again
#[test]
-#[cfg(not(target_os = "emscripten"))]
fn test_should_panic_bad_message() {
- use crate::tests::TrFailedMsg;
fn f() {
panic!("an error message");
}
assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
}
-// FIXME: Re-enable emscripten once it can catch panics again
#[test]
-#[cfg(not(target_os = "emscripten"))]
fn test_should_panic_but_succeeds() {
fn f() {}
let desc = TestDescAndFn {
-// ignore-emscripten compiled with panic=abort by default
// compile-flags: -C no-prepopulate-passes
// ignore-tidy-linelength
-// ignore-emscripten compiled with panic=abort by default
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
-// ignore-emscripten compiled with panic=abort by default
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
-// ignore-emscripten default visibility is hidden
// compile-flags: -O
// `#[no_mangle]`d static variables always have external linkage, i.e., no `internal` in their
// definitions
-// ignore-emscripten default visibility is hidden
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
// compile-flags: -O
+// ignore-asmjs
+
#![feature(asm)]
#![crate_type = "lib"]
// ignore-msvc
-// ignore-emscripten compiled with panic=abort by default
// compile-flags: -O -C no-prepopulate-passes
// compile-flags: -C no-prepopulate-passes
// ignore-aarch64
-// ignore-emscripten
+// ignore-asmjs
// ignore-mips64
// ignore-powerpc
// ignore-powerpc64
// ignore-s390x
// ignore-sparc
// ignore-sparc64
+// ignore-wasm
// ignore-x86
// ignore-x86_64
// See repr-transparent.rs
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
// min-llvm-version 7.0
// compile-flags: -C no-prepopulate-passes
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
+// ignore-emscripten
+
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
// CHECK-LABEL: @sadd_i8x2
#[no_mangle]
pub unsafe fn sadd_i8x2(x: i8x2, y: i8x2) -> i8x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %{{[0-9a-z]+}}, <2 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.sadd.sat.v2i8(<2 x i8> %{{[0-9]+}}, <2 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i8x4
#[no_mangle]
pub unsafe fn sadd_i8x4(x: i8x4, y: i8x4) -> i8x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.sadd.sat.v4i8(<4 x i8> %{{[0-9a-z]+}}, <4 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.sadd.sat.v4i8(<4 x i8> %{{[0-9]+}}, <4 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i8x8
#[no_mangle]
pub unsafe fn sadd_i8x8(x: i8x8, y: i8x8) -> i8x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.sadd.sat.v8i8(<8 x i8> %{{[0-9a-z]+}}, <8 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.sadd.sat.v8i8(<8 x i8> %{{[0-9]+}}, <8 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i8x16
#[no_mangle]
pub unsafe fn sadd_i8x16(x: i8x16, y: i8x16) -> i8x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.sadd.sat.v16i8(<16 x i8> %{{[0-9a-z]+}}, <16 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.sadd.sat.v16i8(<16 x i8> %{{[0-9]+}}, <16 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i8x32
#[no_mangle]
pub unsafe fn sadd_i8x32(x: i8x32, y: i8x32) -> i8x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.sadd.sat.v32i8(<32 x i8> %{{[0-9a-z]+}}, <32 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.sadd.sat.v32i8(<32 x i8> %{{[0-9]+}}, <32 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i8x64
#[no_mangle]
pub unsafe fn sadd_i8x64(x: i8x64, y: i8x64) -> i8x64 {
- // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.sadd.sat.v64i8(<64 x i8> %{{[0-9a-z]+}}, <64 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.sadd.sat.v64i8(<64 x i8> %{{[0-9]+}}, <64 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i16x2
#[no_mangle]
pub unsafe fn sadd_i16x2(x: i16x2, y: i16x2) -> i16x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.sadd.sat.v2i16(<2 x i16> %{{[0-9a-z]+}}, <2 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.sadd.sat.v2i16(<2 x i16> %{{[0-9]+}}, <2 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i16x4
#[no_mangle]
pub unsafe fn sadd_i16x4(x: i16x4, y: i16x4) -> i16x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.sadd.sat.v4i16(<4 x i16> %{{[0-9a-z]+}}, <4 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.sadd.sat.v4i16(<4 x i16> %{{[0-9]+}}, <4 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i16x8
#[no_mangle]
pub unsafe fn sadd_i16x8(x: i16x8, y: i16x8) -> i16x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.sadd.sat.v8i16(<8 x i16> %{{[0-9a-z]+}}, <8 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.sadd.sat.v8i16(<8 x i16> %{{[0-9]+}}, <8 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i16x16
#[no_mangle]
pub unsafe fn sadd_i16x16(x: i16x16, y: i16x16) -> i16x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.sadd.sat.v16i16(<16 x i16> %{{[0-9a-z]+}}, <16 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.sadd.sat.v16i16(<16 x i16> %{{[0-9]+}}, <16 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i16x32
#[no_mangle]
pub unsafe fn sadd_i16x32(x: i16x32, y: i16x32) -> i16x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.sadd.sat.v32i16(<32 x i16> %{{[0-9a-z]+}}, <32 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.sadd.sat.v32i16(<32 x i16> %{{[0-9]+}}, <32 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i32x2
#[no_mangle]
pub unsafe fn sadd_i32x2(x: i32x2, y: i32x2) -> i32x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.sadd.sat.v2i32(<2 x i32> %{{[0-9a-z]+}}, <2 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.sadd.sat.v2i32(<2 x i32> %{{[0-9]+}}, <2 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i32x4
#[no_mangle]
pub unsafe fn sadd_i32x4(x: i32x4, y: i32x4) -> i32x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %{{[0-9a-z]+}}, <4 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.sadd.sat.v4i32(<4 x i32> %{{[0-9]+}}, <4 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i32x8
#[no_mangle]
pub unsafe fn sadd_i32x8(x: i32x8, y: i32x8) -> i32x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.sadd.sat.v8i32(<8 x i32> %{{[0-9a-z]+}}, <8 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.sadd.sat.v8i32(<8 x i32> %{{[0-9]+}}, <8 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i32x16
#[no_mangle]
pub unsafe fn sadd_i32x16(x: i32x16, y: i32x16) -> i32x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.sadd.sat.v16i32(<16 x i32> %{{[0-9a-z]+}}, <16 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.sadd.sat.v16i32(<16 x i32> %{{[0-9]+}}, <16 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i64x2
#[no_mangle]
pub unsafe fn sadd_i64x2(x: i64x2, y: i64x2) -> i64x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.sadd.sat.v2i64(<2 x i64> %{{[0-9a-z]+}}, <2 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.sadd.sat.v2i64(<2 x i64> %{{[0-9]+}}, <2 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i64x4
#[no_mangle]
pub unsafe fn sadd_i64x4(x: i64x4, y: i64x4) -> i64x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.sadd.sat.v4i64(<4 x i64> %{{[0-9a-z]+}}, <4 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.sadd.sat.v4i64(<4 x i64> %{{[0-9]+}}, <4 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i64x8
#[no_mangle]
pub unsafe fn sadd_i64x8(x: i64x8, y: i64x8) -> i64x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.sadd.sat.v8i64(<8 x i64> %{{[0-9a-z]+}}, <8 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.sadd.sat.v8i64(<8 x i64> %{{[0-9]+}}, <8 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i128x2
#[no_mangle]
pub unsafe fn sadd_i128x2(x: i128x2, y: i128x2) -> i128x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.sadd.sat.v2i128(<2 x i128> %{{[0-9a-z]+}}, <2 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.sadd.sat.v2i128(<2 x i128> %{{[0-9]+}}, <2 x i128> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @sadd_i128x4
#[no_mangle]
pub unsafe fn sadd_i128x4(x: i128x4, y: i128x4) -> i128x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.sadd.sat.v4i128(<4 x i128> %{{[0-9a-z]+}}, <4 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.sadd.sat.v4i128(<4 x i128> %{{[0-9]+}}, <4 x i128> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x2
#[no_mangle]
pub unsafe fn uadd_u8x2(x: u8x2, y: u8x2) -> u8x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %{{[0-9a-z]+}}, <2 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %{{[0-9]+}}, <2 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x4
#[no_mangle]
pub unsafe fn uadd_u8x4(x: u8x4, y: u8x4) -> u8x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.uadd.sat.v4i8(<4 x i8> %{{[0-9a-z]+}}, <4 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.uadd.sat.v4i8(<4 x i8> %{{[0-9]+}}, <4 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x8
#[no_mangle]
pub unsafe fn uadd_u8x8(x: u8x8, y: u8x8) -> u8x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.uadd.sat.v8i8(<8 x i8> %{{[0-9a-z]+}}, <8 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.uadd.sat.v8i8(<8 x i8> %{{[0-9]+}}, <8 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x16
#[no_mangle]
pub unsafe fn uadd_u8x16(x: u8x16, y: u8x16) -> u8x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.uadd.sat.v16i8(<16 x i8> %{{[0-9a-z]+}}, <16 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.uadd.sat.v16i8(<16 x i8> %{{[0-9]+}}, <16 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x32
#[no_mangle]
pub unsafe fn uadd_u8x32(x: u8x32, y: u8x32) -> u8x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.uadd.sat.v32i8(<32 x i8> %{{[0-9a-z]+}}, <32 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.uadd.sat.v32i8(<32 x i8> %{{[0-9]+}}, <32 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u8x64
#[no_mangle]
pub unsafe fn uadd_u8x64(x: u8x64, y: u8x64) -> u8x64 {
- // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.uadd.sat.v64i8(<64 x i8> %{{[0-9a-z]+}}, <64 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.uadd.sat.v64i8(<64 x i8> %{{[0-9]+}}, <64 x i8> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u16x2
#[no_mangle]
pub unsafe fn uadd_u16x2(x: u16x2, y: u16x2) -> u16x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.uadd.sat.v2i16(<2 x i16> %{{[0-9a-z]+}}, <2 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.uadd.sat.v2i16(<2 x i16> %{{[0-9]+}}, <2 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u16x4
#[no_mangle]
pub unsafe fn uadd_u16x4(x: u16x4, y: u16x4) -> u16x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.uadd.sat.v4i16(<4 x i16> %{{[0-9a-z]+}}, <4 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.uadd.sat.v4i16(<4 x i16> %{{[0-9]+}}, <4 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u16x8
#[no_mangle]
pub unsafe fn uadd_u16x8(x: u16x8, y: u16x8) -> u16x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.uadd.sat.v8i16(<8 x i16> %{{[0-9a-z]+}}, <8 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.uadd.sat.v8i16(<8 x i16> %{{[0-9]+}}, <8 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u16x16
#[no_mangle]
pub unsafe fn uadd_u16x16(x: u16x16, y: u16x16) -> u16x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.uadd.sat.v16i16(<16 x i16> %{{[0-9a-z]+}}, <16 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.uadd.sat.v16i16(<16 x i16> %{{[0-9]+}}, <16 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u16x32
#[no_mangle]
pub unsafe fn uadd_u16x32(x: u16x32, y: u16x32) -> u16x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.uadd.sat.v32i16(<32 x i16> %{{[0-9a-z]+}}, <32 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.uadd.sat.v32i16(<32 x i16> %{{[0-9]+}}, <32 x i16> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u32x2
#[no_mangle]
pub unsafe fn uadd_u32x2(x: u32x2, y: u32x2) -> u32x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.uadd.sat.v2i32(<2 x i32> %{{[0-9a-z]+}}, <2 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.uadd.sat.v2i32(<2 x i32> %{{[0-9]+}}, <2 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u32x4
#[no_mangle]
pub unsafe fn uadd_u32x4(x: u32x4, y: u32x4) -> u32x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %{{[0-9a-z]+}}, <4 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.uadd.sat.v4i32(<4 x i32> %{{[0-9]+}}, <4 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u32x8
#[no_mangle]
pub unsafe fn uadd_u32x8(x: u32x8, y: u32x8) -> u32x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.uadd.sat.v8i32(<8 x i32> %{{[0-9a-z]+}}, <8 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.uadd.sat.v8i32(<8 x i32> %{{[0-9]+}}, <8 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u32x16
#[no_mangle]
pub unsafe fn uadd_u32x16(x: u32x16, y: u32x16) -> u32x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.uadd.sat.v16i32(<16 x i32> %{{[0-9a-z]+}}, <16 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.uadd.sat.v16i32(<16 x i32> %{{[0-9]+}}, <16 x i32> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u64x2
#[no_mangle]
pub unsafe fn uadd_u64x2(x: u64x2, y: u64x2) -> u64x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64> %{{[0-9a-z]+}}, <2 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64> %{{[0-9]+}}, <2 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u64x4
#[no_mangle]
pub unsafe fn uadd_u64x4(x: u64x4, y: u64x4) -> u64x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.uadd.sat.v4i64(<4 x i64> %{{[0-9a-z]+}}, <4 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.uadd.sat.v4i64(<4 x i64> %{{[0-9]+}}, <4 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u64x8
#[no_mangle]
pub unsafe fn uadd_u64x8(x: u64x8, y: u64x8) -> u64x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.uadd.sat.v8i64(<8 x i64> %{{[0-9a-z]+}}, <8 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.uadd.sat.v8i64(<8 x i64> %{{[0-9]+}}, <8 x i64> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u128x2
#[no_mangle]
pub unsafe fn uadd_u128x2(x: u128x2, y: u128x2) -> u128x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.uadd.sat.v2i128(<2 x i128> %{{[0-9a-z]+}}, <2 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.uadd.sat.v2i128(<2 x i128> %{{[0-9]+}}, <2 x i128> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @uadd_u128x4
#[no_mangle]
pub unsafe fn uadd_u128x4(x: u128x4, y: u128x4) -> u128x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.uadd.sat.v4i128(<4 x i128> %{{[0-9a-z]+}}, <4 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.uadd.sat.v4i128(<4 x i128> %{{[0-9]+}}, <4 x i128> %{{[0-9]+}})
simd_saturating_add(x, y)
}
// CHECK-LABEL: @ssub_i8x2
#[no_mangle]
pub unsafe fn ssub_i8x2(x: i8x2, y: i8x2) -> i8x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %{{[0-9a-z]+}}, <2 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.ssub.sat.v2i8(<2 x i8> %{{[0-9]+}}, <2 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i8x4
#[no_mangle]
pub unsafe fn ssub_i8x4(x: i8x4, y: i8x4) -> i8x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.ssub.sat.v4i8(<4 x i8> %{{[0-9a-z]+}}, <4 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.ssub.sat.v4i8(<4 x i8> %{{[0-9]+}}, <4 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i8x8
#[no_mangle]
pub unsafe fn ssub_i8x8(x: i8x8, y: i8x8) -> i8x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.ssub.sat.v8i8(<8 x i8> %{{[0-9a-z]+}}, <8 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.ssub.sat.v8i8(<8 x i8> %{{[0-9]+}}, <8 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i8x16
#[no_mangle]
pub unsafe fn ssub_i8x16(x: i8x16, y: i8x16) -> i8x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.ssub.sat.v16i8(<16 x i8> %{{[0-9a-z]+}}, <16 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.ssub.sat.v16i8(<16 x i8> %{{[0-9]+}}, <16 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i8x32
#[no_mangle]
pub unsafe fn ssub_i8x32(x: i8x32, y: i8x32) -> i8x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.ssub.sat.v32i8(<32 x i8> %{{[0-9a-z]+}}, <32 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.ssub.sat.v32i8(<32 x i8> %{{[0-9]+}}, <32 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i8x64
#[no_mangle]
pub unsafe fn ssub_i8x64(x: i8x64, y: i8x64) -> i8x64 {
- // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.ssub.sat.v64i8(<64 x i8> %{{[0-9a-z]+}}, <64 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.ssub.sat.v64i8(<64 x i8> %{{[0-9]+}}, <64 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i16x2
#[no_mangle]
pub unsafe fn ssub_i16x2(x: i16x2, y: i16x2) -> i16x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.ssub.sat.v2i16(<2 x i16> %{{[0-9a-z]+}}, <2 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.ssub.sat.v2i16(<2 x i16> %{{[0-9]+}}, <2 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i16x4
#[no_mangle]
pub unsafe fn ssub_i16x4(x: i16x4, y: i16x4) -> i16x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.ssub.sat.v4i16(<4 x i16> %{{[0-9a-z]+}}, <4 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.ssub.sat.v4i16(<4 x i16> %{{[0-9]+}}, <4 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i16x8
#[no_mangle]
pub unsafe fn ssub_i16x8(x: i16x8, y: i16x8) -> i16x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> %{{[0-9a-z]+}}, <8 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.ssub.sat.v8i16(<8 x i16> %{{[0-9]+}}, <8 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i16x16
#[no_mangle]
pub unsafe fn ssub_i16x16(x: i16x16, y: i16x16) -> i16x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.ssub.sat.v16i16(<16 x i16> %{{[0-9a-z]+}}, <16 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.ssub.sat.v16i16(<16 x i16> %{{[0-9]+}}, <16 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i16x32
#[no_mangle]
pub unsafe fn ssub_i16x32(x: i16x32, y: i16x32) -> i16x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.ssub.sat.v32i16(<32 x i16> %{{[0-9a-z]+}}, <32 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.ssub.sat.v32i16(<32 x i16> %{{[0-9]+}}, <32 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i32x2
#[no_mangle]
pub unsafe fn ssub_i32x2(x: i32x2, y: i32x2) -> i32x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.ssub.sat.v2i32(<2 x i32> %{{[0-9a-z]+}}, <2 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.ssub.sat.v2i32(<2 x i32> %{{[0-9]+}}, <2 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i32x4
#[no_mangle]
pub unsafe fn ssub_i32x4(x: i32x4, y: i32x4) -> i32x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %{{[0-9a-z]+}}, <4 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.ssub.sat.v4i32(<4 x i32> %{{[0-9]+}}, <4 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i32x8
#[no_mangle]
pub unsafe fn ssub_i32x8(x: i32x8, y: i32x8) -> i32x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.ssub.sat.v8i32(<8 x i32> %{{[0-9a-z]+}}, <8 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.ssub.sat.v8i32(<8 x i32> %{{[0-9]+}}, <8 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i32x16
#[no_mangle]
pub unsafe fn ssub_i32x16(x: i32x16, y: i32x16) -> i32x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.ssub.sat.v16i32(<16 x i32> %{{[0-9a-z]+}}, <16 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.ssub.sat.v16i32(<16 x i32> %{{[0-9]+}}, <16 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i64x2
#[no_mangle]
pub unsafe fn ssub_i64x2(x: i64x2, y: i64x2) -> i64x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.ssub.sat.v2i64(<2 x i64> %{{[0-9a-z]+}}, <2 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.ssub.sat.v2i64(<2 x i64> %{{[0-9]+}}, <2 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i64x4
#[no_mangle]
pub unsafe fn ssub_i64x4(x: i64x4, y: i64x4) -> i64x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.ssub.sat.v4i64(<4 x i64> %{{[0-9a-z]+}}, <4 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.ssub.sat.v4i64(<4 x i64> %{{[0-9]+}}, <4 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i64x8
#[no_mangle]
pub unsafe fn ssub_i64x8(x: i64x8, y: i64x8) -> i64x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.ssub.sat.v8i64(<8 x i64> %{{[0-9a-z]+}}, <8 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.ssub.sat.v8i64(<8 x i64> %{{[0-9]+}}, <8 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i128x2
#[no_mangle]
pub unsafe fn ssub_i128x2(x: i128x2, y: i128x2) -> i128x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.ssub.sat.v2i128(<2 x i128> %{{[0-9a-z]+}}, <2 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.ssub.sat.v2i128(<2 x i128> %{{[0-9]+}}, <2 x i128> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @ssub_i128x4
#[no_mangle]
pub unsafe fn ssub_i128x4(x: i128x4, y: i128x4) -> i128x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.ssub.sat.v4i128(<4 x i128> %{{[0-9a-z]+}}, <4 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.ssub.sat.v4i128(<4 x i128> %{{[0-9]+}}, <4 x i128> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x2
#[no_mangle]
pub unsafe fn usub_u8x2(x: u8x2, y: u8x2) -> u8x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %{{[0-9a-z]+}}, <2 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> %{{[0-9]+}}, <2 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x4
#[no_mangle]
pub unsafe fn usub_u8x4(x: u8x4, y: u8x4) -> u8x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.usub.sat.v4i8(<4 x i8> %{{[0-9a-z]+}}, <4 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i8> @llvm.usub.sat.v4i8(<4 x i8> %{{[0-9]+}}, <4 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x8
#[no_mangle]
pub unsafe fn usub_u8x8(x: u8x8, y: u8x8) -> u8x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.usub.sat.v8i8(<8 x i8> %{{[0-9a-z]+}}, <8 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i8> @llvm.usub.sat.v8i8(<8 x i8> %{{[0-9]+}}, <8 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x16
#[no_mangle]
pub unsafe fn usub_u8x16(x: u8x16, y: u8x16) -> u8x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.usub.sat.v16i8(<16 x i8> %{{[0-9a-z]+}}, <16 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i8> @llvm.usub.sat.v16i8(<16 x i8> %{{[0-9]+}}, <16 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x32
#[no_mangle]
pub unsafe fn usub_u8x32(x: u8x32, y: u8x32) -> u8x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.usub.sat.v32i8(<32 x i8> %{{[0-9a-z]+}}, <32 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i8> @llvm.usub.sat.v32i8(<32 x i8> %{{[0-9]+}}, <32 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u8x64
#[no_mangle]
pub unsafe fn usub_u8x64(x: u8x64, y: u8x64) -> u8x64 {
- // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.usub.sat.v64i8(<64 x i8> %{{[0-9a-z]+}}, <64 x i8> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <64 x i8> @llvm.usub.sat.v64i8(<64 x i8> %{{[0-9]+}}, <64 x i8> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u16x2
#[no_mangle]
pub unsafe fn usub_u16x2(x: u16x2, y: u16x2) -> u16x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> %{{[0-9a-z]+}}, <2 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i16> @llvm.usub.sat.v2i16(<2 x i16> %{{[0-9]+}}, <2 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u16x4
#[no_mangle]
pub unsafe fn usub_u16x4(x: u16x4, y: u16x4) -> u16x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.usub.sat.v4i16(<4 x i16> %{{[0-9a-z]+}}, <4 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i16> @llvm.usub.sat.v4i16(<4 x i16> %{{[0-9]+}}, <4 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u16x8
#[no_mangle]
pub unsafe fn usub_u16x8(x: u16x8, y: u16x8) -> u16x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.usub.sat.v8i16(<8 x i16> %{{[0-9a-z]+}}, <8 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i16> @llvm.usub.sat.v8i16(<8 x i16> %{{[0-9]+}}, <8 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u16x16
#[no_mangle]
pub unsafe fn usub_u16x16(x: u16x16, y: u16x16) -> u16x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.usub.sat.v16i16(<16 x i16> %{{[0-9a-z]+}}, <16 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i16> @llvm.usub.sat.v16i16(<16 x i16> %{{[0-9]+}}, <16 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u16x32
#[no_mangle]
pub unsafe fn usub_u16x32(x: u16x32, y: u16x32) -> u16x32 {
- // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.usub.sat.v32i16(<32 x i16> %{{[0-9a-z]+}}, <32 x i16> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <32 x i16> @llvm.usub.sat.v32i16(<32 x i16> %{{[0-9]+}}, <32 x i16> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u32x2
#[no_mangle]
pub unsafe fn usub_u32x2(x: u32x2, y: u32x2) -> u32x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> %{{[0-9a-z]+}}, <2 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i32> @llvm.usub.sat.v2i32(<2 x i32> %{{[0-9]+}}, <2 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u32x4
#[no_mangle]
pub unsafe fn usub_u32x4(x: u32x4, y: u32x4) -> u32x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %{{[0-9a-z]+}}, <4 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i32> @llvm.usub.sat.v4i32(<4 x i32> %{{[0-9]+}}, <4 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u32x8
#[no_mangle]
pub unsafe fn usub_u32x8(x: u32x8, y: u32x8) -> u32x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.usub.sat.v8i32(<8 x i32> %{{[0-9a-z]+}}, <8 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i32> @llvm.usub.sat.v8i32(<8 x i32> %{{[0-9]+}}, <8 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u32x16
#[no_mangle]
pub unsafe fn usub_u32x16(x: u32x16, y: u32x16) -> u32x16 {
- // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.usub.sat.v16i32(<16 x i32> %{{[0-9a-z]+}}, <16 x i32> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <16 x i32> @llvm.usub.sat.v16i32(<16 x i32> %{{[0-9]+}}, <16 x i32> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u64x2
#[no_mangle]
pub unsafe fn usub_u64x2(x: u64x2, y: u64x2) -> u64x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> %{{[0-9a-z]+}}, <2 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i64> @llvm.usub.sat.v2i64(<2 x i64> %{{[0-9]+}}, <2 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u64x4
#[no_mangle]
pub unsafe fn usub_u64x4(x: u64x4, y: u64x4) -> u64x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.usub.sat.v4i64(<4 x i64> %{{[0-9a-z]+}}, <4 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i64> @llvm.usub.sat.v4i64(<4 x i64> %{{[0-9]+}}, <4 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u64x8
#[no_mangle]
pub unsafe fn usub_u64x8(x: u64x8, y: u64x8) -> u64x8 {
- // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.usub.sat.v8i64(<8 x i64> %{{[0-9a-z]+}}, <8 x i64> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <8 x i64> @llvm.usub.sat.v8i64(<8 x i64> %{{[0-9]+}}, <8 x i64> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u128x2
#[no_mangle]
pub unsafe fn usub_u128x2(x: u128x2, y: u128x2) -> u128x2 {
- // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.usub.sat.v2i128(<2 x i128> %{{[0-9a-z]+}}, <2 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <2 x i128> @llvm.usub.sat.v2i128(<2 x i128> %{{[0-9]+}}, <2 x i128> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @usub_u128x4
#[no_mangle]
pub unsafe fn usub_u128x4(x: u128x4, y: u128x4) -> u128x4 {
- // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.usub.sat.v4i128(<4 x i128> %{{[0-9a-z]+}}, <4 x i128> %{{[0-9a-z]+}})
+ // CHECK: %{{[0-9]+}} = call <4 x i128> @llvm.usub.sat.v4i128(<4 x i128> %{{[0-9]+}}, <4 x i128> %{{[0-9]+}})
simd_saturating_sub(x, y)
}
// CHECK-LABEL: @bitmask_int
#[no_mangle]
pub unsafe fn bitmask_int(x: i32x2) -> u8 {
- // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9a-z]+}}, <i32 31, i32 31>
+ // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9]+}}, <i32 31, i32 31>
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
// CHECK-LABEL: @bitmask_uint
#[no_mangle]
pub unsafe fn bitmask_uint(x: u32x2) -> u8 {
- // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9a-z]+}}, <i32 31, i32 31>
+ // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{[0-9]+}}, <i32 31, i32 31>
// CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1>
// CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2
// CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8
// CHECK-LABEL: @bitmask_int16
#[no_mangle]
pub unsafe fn bitmask_int16(x: i8x16) -> u16 {
- // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{[0-9a-z]+}}, <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>
+ // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{[0-9]+}}, <i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7, i8 7>
// CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1>
// CHECK: %{{[0-9]+}} = bitcast <16 x i1> [[B]] to i16
// CHECK-NOT: zext
+// ignore-emscripten
// ignore-tidy-linelength
// compile-flags: -C no-prepopulate-passes
+// ignore-emscripten
// ignore-tidy-linelength
// compile-flags: -C no-prepopulate-passes
-// ignore-emscripten vectors passed directly
// compile-flags: -C no-prepopulate-passes
// This test that using union forward the abi of the inner type, as
// aux-build:weak-lang-items.rs
// error-pattern: `#[panic_handler]` function required, but not found
// error-pattern: language item required, but not found: `eh_personality`
-// ignore-emscripten compiled with panic=abort, personality not required
+// ignore-wasm32-bare compiled with panic=abort, personality not required
#![no_std]
// Test that we detect changes to the `dep_kind` query. If the change is not
// detected then -Zincremental-verify-ich will trigger an assertion.
-// ignore-emscripten compiled with panic=abort by default
// revisions:cfail1 cfail2
// compile-flags: -Z query-dep-graph -Cpanic=unwind
// build-pass (FIXME(62277): could be check-pass?)
// Test that changing a tracked commandline argument invalidates
// the cache while changing an untracked one doesn't.
-// ignore-asmjs wasm2js does not support source maps yet
// revisions:rpass1 rpass2 rpass3
// compile-flags: -Z query-dep-graph
// compile-flags: -Z query-dep-graph -g
// aux-build:extern_crate.rs
-// ignore-asmjs wasm2js does not support source maps yet
// This test case makes sure that we detect if paths emitted into debuginfo
// are changed, even when the change happens in an external crate.
// the spans and this test makes sure that we handle them correctly by hashing
// file:line:column instead of raw byte offset.
-// ignore-asmjs wasm2js does not support source maps yet
// revisions:rpass1 rpass2
// compile-flags: -g -Z query-dep-graph
// Test that moving a type definition within a source file does not affect
// re-compilation.
-// ignore-asmjs wasm2js does not support source maps yet
// revisions:rpass1 rpass2
// compile-flags: -Z query-dep-graph -g
// revisions:rpass1 rpass2
-// ignore-asmjs wasm2js does not support source maps yet
// compile-flags: -g -Z query-dep-graph
#![feature(rustc_attrs)]
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(box_syntax)]
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// Test that we generate StorageDead on unwind paths for generators.
//
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// check that we don't emit multiple drop flags when they are not needed.
// check that we don't forget to drop the Box if we early return before
// initializing it
// ignore-tidy-linelength
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(box_syntax)]
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// Test that after the call to `std::mem::drop` we do not generate a
// MIR drop of the argument. (We used to have a `DROP(_2)` in the code
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
fn main() {
let mut x = Packed(Aligned(Droppy(0)));
// Test that the fake borrows for matches are removed after borrow checking.
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare
fn match_guard(x: Option<&&i32>, c: bool) -> i32 {
match x {
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// ignore-tidy-linelength
// compile-flags: -Z mir-emit-retag -Z mir-opt-level=0 -Z span_free_formats
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) foo.rs --target wasm32-unknown-unknown
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) foo.rs -O --target wasm32-unknown-unknown
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) bar.rs --target wasm32-unknown-unknown
-include ../../run-make-fulldeps/tools.mk
- # only-wasm32-bare
+ # only-wasm32
all:
$(RUSTC) foo.rs --target wasm32-unknown-unknown
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg a
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) foo.rs --target wasm32-unknown-unknown
-include ../../run-make-fulldeps/tools.mk
-# only-wasm32-bare
+# only-wasm32
all:
$(RUSTC) foo.rs --target wasm32-unknown-unknown
// ignore-wasm32-bare no libc to test ffi with
-// FIXME: This will work on emscripten once libc is updated to include
-// rust-lang/libc/#1478
-// ignore-emscripten libc type mismatch
-
#![feature(rustc_private)]
extern crate libc;
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+// check-pass
+// Check that the anonymous lifetimes used here aren't considered to shadow one
+// another. Note that `async fn` is different to `fn` here because the lifetimes
+// are numbered by HIR lowering, rather than lifetime resolution.
+
+// edition:2018
+
+struct A<'a, 'b>(&'a &'b i32);
+struct B<'a>(&'a i32);
+
+impl A<'_, '_> {
+ async fn assoc(x: &u32, y: B<'_>) {
+ async fn nested(x: &u32, y: A<'_, '_>) {}
+ }
+
+ async fn assoc2(x: &u32, y: A<'_, '_>) {
+ impl A<'_, '_> {
+ async fn nested_assoc(x: &u32, y: B<'_>) {}
+ }
+ }
+}
+
+fn main() {}
--- /dev/null
+// edition:2018
+// run-rustfix
+
+fn foo() -> Box<impl std::future::Future<Output = u32>> {
+ let x = 0u32;
+ Box::new(async move { x } )
+ //~^ ERROR E0373
+}
+
+fn main() {
+ let _foo = foo();
+}
--- /dev/null
+// edition:2018
+// run-rustfix
+
+fn foo() -> Box<impl std::future::Future<Output = u32>> {
+ let x = 0u32;
+ Box::new(async { x } )
+ //~^ ERROR E0373
+}
+
+fn main() {
+ let _foo = foo();
+}
--- /dev/null
+error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
+ --> $DIR/async-borrowck-escaping-block-error.rs:6:20
+ |
+LL | Box::new(async { x } )
+ | ^^-^^
+ | | |
+ | | `x` is borrowed here
+ | may outlive borrowed value `x`
+ |
+note: generator is returned here
+ --> $DIR/async-borrowck-escaping-block-error.rs:4:13
+ |
+LL | fn foo() -> Box<impl std::future::Future<Output = u32>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
+ |
+LL | Box::new(async move { x } )
+ | ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0373`.
//
// See issue #59123 for a full explanation.
-// ignore-emscripten (sizes don't match)
+// ignore-wasm32-bare (sizes don't match)
// run-pass
// edition:2018
impl BigFut {
fn new() -> Self {
BigFut([0; BIG_FUT_SIZE])
- } }
+ }
+}
impl Drop for BigFut {
fn drop(&mut self) {}
--- /dev/null
+// Test that we don't store uninitialized locals in futures from `async fn`.
+//
+// The exact sizes can change by a few bytes (we'd like to know when they do).
+// What we don't want to see is the wrong multiple of 1024 (the size of `Big`)
+// being reflected in the size.
+
+// ignore-wasm32-bare (sizes don't match)
+// run-pass
+
+// edition:2018
+
+#![allow(unused_variables, unused_assignments)]
+
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+const BIG_FUT_SIZE: usize = 1024;
+struct Big([u8; BIG_FUT_SIZE]);
+
+impl Big {
+ fn new() -> Self {
+ Big([0; BIG_FUT_SIZE])
+ }
+}
+
+impl Drop for Big {
+ fn drop(&mut self) {}
+}
+
+#[allow(dead_code)]
+struct Joiner {
+ a: Option<Big>,
+ b: Option<Big>,
+ c: Option<Big>,
+}
+
+impl Future for Joiner {
+ type Output = ();
+
+ fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
+ Poll::Ready(())
+ }
+}
+
+fn noop() {}
+async fn fut() {}
+
+async fn single() {
+ let x;
+ fut().await;
+ x = Big::new();
+}
+
+async fn single_with_noop() {
+ let x;
+ fut().await;
+ noop();
+ x = Big::new();
+ noop();
+}
+
+async fn joined() {
+ let joiner;
+ let a = Big::new();
+ let b = Big::new();
+ let c = Big::new();
+
+ fut().await;
+ noop();
+ joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
+ noop();
+}
+
+async fn joined_with_noop() {
+ let joiner;
+ let a = Big::new();
+ let b = Big::new();
+ let c = Big::new();
+
+ fut().await;
+ noop();
+ joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
+ noop();
+}
+
+async fn join_retval() -> Joiner {
+ let a = Big::new();
+ let b = Big::new();
+ let c = Big::new();
+
+ fut().await;
+ noop();
+ Joiner { a: Some(a), b: Some(b), c: Some(c) }
+}
+
+fn main() {
+ assert_eq!(8, std::mem::size_of_val(&single()));
+ assert_eq!(12, std::mem::size_of_val(&single_with_noop()));
+ assert_eq!(3084, std::mem::size_of_val(&joined()));
+ assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
+ assert_eq!(3080, std::mem::size_of_val(&join_retval()));
+}
assert_eq!(8, std::mem::size_of_val(&await1_level1()));
assert_eq!(12, std::mem::size_of_val(&await2_level1()));
assert_eq!(12, std::mem::size_of_val(&await3_level1()));
- assert_eq!(20, std::mem::size_of_val(&await3_level2()));
- assert_eq!(28, std::mem::size_of_val(&await3_level3()));
- assert_eq!(36, std::mem::size_of_val(&await3_level4()));
- assert_eq!(44, std::mem::size_of_val(&await3_level5()));
+ assert_eq!(24, std::mem::size_of_val(&await3_level2()));
+ assert_eq!(36, std::mem::size_of_val(&await3_level3()));
+ assert_eq!(48, std::mem::size_of_val(&await3_level4()));
+ assert_eq!(60, std::mem::size_of_val(&await3_level5()));
assert_eq!(1, wait(base()));
assert_eq!(1, wait(await1_level1()));
// compile-flags: -Copt-level=z -Cdebuginfo=2 --edition=2018
// run-pass
-// ignore-asmjs wasm2js does not support source maps yet
use std::future::Future;
use std::task::Poll;
error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0380`.
+Some errors have detailed explanations: E0380, E0567.
+For more information about an error, try `rustc --explain E0380`.
// Check that partially moved from function parameters are dropped after the
// named bindings that move from them.
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
use std::{panic, cell::RefCell};
// run-pass
#![allow(dead_code)]
// compile-flags: -g
-// ignore-asmjs wasm2js does not support source maps yet
#[derive(PartialEq, Eq)]
struct NewBool(bool);
#![allow(unused_variables)]
#![allow(unused_imports)]
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// Test that builtin implementations of `Clone` cleanup everything
// in case of unwinding.
error: aborting due to 8 previous errors
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0495.
+For more information about an error, try `rustc --explain E0308`.
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
fn worker() -> ! {
panic!()
error: aborting due to 8 previous errors
+For more information about this error, try `rustc --explain E0566`.
LL | let _: ConstString<"Hello"> = ConstString::<"World">;
| ^^^^^^^^^^^^^^^^^^^^^^ expected `"Hello"`, found `"World"`
|
- = note: expected type `ConstString<>`
- found type `ConstString<>`
+ = note: expected type `ConstString<"Hello">`
+ found type `ConstString<"World">`
error[E0308]: mismatched types
--> $DIR/slice-const-param-mismatch.rs:11:33
LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">;
| ^^^^^^^^^^^^^^^^^^^^^ expected `"ℇ㇈↦"`, found `"ℇ㇈↥"`
|
- = note: expected type `ConstString<>`
- found type `ConstString<>`
+ = note: expected type `ConstString<"ℇ㇈↦">`
+ found type `ConstString<"ℇ㇈↥">`
error[E0308]: mismatched types
--> $DIR/slice-const-param-mismatch.rs:13:33
LL | let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
| ^^^^^^^^^^^^^^^^^^^^ expected `b"AAA"`, found `b"BBB"`
|
- = note: expected type `ConstBytes<>`
- found type `ConstBytes<>`
+ = note: expected type `ConstBytes<b"AAA">`
+ found type `ConstBytes<b"BBB">`
error: aborting due to 3 previous errors
--- /dev/null
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
+
+// tests the diagnostic output of type mismatches for types that have const generics arguments.
+
+use std::marker::PhantomData;
+
+struct A<'a, T, const X: u32, const Y: u32> {
+ data: PhantomData<&'a T>
+}
+
+fn a<'a, 'b>() {
+ let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData };
+ //~^ ERROR mismatched types
+ let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData };
+ //~^ ERROR mismatched types
+}
+
+pub fn main() {}
--- /dev/null
+warning: the feature `const_generics` is incomplete and may cause the compiler to crash
+ --> $DIR/types-mismatch-const-args.rs:1:12
+ |
+LL | #![feature(const_generics)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+ --> $DIR/types-mismatch-const-args.rs:13:41
+ |
+LL | let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
+ |
+ = note: expected type `A<'_, _, 2u32, _>`
+ found type `A<'_, _, 4u32, _>`
+
+error[E0308]: mismatched types
+ --> $DIR/types-mismatch-const-args.rs:15:41
+ |
+LL | let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data: PhantomData };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u16, found u32
+ |
+ = note: expected type `A<'a, u16, _, _>`
+ found type `A<'b, u32, _, _>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
impl<A: Unsigned, B: Unsigned> Unsigned for Sum<A,B> {
const MAX: u8 = A::MAX + B::MAX; //~ ERROR any use of this value will cause an error
- //~| ERROR any use of this value will cause an error
}
fn foo<T>(_: T) -> &'static u8 {
= note: `#[deny(const_err)]` on by default
error[E0080]: evaluation of constant expression failed
- --> $DIR/issue-50814.rs:18:5
+ --> $DIR/issue-50814.rs:17:5
|
LL | &Sum::<U8,U8>::MAX
| ^-----------------
| |
| referenced constant has errors
-error: any use of this value will cause an error
- --> $DIR/issue-50814.rs:13:21
- |
-LL | const MAX: u8 = A::MAX + B::MAX;
- | ----------------^^^^^^^^^^^^^^^-
- | |
- | attempt to add with overflow
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// run-pass
+
+// This test verifies that the `ConstProp` pass doesn't cause an ICE when evaluating polymorphic
+// promoted MIR.
+
+pub trait ArrowPrimitiveType {
+ type Native;
+}
+
+pub fn new<T: ArrowPrimitiveType>() {
+ assert_eq!(0, std::mem::size_of::<T::Native>());
+}
+
+impl ArrowPrimitiveType for () {
+ type Native = ();
+}
+
+fn main() {
+ new::<()>();
+}
--- /dev/null
+#![feature(const_extern_fn)]
+
+extern "C" {
+ fn regular_in_block();
+}
+
+const extern fn bar() {
+ unsafe {
+ regular_in_block();
+ //~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
+ }
+}
+
+extern fn regular() {}
+
+const extern fn foo() {
+ unsafe {
+ regular();
+ //~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
+ --> $DIR/const-extern-fn-call-extern-fn.rs:9:9
+ |
+LL | regular_in_block();
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
+ --> $DIR/const-extern-fn-call-extern-fn.rs:18:9
+ |
+LL | regular();
+ | ^^^^^^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0723`.
--- /dev/null
+#![feature(const_extern_fn)]
+
+const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
+//~^ ERROR unsizing casts are not allowed in const fn
+const unsafe extern "C" fn closure() -> fn() { || {} }
+//~^ ERROR function pointers in const fn are unstable
+const unsafe extern fn use_float() { 1.0 + 1.0; }
+//~^ ERROR only int, `bool` and `char` operations are stable in const fn
+const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
+//~^ ERROR casting pointers to ints is unstable in const fn
+
+
+fn main() {}
--- /dev/null
+error[E0723]: unsizing casts are not allowed in const fn
+ --> $DIR/const-extern-fn-min-const-fn.rs:3:48
+ |
+LL | const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
+ | ^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error[E0723]: function pointers in const fn are unstable
+ --> $DIR/const-extern-fn-min-const-fn.rs:5:41
+ |
+LL | const unsafe extern "C" fn closure() -> fn() { || {} }
+ | ^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error[E0723]: only int, `bool` and `char` operations are stable in const fn
+ --> $DIR/const-extern-fn-min-const-fn.rs:7:38
+ |
+LL | const unsafe extern fn use_float() { 1.0 + 1.0; }
+ | ^^^^^^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error[E0723]: casting pointers to ints is unstable in const fn
+ --> $DIR/const-extern-fn-min-const-fn.rs:9:48
+ |
+LL | const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
+ | ^^^^^^^^^^^^
+ |
+ = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
+ = help: add `#![feature(const_fn)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0723`.
--- /dev/null
+#![feature(const_extern_fn)]
+
+const unsafe extern fn foo() -> usize { 5 }
+
+fn main() {
+ let a: [u8; foo()];
+ //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+ foo();
+ //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+}
--- /dev/null
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/const-extern-fn-requires-unsafe.rs:8:5
+ |
+LL | foo();
+ | ^^^^^ call to unsafe function
+ |
+ = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/const-extern-fn-requires-unsafe.rs:6:17
+ |
+LL | let a: [u8; foo()];
+ | ^^^^^ call to unsafe function
+ |
+ = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
--- /dev/null
+// run-pass
+#![feature(const_extern_fn)]
+
+const extern fn foo1(val: u8) -> u8 {
+ val + 1
+}
+
+const extern "C" fn foo2(val: u8) -> u8 {
+ val + 1
+}
+
+const unsafe extern fn bar1(val: bool) -> bool {
+ !val
+}
+
+const unsafe extern "C" fn bar2(val: bool) -> bool {
+ !val
+}
+
+
+fn main() {
+ let a: [u8; foo1(25) as usize] = [0; 26];
+ let b: [u8; foo2(25) as usize] = [0; 26];
+ assert_eq!(a, b);
+
+ let bar1_res = unsafe { bar1(false) };
+ let bar2_res = unsafe { bar2(false) };
+ assert!(bar1_res);
+ assert_eq!(bar1_res, bar2_res);
+
+ let _foo1_cast: extern fn(u8) -> u8 = foo1;
+ let _foo2_cast: extern fn(u8) -> u8 = foo2;
+ let _bar1_cast: unsafe extern fn(bool) -> bool = bar1;
+ let _bar2_cast: unsafe extern fn(bool) -> bool = bar2;
+}
--- /dev/null
+// Check that `const extern fn` and `const unsafe extern fn` are feature-gated.
+
+#[cfg(FALSE)] const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
+#[cfg(FALSE)] const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
+#[cfg(FALSE)] const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
+#[cfg(FALSE)] const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
+#[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
+//~^ ERROR `const extern fn` definitions are unstable
+#[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
+//~^ ERROR `const extern fn` definitions are unstable
+
+fn main() {}
--- /dev/null
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:3:15
+ |
+LL | #[cfg(FALSE)] const extern fn foo1() {}
+ | ^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:4:15
+ |
+LL | #[cfg(FALSE)] const extern "C" fn foo2() {}
+ | ^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:5:15
+ |
+LL | #[cfg(FALSE)] const extern "Rust" fn foo3() {}
+ | ^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:6:15
+ |
+LL | #[cfg(FALSE)] const unsafe extern fn bar1() {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:7:15
+ |
+LL | #[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error[E0658]: `const extern fn` definitions are unstable
+ --> $DIR/feature-gate-const_extern_fn.rs:9:15
+ |
+LL | #[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/64926
+ = help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
// run-pass
+// ignore-emscripten no i128 support
#![feature(const_saturating_int_methods)]
const INT_U32_NO: u32 = (42 as u32).saturating_add(2);
// aux-build:debuginfo-lto-aux.rs
// compile-flags: -C lto -g
// no-prefer-dynamic
-// ignore-asmjs wasm2js does not support source maps yet
extern crate debuginfo_lto_aux;
error: aborting due to 9 previous errors
-Some errors have detailed explanations: E0538, E0541, E0550, E0565.
+Some errors have detailed explanations: E0538, E0541, E0550, E0551, E0565.
For more information about an error, try `rustc --explain E0538`.
-fn foo() {
+fn main() {
(0..13).collect<Vec<i32>>();
//~^ ERROR chained comparison
-}
-
-fn bar() {
Vec<i32>::new();
//~^ ERROR chained comparison
-}
-
-fn qux() {
(0..13).collect<Vec<i32>();
//~^ ERROR chained comparison
}
-
-fn main() {}
--> $DIR/issue-40396.rs:2:20
|
LL | (0..13).collect<Vec<i32>>();
- | ^^^^^^^^
+ | ^^^^^
+help: use `::<...>` instead of `<...>` to specify type arguments
|
- = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
- = help: or use `(...)` if you meant to specify fn arguments
+LL | (0..13).collect::<Vec<i32>>();
+ | ^^
error: chained comparison operators require parentheses
- --> $DIR/issue-40396.rs:7:8
+ --> $DIR/issue-40396.rs:4:8
|
LL | Vec<i32>::new();
- | ^^^^^^^
+ | ^^^^^
+help: use `::<...>` instead of `<...>` to specify type arguments
|
- = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
- = help: or use `(...)` if you meant to specify fn arguments
+LL | Vec::<i32>::new();
+ | ^^
error: chained comparison operators require parentheses
- --> $DIR/issue-40396.rs:12:20
+ --> $DIR/issue-40396.rs:6:20
|
LL | (0..13).collect<Vec<i32>();
- | ^^^^^^^^
+ | ^^^^^
+help: use `::<...>` instead of `<...>` to specify type arguments
|
- = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
- = help: or use `(...)` if you meant to specify fn arguments
+LL | (0..13).collect::<Vec<i32>();
+ | ^^
error: aborting due to 3 previous errors
| ^^^^^^
help: try using one of the enum's variants
|
-LL | let x = std::prelude::v1::Option::None(1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-LL | let x = std::prelude::v1::Option::Some(1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let x = std::option::Option::None(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let x = std::option::Option::Some(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0532]: expected tuple struct/variant, found enum `Option`
--> $DIR/issue-43871-enum-instead-of-variant.rs:21:12
| ^^^^^^
help: try using one of the enum's variants
|
-LL | if let std::prelude::v1::Option::None(_) = x {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-LL | if let std::prelude::v1::Option::Some(_) = x {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | if let std::option::Option::None(_) = x {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | if let std::option::Option::Some(_) = x {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0532]: expected tuple struct/variant, found enum `Example`
--> $DIR/issue-43871-enum-instead-of-variant.rs:27:12
| ^^^^^^^^^^^^
help: try using one of the enum's variants
|
-LL | let z = ManyVariants::Eight();
+LL | let z = ManyVariants::One();
+ | ^^^^^^^^^^^^^^^^^
+LL | let z = ManyVariants::Two();
+ | ^^^^^^^^^^^^^^^^^
+LL | let z = ManyVariants::Three();
| ^^^^^^^^^^^^^^^^^^^
-LL | let z = ManyVariants::Five();
- | ^^^^^^^^^^^^^^^^^^
LL | let z = ManyVariants::Four();
| ^^^^^^^^^^^^^^^^^^
-LL | let z = ManyVariants::Nine();
- | ^^^^^^^^^^^^^^^^^^
and 6 other candidates
error: aborting due to 5 previous errors
// run-pass
// edition:2018
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(slice_patterns)]
#![allow(unused)]
#![allow(unused_assignments)]
#![allow(unused_variables)]
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(generators, generator_trait, untagged_unions)]
#![feature(slice_patterns)]
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
// compile. To sidestep this by using one that *is* defined.
// run-rustfix
-// ignore-wasm32-bare no external library to link to.
-// ignore-asmjs wasm2js does not support source maps yet
+// ignore-wasm32 no external library to link to.
// compile-flags: -g
#![feature(rustc_private)]
extern crate libc;
// compile. To sidestep this by using one that *is* defined.
// run-rustfix
-// ignore-wasm32-bare no external library to link to.
-// ignore-asmjs wasm2js does not support source maps yet
+// ignore-wasm32 no external library to link to.
// compile-flags: -g
#![feature(rustc_private)]
extern crate libc;
error: extern items cannot be `const`
- --> $DIR/extern-const.rs:16:5
+ --> $DIR/extern-const.rs:15:5
|
LL | const rust_dbg_static_mut: libc::c_int;
| ^^^^^ help: try using a static value: `static`
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0566, E0658.
+For more information about an error, try `rustc --explain E0566`.
--- /dev/null
+#[track_caller]
+fn f() {}
+//~^^ ERROR the `#[track_caller]` attribute is an experimental feature
+
+fn main() {}
--- /dev/null
+error[E0658]: the `#[track_caller]` attribute is an experimental feature
+ --> $DIR/feature-gate-track_caller.rs:1:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/47809
+ = help: add `#![feature(track_caller)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
// run-pass
// compile-flags: -g
-// ignore-asmjs wasm2js does not support source maps yet
#![feature(generators, generator_trait)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled as panic=abort by default
#![feature(generators, generator_trait)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(generators, generator_trait)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(generators, generator_trait)]
// edition:2018
// ignore-wasm32 issue #62807
-// ignore-asmjs issue #62807
#![feature(generators, generator_trait)]
--> $DIR/if-no-match-bindings.rs:18:8
|
LL | if b_ref() {}
- | ^^^^^^^ expected bool, found &bool
+ | ^^^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider dereferencing the borrow: `*b_ref()`
|
= note: expected type `bool`
found type `&bool`
--> $DIR/if-no-match-bindings.rs:19:8
|
LL | if b_mut_ref() {}
- | ^^^^^^^^^^^ expected bool, found &mut bool
+ | ^^^^^^^^^^^
+ | |
+ | expected bool, found &mut bool
+ | help: consider dereferencing the borrow: `*b_mut_ref()`
|
= note: expected type `bool`
found type `&mut bool`
--> $DIR/if-no-match-bindings.rs:20:8
|
LL | if &true {}
- | ^^^^^ expected bool, found &bool
+ | ^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider removing the borrow: `true`
|
= note: expected type `bool`
found type `&bool`
--> $DIR/if-no-match-bindings.rs:21:8
|
LL | if &mut true {}
- | ^^^^^^^^^ expected bool, found &mut bool
+ | ^^^^^^^^^
+ | |
+ | expected bool, found &mut bool
+ | help: consider removing the borrow: `true`
|
= note: expected type `bool`
found type `&mut bool`
--> $DIR/if-no-match-bindings.rs:24:11
|
LL | while b_ref() {}
- | ^^^^^^^ expected bool, found &bool
+ | ^^^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider dereferencing the borrow: `*b_ref()`
|
= note: expected type `bool`
found type `&bool`
--> $DIR/if-no-match-bindings.rs:25:11
|
LL | while b_mut_ref() {}
- | ^^^^^^^^^^^ expected bool, found &mut bool
+ | ^^^^^^^^^^^
+ | |
+ | expected bool, found &mut bool
+ | help: consider dereferencing the borrow: `*b_mut_ref()`
|
= note: expected type `bool`
found type `&mut bool`
--> $DIR/if-no-match-bindings.rs:26:11
|
LL | while &true {}
- | ^^^^^ expected bool, found &bool
+ | ^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider removing the borrow: `true`
|
= note: expected type `bool`
found type `&bool`
--> $DIR/if-no-match-bindings.rs:27:11
|
LL | while &mut true {}
- | ^^^^^^^^^ expected bool, found &mut bool
+ | ^^^^^^^^^
+ | |
+ | expected bool, found &mut bool
+ | help: consider removing the borrow: `true`
|
= note: expected type `bool`
found type `&mut bool`
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+// ignore-test auxiliary file for include-single-expr.rs
+
+0
+
+// trailing comment permitted
--- /dev/null
+// ignore-test auxiliary file for include-single-expr.rs
+
+0
+10
+100
--- /dev/null
+// error-pattern include macro expected single expression
+
+fn main() {
+ include!("include-single-expr-helper.rs");
+ include!("include-single-expr-helper-1.rs");
+}
--- /dev/null
+error: include macro expected single expression in source
+ --> $DIR/include-single-expr-helper.rs:4:1
+ |
+LL | 10
+ | ^^
+ |
+ = note: `#[deny(incomplete_include)]` on by default
+
+error: aborting due to previous error
+
--> $DIR/inner-static-type-parameter.rs:6:19
|
LL | fn foo<T>() {
- | --- - type parameter from outer function
- | |
- | try adding a local generic parameter in this method instead
+ | - type parameter from outer function
LL | static a: Bar<T> = Bar::What;
| ^ use of generic parameter from outer function
// run-pass
+// ignore-emscripten no i128 support
#![feature(intrinsics)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare always compiled as panic=abort right now
// Check that values are not leaked when a dtor panics (#14875)
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0495.
+For more information about an error, try `rustc --explain E0308`.
// build-pass
-// ignore-asmjs wasm2js does not support source maps yet
// compile-flags: -g
pub struct Dst {
// run-pass
// aux-build:issue-24687-lib.rs
// compile-flags:-g
-// ignore-asmjs wasm2js does not support source maps yet
extern crate issue_24687_lib as d;
// as options to the compiler.
// compile-flags:-g -g -O -O
-// ignore-asmjs wasm2js does not support source maps yet
fn main() {
assert_eq!(1, 1);
// run-pass
// compile-flags:-g
-// ignore-asmjs wasm2js does not support source maps yet
fn helper<F: FnOnce(usize) -> bool>(_f: F) {
print!("");
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
use std::panic;
// run-pass
// compile-flags: -g
-// ignore-asmjs wasm2js does not support source maps yet
use std::ops::Deref;
// run-pass
// ignore-windows
// ignore-macos
-// ignore-emscripten common linkage not implemented right now
+// ignore-wasm32-bare common linkage not implemented right now
#![feature(linkage)]
// run-pass
// compile-flags:-g
-// ignore-asmjs wasm2js does not support source maps yet
// In this test we just want to make sure that the code below does not lead to
// a debuginfo verification assertion during compilation. This was caused by the
| ^^^^
| |
| not a type
- | help: try using the variant's enum: `Option`
+ | help: try using the variant's enum: `std::option::Option`
error: aborting due to 7 previous errors
// Regression test for #36856.
// compile-flags:-g
-// ignore-asmjs wasm2js does not support source maps yet
fn g() -> bool {
false
| -----^^^^
| | |
| | this is an associated function, not a method
- | help: use associated function syntax instead: `&Obj::boom`
+ | help: use associated function syntax instead: `Obj::boom`
|
= note: found the following associated functions; to be used as methods, functions must have a `self` parameter
note: the candidate is defined in an impl for the type `Obj`
// Regression test for #42210.
// compile-flags: -g
-// ignore-asmjs wasm2js does not support source maps yet
trait Foo {
fn foo() { }
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
use std::panic;
// run-pass
#![allow(unused_variables)]
// compile-flags:--test -g
-// ignore-asmjs wasm2js does not support source maps yet
#[cfg(target_os = "macos")]
#[test]
// run-pass
// compile-flags:--test -O
-// ignore-emscripten compiled with panic=abort by default
-
#[test]
#[should_panic(expected = "creating inhabited type")]
fn test() {
// compile-flags:-g
// ignore-pretty issue #37195
-// ignore-asmjs wasm2js does not support source maps yet
#![feature(non_ascii_idents)]
// build-pass (FIXME(62277): could be check-pass?)
+// ignore-emscripten no i128 support
fn fibs(n: u32) -> impl Iterator<Item=u128> {
(0 .. n)
-#![feature(inner_deref)]
-
fn main() {
let _result = &Some(42).as_deref();
//~^ ERROR no method named `as_deref` found for type `std::option::Option<{integer}>`
error[E0599]: no method named `as_deref` found for type `std::option::Option<{integer}>` in the current scope
- --> $DIR/option-as_deref.rs:4:29
+ --> $DIR/option-as_deref.rs:2:29
|
LL | let _result = &Some(42).as_deref();
| ^^^^^^^^ help: there is a method with a similar name: `as_ref`
-#![feature(inner_deref)]
-
fn main() {
let _result = &mut Some(42).as_deref_mut();
//~^ ERROR no method named `as_deref_mut` found for type `std::option::Option<{integer}>`
error[E0599]: no method named `as_deref_mut` found for type `std::option::Option<{integer}>` in the current scope
- --> $DIR/option-as_deref_mut.rs:4:33
+ --> $DIR/option-as_deref_mut.rs:2:33
|
LL | let _result = &mut Some(42).as_deref_mut();
| ^^^^^^^^^^^^ method not found in `std::option::Option<{integer}>`
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+// compile-flags:-Ztreat-err-as-bug=5
+#[derive(Debug)]
+enum MyError {
+ NotFound { key: Vec<u8> },
+ Err41,
+}
+
+impl std::error::Error for MyError {}
+
+impl std::fmt::Display for MyError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ MyError::NotFound { key } => write!(
+ f,
+ "unknown error with code {}.",
+ String::from_utf8(*key).unwrap()
+ //~^ ERROR cannot move out of `*key` which is behind a shared reference
+ ),
+ MyError::Err41 => write!(f, "Sit by a lake"),
+ }
+ }
+}
+fn main() {
+ println!("Hello, world!");
+}
--- /dev/null
+error[E0507]: cannot move out of `*key` which is behind a shared reference
+ --> $DIR/issue-52262.rs:16:35
+ |
+LL | String::from_utf8(*key).unwrap()
+ | ^^^^ move occurs because `*key` has type `std::vec::Vec<u8>`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0495`.
// run-pass
// compile-flags:-C debuginfo=2
-// ignore-asmjs wasm2js does not support source maps yet
-
fn foo() -> impl Copy {
foo
}
// run-pass
// only-32bit too impatient for 2⁶⁴ items
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C debug_assertions=yes -C opt-level=3
use std::panic;
// run-pass
// only-32bit too impatient for 2⁶⁴ items
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C debug_assertions=yes -C opt-level=3
use std::panic;
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C debug_assertions=yes
use std::panic;
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C debug_assertions=yes
use std::panic;
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: -C overflow-checks
use std::panic;
// compile-flags: --test -C debug_assertions=yes
// revisions: std core
-// ignore-emscripten compiled with panic=abort by default
-
#![cfg_attr(core, no_std)]
#[cfg(std)] use std::fmt;
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![feature(fn_traits)]
#![feature(never_type)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
use std::cell::RefCell;
use std::panic;
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0130, E0642.
+Some errors have detailed explanations: E0130, E0561, E0642.
For more information about an error, try `rustc --explain E0130`.
error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0130`.
+Some errors have detailed explanations: E0130, E0561.
+For more information about an error, try `rustc --explain E0130`.
| ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary
|
= help: the trait `std::panic::UnwindSafe` is not implemented for `&mut i32`
+ = note: `std::panic::UnwindSafe` is implemented for `&i32`, but not for `&mut i32`
error: aborting due to previous error
// run-pass
+// ignore-emscripten no i128 support
#![deny(const_err)]
// run-pass
#![allow(overflowing_literals)]
+// ignore-emscripten i128 doesn't work
+
+
#![feature(test)]
extern crate test;
// run-pass
// compile-flags: -C debug_assertions=yes
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// ignore-emscripten dies with an LLVM error
use std::panic;
// run-pass
+// ignore-emscripten u128 not supported
#![feature(test)]
#![deny(overflowing_literals)]
// run-pass
+// ignore-emscripten u128 not supported
+
#![feature(test)]
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0495`.
// aux-build:wants-panic-runtime-abort.rs
// aux-build:panic-runtime-lang-items.rs
// error-pattern: is not compiled with this crate's panic strategy `unwind`
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![no_std]
#![no_main]
// error-pattern:is incompatible with this crate's strategy of `unwind`
// aux-build:panic-runtime-abort.rs
// aux-build:panic-runtime-lang-items.rs
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![no_std]
#![no_main]
// aux-build:panic-runtime-abort.rs
// aux-build:wants-panic-runtime-abort.rs
// aux-build:panic-runtime-lang-items.rs
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![no_std]
#![no_main]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare always compiled as panic=abort right now and this requires unwinding
// This test checks that instantiating an uninhabited type via `mem::{uninitialized,zeroed}` results
// in a runtime panic.
impl S {
fn f(*, a: u8) -> u8 {}
//~^ ERROR expected parameter name, found `*`
+ //~| ERROR mismatched types
}
fn main() {}
LL | fn f(*, a: u8) -> u8 {}
| ^ expected parameter name
-error: aborting due to previous error
+error[E0308]: mismatched types
+ --> $DIR/issue-33413.rs:4:23
+ |
+LL | fn f(*, a: u8) -> u8 {}
+ | - ^^ expected u8, found ()
+ | |
+ | implicitly returns `()` as its body has no tail or `return` expression
+ |
+ = note: expected type `u8`
+ found type `()`
+
+error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Regression test; used to ICE with 'visit_mac disabled by default' due to a
+// `MutVisitor` in `fn make_all_value_bindings_mutable` (`parse/parser/pat.rs`).
+
+macro_rules! mac1 {
+ ($eval:expr) => {
+ let mut $eval = ();
+ //~^ ERROR `mut` must be followed by a named binding
+ };
+}
+
+macro_rules! mac2 {
+ ($eval:pat) => {
+ let mut $eval = ();
+ //~^ ERROR `mut` must be followed by a named binding
+ //~| ERROR expected identifier, found `does_not_exist!()`
+ };
+}
+
+fn foo() {
+ mac1! { does_not_exist!() }
+ //~^ ERROR cannot find macro `does_not_exist` in this scope
+ mac2! { does_not_exist!() }
+ //~^ ERROR cannot find macro `does_not_exist` in this scope
+}
+
+fn main() {}
--- /dev/null
+error: `mut` must be followed by a named binding
+ --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:6:13
+ |
+LL | let mut $eval = ();
+ | ^^^^^^^^^ help: remove the `mut` prefix: `does_not_exist!()`
+...
+LL | mac1! { does_not_exist!() }
+ | --------------------------- in this macro invocation
+ |
+ = note: `mut` may be followed by `variable` and `variable @ pattern`
+
+error: expected identifier, found `does_not_exist!()`
+ --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:13:17
+ |
+LL | let mut $eval = ();
+ | ^^^^^ expected identifier
+...
+LL | mac2! { does_not_exist!() }
+ | --------------------------- in this macro invocation
+
+error: `mut` must be followed by a named binding
+ --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:13:13
+ |
+LL | let mut $eval = ();
+ | ^^^ help: remove the `mut` prefix: `does_not_exist!()`
+...
+LL | mac2! { does_not_exist!() }
+ | --------------------------- in this macro invocation
+ |
+ = note: `mut` may be followed by `variable` and `variable @ pattern`
+
+error: cannot find macro `does_not_exist` in this scope
+ --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:20:13
+ |
+LL | mac1! { does_not_exist!() }
+ | ^^^^^^^^^^^^^^
+
+error: cannot find macro `does_not_exist` in this scope
+ --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:22:13
+ |
+LL | mac2! { does_not_exist!() }
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+extern {
+ const fn foo();
+ //~^ ERROR extern items cannot be `const`
+ const unsafe fn bar();
+ //~^ ERROR extern items cannot be `const`
+}
+
+fn main() {}
--- /dev/null
+error: extern items cannot be `const`
+ --> $DIR/no-const-fn-in-extern-block.rs:2:5
+ |
+LL | const fn foo();
+ | ^^^^^
+
+error: extern items cannot be `const`
+ --> $DIR/no-const-fn-in-extern-block.rs:4:5
+ |
+LL | const unsafe fn bar();
+ | ^^^^^
+
+error: aborting due to 2 previous errors
+
fn main() {
false == false == false;
- //~^ ERROR: chained comparison operators require parentheses
+ //~^ ERROR chained comparison operators require parentheses
false == 0 < 2;
- //~^ ERROR: chained comparison operators require parentheses
- //~| ERROR: mismatched types
- //~| ERROR: mismatched types
+ //~^ ERROR chained comparison operators require parentheses
+ //~| ERROR mismatched types
+ //~| ERROR mismatched types
f<X>();
//~^ ERROR chained comparison operators require parentheses
- //~| HELP: use `::<...>` instead of `<...>`
- //~| HELP: or use `(...)`
+ //~| HELP use `::<...>` instead of `<...>` to specify type arguments
+
+ f<Result<Option<X>, Option<Option<X>>>(1, 2);
+ //~^ ERROR chained comparison operators require parentheses
+ //~| HELP use `::<...>` instead of `<...>` to specify type arguments
+
+ use std::convert::identity;
+ let _ = identity<u8>;
+ //~^ ERROR chained comparison operators require parentheses
+ //~| HELP use `::<...>` instead of `<...>` to specify type arguments
+ //~| HELP or use `(...)` if you meant to specify fn arguments
}
--> $DIR/require-parens-for-chained-comparison.rs:5:11
|
LL | false == false == false;
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^
error: chained comparison operators require parentheses
--> $DIR/require-parens-for-chained-comparison.rs:8:11
|
LL | false == 0 < 2;
- | ^^^^^^^^
+ | ^^^^^^
error: chained comparison operators require parentheses
--> $DIR/require-parens-for-chained-comparison.rs:13:6
|
LL | f<X>();
- | ^^^^
+ | ^^^
+help: use `::<...>` instead of `<...>` to specify type arguments
|
- = help: use `::<...>` instead of `<...>` if you meant to specify type arguments
+LL | f::<X>();
+ | ^^
+
+error: chained comparison operators require parentheses
+ --> $DIR/require-parens-for-chained-comparison.rs:17:6
+ |
+LL | f<Result<Option<X>, Option<Option<X>>>(1, 2);
+ | ^^^^^^^^
+help: use `::<...>` instead of `<...>` to specify type arguments
+ |
+LL | f::<Result<Option<X>, Option<Option<X>>>(1, 2);
+ | ^^
+
+error: chained comparison operators require parentheses
+ --> $DIR/require-parens-for-chained-comparison.rs:22:21
+ |
+LL | let _ = identity<u8>;
+ | ^^^^
+ |
+ = help: use `::<...>` instead of `<...>` to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments
error[E0308]: mismatched types
= note: expected type `bool`
found type `{integer}`
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0308`.
// run-pass
// aux-build:expand-with-a-macro.rs
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![deny(warnings)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// aux-build:reachable-unnameable-items.rs
extern crate reachable_unnameable_items;
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 4 previous errors
-For more information about this error, try `rustc --explain E0621`.
+Some errors have detailed explanations: E0495, E0621.
+For more information about an error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0312`.
+Some errors have detailed explanations: E0312, E0495.
+For more information about an error, try `rustc --explain E0312`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0308, E0478.
+Some errors have detailed explanations: E0308, E0478, E0495.
For more information about an error, try `rustc --explain E0308`.
error: aborting due to 8 previous errors
-Some errors have detailed explanations: E0308, E0366, E0367.
+Some errors have detailed explanations: E0308, E0366, E0367, E0495.
For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+unsafe fn foo<A>() {
+ extern "C" {
+ static baz: *const A;
+ //~^ ERROR can't use generic parameters from outer function
+ }
+
+ let bar: *const u64 = core::mem::transmute(&baz);
+}
+
+fn main() { }
--- /dev/null
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65025-extern-static-parent-generics.rs:3:28
+ |
+LL | unsafe fn foo<A>() {
+ | - type parameter from outer function
+LL | extern "C" {
+LL | static baz: *const A;
+ | ^ use of generic parameter from outer function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0401`.
--- /dev/null
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
+
+fn f<T>() {
+ extern "C" {
+ static a: *const T;
+ //~^ ERROR can't use generic parameters from outer function
+ }
+}
+
+fn g<T: Default>() {
+ static a: *const T = Default::default();
+ //~^ ERROR can't use generic parameters from outer function
+}
+
+fn h<const N: usize>() {
+ extern "C" {
+ static a: [u8; N];
+ //~^ ERROR can't use generic parameters from outer function
+ }
+}
+
+fn i<const N: usize>() {
+ static a: [u8; N] = [0; N];
+ //~^ ERROR can't use generic parameters from outer function
+ //~^^ ERROR can't use generic parameters from outer function
+}
+
+fn main() {}
--- /dev/null
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65035-static-with-parent-generics.rs:6:26
+ |
+LL | fn f<T>() {
+ | - type parameter from outer function
+LL | extern "C" {
+LL | static a: *const T;
+ | ^ use of generic parameter from outer function
+
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65035-static-with-parent-generics.rs:12:22
+ |
+LL | fn g<T: Default>() {
+ | - type parameter from outer function
+LL | static a: *const T = Default::default();
+ | ^ use of generic parameter from outer function
+
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65035-static-with-parent-generics.rs:18:24
+ |
+LL | fn h<const N: usize>() {
+ | - const parameter from outer function
+LL | extern "C" {
+LL | static a: [u8; N];
+ | ^ use of generic parameter from outer function
+
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65035-static-with-parent-generics.rs:24:20
+ |
+LL | fn i<const N: usize>() {
+ | - const parameter from outer function
+LL | static a: [u8; N] = [0; N];
+ | ^ use of generic parameter from outer function
+
+error[E0401]: can't use generic parameters from outer function
+ --> $DIR/issue-65035-static-with-parent-generics.rs:24:29
+ |
+LL | fn i<const N: usize>() {
+ | - const parameter from outer function
+LL | static a: [u8; N] = [0; N];
+ | ^ use of generic parameter from outer function
+
+warning: the feature `const_generics` is incomplete and may cause the compiler to crash
+ --> $DIR/issue-65035-static-with-parent-generics.rs:1:12
+ |
+LL | #![feature(const_generics)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0401`.
// compile-flags: --test
// run-pass
-// ignore-emscripten compiled with panic=abort by default
-
#![feature(test)]
extern crate test;
--- /dev/null
+#![feature(non_exhaustive)]
+
+#[non_exhaustive]
+#[repr(C)]
+pub enum NonExhaustiveEnum {
+ Unit,
+ Tuple(u32),
+ Struct { field: u32 }
+}
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct NormalStruct {
+ pub first_field: u16,
+ pub second_field: u16,
+}
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct UnitStruct;
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct TupleStruct (pub u16, pub u16);
+
+#[repr(C)]
+pub enum NonExhaustiveVariants {
+ #[non_exhaustive] Unit,
+ #[non_exhaustive] Tuple(u32),
+ #[non_exhaustive] Struct { field: u32 }
+}
--- /dev/null
+// aux-build:types.rs
+#![deny(improper_ctypes)]
+
+extern crate types;
+
+// This test checks that non-exhaustive types with `#[repr(C)]` from an extern crate are considered
+// improper.
+
+use types::{NonExhaustiveEnum, NormalStruct, UnitStruct, TupleStruct, NonExhaustiveVariants};
+
+extern {
+ pub fn non_exhaustive_enum(_: NonExhaustiveEnum);
+ //~^ ERROR `extern` block uses type `types::NonExhaustiveEnum`, which is not FFI-safe
+ pub fn non_exhaustive_normal_struct(_: NormalStruct);
+ //~^ ERROR `extern` block uses type `types::NormalStruct`, which is not FFI-safe
+ pub fn non_exhaustive_unit_struct(_: UnitStruct);
+ //~^ ERROR `extern` block uses type `types::UnitStruct`, which is not FFI-safe
+ pub fn non_exhaustive_tuple_struct(_: TupleStruct);
+ //~^ ERROR `extern` block uses type `types::TupleStruct`, which is not FFI-safe
+ pub fn non_exhaustive_variant(_: NonExhaustiveVariants);
+ //~^ ERROR `extern` block uses type `types::NonExhaustiveVariants`, which is not FFI-safe
+}
+
+fn main() { }
--- /dev/null
+error: `extern` block uses type `types::NonExhaustiveEnum`, which is not FFI-safe
+ --> $DIR/extern_crate_improper.rs:12:35
+ |
+LL | pub fn non_exhaustive_enum(_: NonExhaustiveEnum);
+ | ^^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+note: lint level defined here
+ --> $DIR/extern_crate_improper.rs:2:9
+ |
+LL | #![deny(improper_ctypes)]
+ | ^^^^^^^^^^^^^^^
+ = note: this enum is non-exhaustive
+
+error: `extern` block uses type `types::NormalStruct`, which is not FFI-safe
+ --> $DIR/extern_crate_improper.rs:14:44
+ |
+LL | pub fn non_exhaustive_normal_struct(_: NormalStruct);
+ | ^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: this struct is non-exhaustive
+
+error: `extern` block uses type `types::UnitStruct`, which is not FFI-safe
+ --> $DIR/extern_crate_improper.rs:16:42
+ |
+LL | pub fn non_exhaustive_unit_struct(_: UnitStruct);
+ | ^^^^^^^^^^ not FFI-safe
+ |
+ = note: this struct is non-exhaustive
+
+error: `extern` block uses type `types::TupleStruct`, which is not FFI-safe
+ --> $DIR/extern_crate_improper.rs:18:43
+ |
+LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct);
+ | ^^^^^^^^^^^ not FFI-safe
+ |
+ = note: this struct is non-exhaustive
+
+error: `extern` block uses type `types::NonExhaustiveVariants`, which is not FFI-safe
+ --> $DIR/extern_crate_improper.rs:20:38
+ |
+LL | pub fn non_exhaustive_variant(_: NonExhaustiveVariants);
+ | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+ |
+ = note: this enum has non-exhaustive variants
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// check-pass
+#![feature(non_exhaustive)]
+#![deny(improper_ctypes)]
+
+// This test checks that non-exhaustive types with `#[repr(C)]` are considered proper within
+// the defining crate.
+
+#[non_exhaustive]
+#[repr(C)]
+pub enum NonExhaustiveEnum {
+ Unit,
+ Tuple(u32),
+ Struct { field: u32 }
+}
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct NormalStruct {
+ pub first_field: u16,
+ pub second_field: u16,
+}
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct UnitStruct;
+
+#[non_exhaustive]
+#[repr(C)]
+pub struct TupleStruct (pub u16, pub u16);
+
+#[repr(C)]
+pub enum NonExhaustiveVariants {
+ #[non_exhaustive] Unit,
+ #[non_exhaustive] Tuple(u32),
+ #[non_exhaustive] Struct { field: u32 }
+}
+
+extern {
+ // Unit structs aren't tested here because they will trigger `improper_ctypes` anyway.
+ pub fn non_exhaustive_enum(_: NonExhaustiveEnum);
+ pub fn non_exhaustive_normal_struct(_: NormalStruct);
+ pub fn non_exhaustive_tuple_struct(_: TupleStruct);
+ pub fn non_exhaustive_variant(_: NonExhaustiveVariants);
+}
+
+fn main() { }
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+#[track_caller(1)]
+fn f() {}
+//~^^ ERROR malformed `track_caller` attribute input
+
+fn main() {}
--- /dev/null
+error: malformed `track_caller` attribute input
+ --> $DIR/error-odd-syntax.rs:3:1
+ |
+LL | #[track_caller(1)]
+ | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]`
+
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-odd-syntax.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+#[track_caller]
+extern "C" fn f() {}
+//~^^ ERROR rust ABI is required to use `#[track_caller]`
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-with-invalid-abi.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0737]: rust ABI is required to use `#[track_caller]`
+ --> $DIR/error-with-invalid-abi.rs:3:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0737`.
--- /dev/null
+#![feature(naked_functions, track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+#[track_caller]
+#[naked]
+fn f() {}
+//~^^^ ERROR cannot use `#[track_caller]` with `#[naked]`
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-with-naked.rs:1:29
+ |
+LL | #![feature(naked_functions, track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0736]: cannot use `#[track_caller]` with `#[naked]`
+ --> $DIR/error-with-naked.rs:3:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0736`.
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+trait Trait {
+ #[track_caller]
+ fn unwrap(&self);
+ //~^^ ERROR: `#[track_caller]` is not supported in trait declarations.
+}
+
+impl Trait for u64 {
+ fn unwrap(&self) {}
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-with-trait-decl.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0738]: `#[track_caller]` is not supported in trait declarations.
+ --> $DIR/error-with-trait-decl.rs:4:5
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0738`.
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+trait Trait {
+ #[track_caller]
+ fn unwrap(&self) {}
+ //~^^ ERROR: `#[track_caller]` is not supported in trait declarations.
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-with-trait-default-impl.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0738]: `#[track_caller]` is not supported in trait declarations.
+ --> $DIR/error-with-trait-default-impl.rs:4:5
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0738`.
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+trait Trait {
+ fn unwrap(&self);
+}
+
+impl Trait for u64 {
+ #[track_caller]
+ fn unwrap(&self) {}
+ //~^^ ERROR: `#[track_caller]` is not supported in traits yet.
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/error-with-trait-fn-impl.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0738]: `#[track_caller]` is not supported in traits yet.
+ --> $DIR/error-with-trait-fn-impl.rs:8:5
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0738`.
--- /dev/null
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+#[track_caller]
+struct S;
+//~^^ ERROR attribute should be applied to function
+
+fn main() {}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/only-for-fns.rs:1:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0739]: attribute should be applied to function
+ --> $DIR/only-for-fns.rs:3:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+LL | struct S;
+ | --------- not a function
+
+error: aborting due to previous error
+
--- /dev/null
+// run-pass
+#![feature(track_caller)] //~ WARN the feature `track_caller` is incomplete
+
+#[track_caller]
+fn f() {}
+
+fn main() {
+ f();
+}
--- /dev/null
+warning: the feature `track_caller` is incomplete and may cause the compiler to crash
+ --> $DIR/pass.rs:2:12
+ |
+LL | #![feature(track_caller)]
+ | ^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
--> $DIR/disallowed-positions.rs:32:8
|
LL | if &let 0 = 0 {}
- | ^^^^^^^^^^ expected bool, found &bool
+ | ^^^^^^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider removing the borrow: `let 0 = 0`
|
= note: expected type `bool`
found type `&bool`
--> $DIR/disallowed-positions.rs:96:11
|
LL | while &let 0 = 0 {}
- | ^^^^^^^^^^ expected bool, found &bool
+ | ^^^^^^^^^^
+ | |
+ | expected bool, found &bool
+ | help: consider removing the borrow: `let 0 = 0`
|
= note: expected type `bool`
found type `&bool`
--- /dev/null
+#[link(name="foo")]
+extern {
+ #[link_ordinal(42)]
+ //~^ ERROR: the `#[link_ordinal]` attribute is an experimental feature
+ fn foo();
+}
+
+fn main() {}
--- /dev/null
+error[E0658]: the `#[link_ordinal]` attribute is an experimental feature
+ --> $DIR/feature-gate-raw-dylib-2.rs:3:5
+ |
+LL | #[link_ordinal(42)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/58713
+ = help: add `#![feature(raw_dylib)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#[link(name="foo", kind="raw-dylib")]
+//~^ ERROR: kind="raw-dylib" is unstable
+extern {}
+
+fn main() {}
--- /dev/null
+error[E0658]: kind="raw-dylib" is unstable
+ --> $DIR/feature-gate-raw-dylib.rs:1:1
+ |
+LL | #[link(name="foo", kind="raw-dylib")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: for more information, see https://github.com/rust-lang/rust/issues/58713
+ = help: add `#![feature(raw_dylib)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![feature(raw_dylib)]
+//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash
+
+#[link(name="foo")]
+extern {
+ #[link_name="foo"]
+ #[link_ordinal(42)]
+ //~^ ERROR cannot use `#[link_name]` with `#[link_ordinal]`
+ fn foo();
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash
+ --> $DIR/link-ordinal-and-name.rs:1:12
+ |
+LL | #![feature(raw_dylib)]
+ | ^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: cannot use `#[link_name]` with `#[link_ordinal]`
+ --> $DIR/link-ordinal-and-name.rs:7:5
+ |
+LL | #[link_ordinal(42)]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(raw_dylib)]
+//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash
+
+#[link(name="foo")]
+extern {
+ #[link_ordinal("JustMonika")]
+ //~^ ERROR illegal ordinal format in `link_ordinal`
+ fn foo();
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash
+ --> $DIR/link-ordinal-invalid-format.rs:1:12
+ |
+LL | #![feature(raw_dylib)]
+ | ^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: illegal ordinal format in `link_ordinal`
+ --> $DIR/link-ordinal-invalid-format.rs:6:5
+ |
+LL | #[link_ordinal("JustMonika")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: an unsuffixed integer value, e.g., `1`, is expected
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(raw_dylib)]
+//~^ WARN the feature `raw_dylib` is incomplete and may cause the compiler to crash
+
+#[link(name="foo")]
+extern {
+ #[link_ordinal(18446744073709551616)]
+ //~^ ERROR ordinal value in `link_ordinal` is too large: `18446744073709551616`
+ fn foo();
+}
+
+fn main() {}
--- /dev/null
+warning: the feature `raw_dylib` is incomplete and may cause the compiler to crash
+ --> $DIR/link-ordinal-too-large.rs:1:12
+ |
+LL | #![feature(raw_dylib)]
+ | ^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: ordinal value in `link_ordinal` is too large: `18446744073709551616`
+ --> $DIR/link-ordinal-too-large.rs:6:5
+ |
+LL | #[link_ordinal(18446744073709551616)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the value may not exceed `std::usize::MAX`
+
+error: aborting due to previous error
+
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
#![allow(dead_code, unreachable_code)]
// aux-build:sepcomp_lib.rs
// compile-flags: -C lto -g
-// ignore-asmjs wasm2js does not support source maps yet
// no-prefer-dynamic
extern crate sepcomp_lib;
fn bar(&self) -> i32 { 0 }
}
-impl<T> Bar for T {} // use the provided method
+impl<T> Bar for T {
+ default fn bar(&self) -> i32 { 0 }
+}
impl Bar for i32 {
fn bar(&self) -> i32 { 1 }
fn next(&mut self) -> Option<T> {
unimplemented!()
}
+
+ default fn count(self) -> usize where Self: Sized {
+ self.fold(0, |cnt, _| cnt + 1)
+ }
}
impl<'a, I, T: 'a> Iterator for Cloned<I>
--- /dev/null
+#![feature(specialization, associated_type_defaults)]
+
+// Test that attempting to override a non-default method or one not in the
+// parent impl causes an error.
+
+trait Foo {
+ type Ty = ();
+ const CONST: u8 = 123;
+ fn foo(&self) -> bool { true }
+}
+
+// Specialization tree for Foo:
+//
+// Box<T> Vec<T>
+// / \ / \
+// Box<i32> Box<i64> Vec<()> Vec<bool>
+
+impl<T> Foo for Box<T> {
+ type Ty = bool;
+ const CONST: u8 = 0;
+ fn foo(&self) -> bool { false }
+}
+
+// Allowed
+impl Foo for Box<i32> {}
+
+// Can't override a non-`default` fn
+impl Foo for Box<i64> {
+ type Ty = Vec<()>;
+//~^ error: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
+ const CONST: u8 = 42;
+//~^ error: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
+ fn foo(&self) -> bool { true }
+//~^ error: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
+}
+
+
+// Doesn't mention the item = provided body/value is used and the method is final.
+impl<T> Foo for Vec<T> {}
+
+// Allowed
+impl Foo for Vec<()> {}
+
+impl Foo for Vec<bool> {
+ type Ty = Vec<()>;
+//~^ error: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
+ const CONST: u8 = 42;
+//~^ error: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
+ fn foo(&self) -> bool { true }
+//~^ error: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
+}
+
+fn main() {}
--- /dev/null
+error[E0520]: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:29:5
+ |
+LL | / impl<T> Foo for Box<T> {
+LL | | type Ty = bool;
+LL | | const CONST: u8 = 0;
+LL | | fn foo(&self) -> bool { false }
+LL | | }
+ | |_- parent `impl` is here
+...
+LL | type Ty = Vec<()>;
+ | ^^^^^^^^^^^^^^^^^^ cannot specialize default item `Ty`
+ |
+ = note: to specialize, `Ty` in the parent `impl` must be marked `default`
+
+error[E0520]: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:31:5
+ |
+LL | / impl<T> Foo for Box<T> {
+LL | | type Ty = bool;
+LL | | const CONST: u8 = 0;
+LL | | fn foo(&self) -> bool { false }
+LL | | }
+ | |_- parent `impl` is here
+...
+LL | const CONST: u8 = 42;
+ | ^^^^^^^^^^^^^^^^^^^^^ cannot specialize default item `CONST`
+ |
+ = note: to specialize, `CONST` in the parent `impl` must be marked `default`
+
+error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:33:5
+ |
+LL | / impl<T> Foo for Box<T> {
+LL | | type Ty = bool;
+LL | | const CONST: u8 = 0;
+LL | | fn foo(&self) -> bool { false }
+LL | | }
+ | |_- parent `impl` is here
+...
+LL | fn foo(&self) -> bool { true }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot specialize default item `foo`
+ |
+ = note: to specialize, `foo` in the parent `impl` must be marked `default`
+
+error[E0520]: `Ty` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:45:5
+ |
+LL | impl<T> Foo for Vec<T> {}
+ | ------------------------- parent `impl` is here
+...
+LL | type Ty = Vec<()>;
+ | ^^^^^^^^^^^^^^^^^^ cannot specialize default item `Ty`
+ |
+ = note: to specialize, `Ty` in the parent `impl` must be marked `default`
+
+error[E0520]: `CONST` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:47:5
+ |
+LL | impl<T> Foo for Vec<T> {}
+ | ------------------------- parent `impl` is here
+...
+LL | const CONST: u8 = 42;
+ | ^^^^^^^^^^^^^^^^^^^^^ cannot specialize default item `CONST`
+ |
+ = note: to specialize, `CONST` in the parent `impl` must be marked `default`
+
+error[E0520]: `foo` specializes an item from a parent `impl`, but that item is not marked `default`
+ --> $DIR/non-defaulted-item-fail.rs:49:5
+ |
+LL | impl<T> Foo for Vec<T> {}
+ | ------------------------- parent `impl` is here
+...
+LL | fn foo(&self) -> bool { true }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot specialize default item `foo`
+ |
+ = note: to specialize, `foo` in the parent `impl` must be marked `default`
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0520`.
// / \
// Vec<i32> $Vec<i64>
-// use the provided method
-impl<T> Bar for T {}
+impl<T> Bar for T {
+ default fn bar(&self) -> i32 { 0 }
+}
impl Bar for i32 {
fn bar(&self) -> i32 { 1 }
--- /dev/null
+trait Trait {}
+
+struct S;
+
+impl<'a> Trait for &'a mut S {}
+
+fn foo<X: Trait>(_: X) {}
+
+
+fn main() {
+ let s = S;
+ foo(&s); //~ ERROR the trait bound `&S: Trait` is not satisfied
+ foo(s); //~ ERROR the trait bound `S: Trait` is not satisfied
+}
--- /dev/null
+error[E0277]: the trait bound `&S: Trait` is not satisfied
+ --> $DIR/imm-ref-trait-object-literal.rs:12:7
+ |
+LL | fn foo<X: Trait>(_: X) {}
+ | --- ----- required by this bound in `foo`
+...
+LL | foo(&s);
+ | -^
+ | |
+ | the trait `Trait` is not implemented for `&S`
+ | help: consider changing this borrow's mutability: `&mut`
+ |
+ = help: the following implementations were found:
+ <&'a mut S as Trait>
+
+error[E0277]: the trait bound `S: Trait` is not satisfied
+ --> $DIR/imm-ref-trait-object-literal.rs:13:7
+ |
+LL | fn foo<X: Trait>(_: X) {}
+ | --- ----- required by this bound in `foo`
+...
+LL | foo(s);
+ | ^ the trait `Trait` is not implemented for `S`
+ |
+ = help: the following implementations were found:
+ <&'a mut S as Trait>
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+fn test(t: &dyn Iterator<Item=&u64>) -> u64 {
+ t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object
+}
+
+fn main() {
+ let array = [0u64];
+ test(&mut array.iter());
+}
--- /dev/null
+error: the `min` method cannot be invoked on a trait object
+ --> $DIR/imm-ref-trait-object.rs:2:8
+ |
+LL | t.min().unwrap()
+ | ^^^
+ |
+ = note: you need `&mut dyn std::iter::Iterator<Item = &u64>` instead of `&dyn std::iter::Iterator<Item = &u64>`
+
+error: aborting due to previous error
+
| ^^^ the trait `std::convert::From<std::string::String>` is not implemented for `&str`
|
= note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix
+ = note: `std::convert::From<std::string::String>` is implemented for `&mut str`, but not for `&str`
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String`
error: aborting due to previous error
--- /dev/null
+use std::env::args;
+use std::fs::File;
+use std::io::{stdout, Write, BufWriter};
+
+fn main() {
+ let mut args = args();
+ let _ = args.next();
+ let dest = args.next();
+
+ let h1; let h2; let h3;
+
+ let fp: &dyn Write = match dest {
+ Some(path) => { h1 = File::create(path).unwrap(); &h1 },
+ None => { h2 = stdout(); h3 = h2.lock(); &h3 }
+ };
+
+ let fp = BufWriter::new(fp);
+ //~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+ //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+ //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+
+ writeln!(fp, "hello world").unwrap(); //~ ERROR no method named `write_fmt` found for type
+}
--- /dev/null
+error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+ --> $DIR/mut-borrow-needed-by-trait.rs:17:29
+ |
+LL | let fp = BufWriter::new(fp);
+ | ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
+ |
+ = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
+ = note: required by `std::io::BufWriter::<W>::new`
+
+error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+ --> $DIR/mut-borrow-needed-by-trait.rs:17:14
+ |
+LL | let fp = BufWriter::new(fp);
+ | ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
+ |
+ = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
+ = note: required by `std::io::BufWriter`
+
+error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
+ --> $DIR/mut-borrow-needed-by-trait.rs:17:14
+ |
+LL | let fp = BufWriter::new(fp);
+ | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
+ |
+ = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
+ = note: required by `std::io::BufWriter`
+
+error[E0599]: no method named `write_fmt` found for type `std::io::BufWriter<&dyn std::io::Write>` in the current scope
+ --> $DIR/mut-borrow-needed-by-trait.rs:22:5
+ |
+LL | writeln!(fp, "hello world").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `std::io::BufWriter<&dyn std::io::Write>`
+ |
+ = note: the method `write_fmt` exists but the following trait bounds were not satisfied:
+ `std::io::BufWriter<&dyn std::io::Write> : std::io::Write`
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0277, E0599.
+For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+fn foo1(s: &str) {
+ s.as_str();
+ //~^ ERROR no method named `as_str` found for type `&str` in the current scope
+}
+
+fn foo2<'a>(s: &'a str) {
+ s.as_str();
+ //~^ ERROR no method named `as_str` found for type `&'a str` in the current scope
+}
+
+fn foo3(s: &mut str) {
+ s.as_str();
+ //~^ ERROR no method named `as_str` found for type `&mut str` in the current scope
+}
+
+fn foo4(s: &&str) {
+ s.as_str();
+ //~^ ERROR no method named `as_str` found for type `&&str` in the current scope
+}
+
+fn main() {}
--- /dev/null
+error[E0599]: no method named `as_str` found for type `&str` in the current scope
+ --> $DIR/remove-as_str.rs:2:7
+ |
+LL | s.as_str();
+ | -^^^^^^-- help: remove this method call
+
+error[E0599]: no method named `as_str` found for type `&'a str` in the current scope
+ --> $DIR/remove-as_str.rs:7:7
+ |
+LL | s.as_str();
+ | -^^^^^^-- help: remove this method call
+
+error[E0599]: no method named `as_str` found for type `&mut str` in the current scope
+ --> $DIR/remove-as_str.rs:12:7
+ |
+LL | s.as_str();
+ | -^^^^^^-- help: remove this method call
+
+error[E0599]: no method named `as_str` found for type `&&str` in the current scope
+ --> $DIR/remove-as_str.rs:17:7
+ |
+LL | s.as_str();
+ | -^^^^^^-- help: remove this method call
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+use std::cell::RefCell;
+
+struct HasAssocMethod;
+
+impl HasAssocMethod {
+ fn hello() {}
+}
+fn main() {
+ let shared_state = RefCell::new(HasAssocMethod);
+ let state = shared_state.borrow_mut();
+ state.hello();
+ //~^ ERROR no method named `hello` found for type `std::cell::RefMut<'_, HasAssocMethod>`
+}
--- /dev/null
+error[E0599]: no method named `hello` found for type `std::cell::RefMut<'_, HasAssocMethod>` in the current scope
+ --> $DIR/suggest-assoc-fn-call-with-turbofish-through-deref.rs:11:11
+ |
+LL | state.hello();
+ | ------^^^^^
+ | | |
+ | | this is an associated function, not a method
+ | help: use associated function syntax instead: `HasAssocMethod::hello`
+ |
+ = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+note: the candidate is defined in an impl for the type `HasAssocMethod`
+ --> $DIR/suggest-assoc-fn-call-with-turbofish-through-deref.rs:6:5
+ |
+LL | fn hello() {}
+ | ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+struct GenericAssocMethod<T>(T);
+
+impl<T> GenericAssocMethod<T> {
+ fn default_hello() {}
+}
+
+fn main() {
+ let x = GenericAssocMethod(33i32);
+ x.default_hello();
+ //~^ ERROR no method named `default_hello` found for type `GenericAssocMethod<i32>`
+}
--- /dev/null
+error[E0599]: no method named `default_hello` found for type `GenericAssocMethod<i32>` in the current scope
+ --> $DIR/suggest-assoc-fn-call-with-turbofish.rs:9:7
+ |
+LL | struct GenericAssocMethod<T>(T);
+ | -------------------------------- method `default_hello` not found for this
+...
+LL | x.default_hello();
+ | --^^^^^^^^^^^^^
+ | | |
+ | | this is an associated function, not a method
+ | help: use associated function syntax instead: `GenericAssocMethod::<i32>::default_hello`
+ |
+ = note: found the following associated functions; to be used as methods, functions must have a `self` parameter
+note: the candidate is defined in an impl for the type `GenericAssocMethod<_>`
+ --> $DIR/suggest-assoc-fn-call-with-turbofish.rs:4:5
+ |
+LL | fn default_hello() {}
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: --test
#![feature(allow_fail)]
// run-pass
-// ignore-emscripten compiled with panic=abort by default
+// ignore-wasm32-bare compiled with panic=abort by default
// compile-flags: --test
#[test]
#[should_panic(expected = "foo")]
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
// This is a regression test for issue #17021.
//
// compile-flags: -g
-// ignore-asmjs wasm2js does not support source maps yet
use std::ptr;
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0495`.
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0312, E0478.
+Some errors have detailed explanations: E0312, E0478, E0495.
For more information about an error, try `rustc --explain E0312`.
-Subproject commit 8b0561d68f12eeb1d72e07ceef464ebf6032a1bc
+Subproject commit a429e8cc4614a46a86322a0777a477e2baa83f1c
-Subproject commit 648e5b90b49af483d07caa8e413473a4517853d6
+Subproject commit 5cb983338e924ec85898880d60e65f2a1291b7be
if config.target == "wasm32-unknown-unknown" && config.parse_check_run_results(ln) {
props.ignore = Ignore::Ignore;
}
- // FIXME: Re-enable run-fail once panics are handled correctly
- if config.target.contains("emscripten") && config.mode == common::RunFail {
- props.ignore = Ignore::Ignore;
- }
+
}
if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoGdbLldb) &&
println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
let file = fs::read_to_string(entry.path()).unwrap()
.replace("syntax::register_diagnostics!", "register_diagnostics!");
- let contents = format!("(|| {{\n{}\n}})();", file);
+ let contents = format!("(|| {{\n{}\n}})()", file);
fs::write(&out_dir.join(&format!("error_{}.rs", idx)), &contents).unwrap();
Crate("crc32fast"),
Crate("crossbeam-deque"),
Crate("crossbeam-epoch"),
+ Crate("crossbeam-queue"),
Crate("crossbeam-utils"),
Crate("datafrog"),
Crate("dlmalloc"),
--- /dev/null
+//! Checks that all error codes have at least one test to prevent having error
+//! codes that are silently not thrown by the compiler anymore.
+
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::path::Path;
+
+// A few of those error codes can't be tested but all the others can and *should* be tested!
+const WHITELIST: &[&str] = &[
+ "E0183",
+ "E0227",
+ "E0279",
+ "E0280",
+ "E0311",
+ "E0313",
+ "E0314",
+ "E0315",
+ "E0377",
+ "E0456",
+ "E0461",
+ "E0462",
+ "E0464",
+ "E0465",
+ "E0472",
+ "E0473",
+ "E0474",
+ "E0475",
+ "E0476",
+ "E0479",
+ "E0480",
+ "E0481",
+ "E0482",
+ "E0483",
+ "E0484",
+ "E0485",
+ "E0486",
+ "E0487",
+ "E0488",
+ "E0489",
+ "E0514",
+ "E0519",
+ "E0523",
+ "E0526",
+ "E0554",
+ "E0570",
+ "E0629",
+ "E0630",
+ "E0640",
+ "E0717",
+ "E0727",
+ "E0729",
+];
+
+fn extract_error_codes(f: &str, error_codes: &mut HashMap<String, bool>) {
+ let mut reached_no_explanation = false;
+ let mut last_error_code = None;
+
+ for line in f.lines() {
+ let s = line.trim();
+ if s.starts_with('E') && s.ends_with(": r##\"") {
+ if let Some(err_code) = s.splitn(2, ':').next() {
+ let err_code = err_code.to_owned();
+ last_error_code = Some(err_code.clone());
+ if !error_codes.contains_key(&err_code) {
+ error_codes.insert(err_code, false);
+ }
+ }
+ } else if s.starts_with("```") && s.contains("compile_fail") && s.contains('E') {
+ if let Some(err_code) = s.splitn(2, 'E').skip(1).next() {
+ if let Some(err_code) = err_code.splitn(2, ',').next() {
+ let nb = error_codes.entry(format!("E{}", err_code)).or_insert(false);
+ *nb = true;
+ }
+ }
+ } else if s == ";" {
+ reached_no_explanation = true;
+ } else if reached_no_explanation && s.starts_with('E') {
+ if let Some(err_code) = s.splitn(2, ',').next() {
+ let err_code = err_code.to_owned();
+ if !error_codes.contains_key(&err_code) { // this check should *never* fail!
+ error_codes.insert(err_code, false);
+ }
+ }
+ } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
+ if let Some(last) = last_error_code {
+ error_codes.get_mut(&last).map(|x| *x = true);
+ }
+ last_error_code = None;
+ }
+ }
+}
+
+fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
+ for line in f.lines() {
+ let s = line.trim();
+ if s.starts_with("error[E") || s.starts_with("warning[E") {
+ if let Some(err_code) = s.splitn(2, ']').next() {
+ if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
+ let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
+ *nb = true;
+ }
+ }
+ }
+ }
+}
+
+pub fn check(path: &Path, bad: &mut bool) {
+ println!("Checking which error codes lack tests...");
+ let mut error_codes: HashMap<String, bool> = HashMap::new();
+ super::walk(path,
+ &mut |path| super::filter_dirs(path),
+ &mut |entry, contents| {
+ let file_name = entry.file_name();
+ if file_name == "error_codes.rs" {
+ extract_error_codes(contents, &mut error_codes);
+ } else if entry.path().extension() == Some(OsStr::new("stderr")) {
+ extract_error_codes_from_tests(contents, &mut error_codes);
+ }
+ });
+ println!("Found {} error codes", error_codes.len());
+
+ let mut errors = Vec::new();
+ for (err_code, nb) in &error_codes {
+ if !*nb && !WHITELIST.contains(&err_code.as_str()) {
+ errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
+ }
+ }
+ errors.sort();
+ for err in &errors {
+ eprintln!("{}", err);
+ }
+ println!("Found {} error codes with no tests", errors.len());
+ if !errors.is_empty() {
+ *bad = true;
+ }
+ println!("Done!");
+}
pub mod ui_tests;
pub mod unit_tests;
pub mod unstable_book;
+pub mod error_codes_check;
fn filter_dirs(path: &Path) -> bool {
let skip = [
deps::check_whitelist(&path, &cargo, &mut bad);
extdeps::check(&path, &mut bad);
ui_tests::check(&path, &mut bad);
+ error_codes_check::check(&path, &mut bad);
if bad {
eprintln!("some tidy checks failed");
"C-*", "A-*", "E-*", "NLL-*", "O-*", "S-*", "T-*", "WG-*", "F-*",
"requires-nightly",
# I-* without I-nominated
- "I-compilemem", "I-compiletime", "I-crash", "I-hang", "I-ICE", "I-slow",
+ "I-*", "!I-nominated",
+ "AsyncAwait-OnDeck",
]
[assign]