diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e864172e81332..e4870aa6a8980 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ links to the major sections: * [Feature Requests](#feature-requests) * [Bug Reports](#bug-reports) +* [The Build System](#the-build-system) * [Pull Requests](#pull-requests) * [Writing Documentation](#writing-documentation) * [Issue Triage](#issue-triage) @@ -77,6 +78,66 @@ to do this is to invoke `rustc` like this: $ RUST_BACKTRACE=1 rustc ... ``` +## The Build System + +Rust's build system allows you to bootstrap the compiler, run tests & +benchmarks, generate documentation, install a fresh build of Rust, and more. +It's your best friend when working on Rust, allowing you to compile & test +your contributions before submission. + +All the configuration for the build system lives in [the `mk` directory][mkdir] +in the project root. It can be hard to follow in places, as it uses some +advanced Make features which make for some challenging reading. If you have +questions on the build system internals, try asking in +[`#rust-internals`][pound-rust-internals]. + +[mkdir]: https://github.com/rust-lang/rust/tree/master/mk/ + +### Configuration + +Before you can start building the compiler you need to configure the build for +your system. In most cases, that will just mean using the defaults provided +for Rust. Configuring involves invoking the `configure` script in the project +root. + +``` +./configure +``` + +There are large number of options accepted by this script to alter the +configuration used later in the build process. Some options to note: + +- `--enable-debug` - Build a debug version of the compiler (disables optimizations) +- `--enable-optimize` - Enable optimizations (can be used with `--enable-debug` + to make a debug build with optimizations) +- `--disable-valgrind-rpass` - Don't run tests with valgrind +- `--enable-clang` - Prefer clang to gcc for building dependencies (e.g., LLVM) +- `--enable-ccache` - Invoke clang/gcc with ccache to re-use object files between builds +- `--enable-compiler-docs` - Build compiler documentation + +To see a full list of options, run `./configure --help`. + +### Useful Targets + +Some common make targets are: + +- `make rustc-stage1` - build up to (and including) the first stage. For most + cases we don't need to build the stage2 compiler, so we can save time by not + building it. The stage1 compiler is a fully functioning compiler and + (probably) will be enough to determine if your change works as expected. +- `make check` - build the full compiler & run all tests (takes a while). This + is what gets run by the continuous integration system against your pull + request. You should run this before submitting to make sure your tests pass + & everything builds in the correct manner. +- `make check-stage1-std NO_REBUILD=1` - test the standard library without + rebuilding the entire compiler +- `make check TESTNAME=.rs` - Run a single test file +- `make check-stage1-rpass TESTNAME=.rs` - Run a single + rpass test with the stage1 compiler (this will be quicker than running the + command above as we only build the stage1 compiler, not the entire thing). + You can also leave off the `-rpass` to run all stage1 test types. +- `make check-stage1-coretest` - Run stage1 tests in `libcore`. + ## Pull Requests Pull requests are the primary mechanism we use to change Rust. GitHub itself diff --git a/configure b/configure index f284d13ee3bbd..2693e9bcd236a 100755 --- a/configure +++ b/configure @@ -1409,6 +1409,7 @@ do make_dir $h/test/debuginfo-gdb make_dir $h/test/debuginfo-lldb make_dir $h/test/codegen + make_dir $h/test/codegen-units make_dir $h/test/rustdoc done diff --git a/mk/cfg/armv7-unknown-linux-gnueabihf.mk b/mk/cfg/armv7-unknown-linux-gnueabihf.mk new file mode 100644 index 0000000000000..c676bfb1289fd --- /dev/null +++ b/mk/cfg/armv7-unknown-linux-gnueabihf.mk @@ -0,0 +1,26 @@ +# armv7-unknown-linux-gnueabihf configuration +CROSS_PREFIX_armv7-unknown-linux-gnueabihf=armv7-unknown-linux-gnueabihf- +CC_armv7-unknown-linux-gnueabihf=gcc +CXX_armv7-unknown-linux-gnueabihf=g++ +CPP_armv7-unknown-linux-gnueabihf=gcc -E +AR_armv7-unknown-linux-gnueabihf=ar +CFG_LIB_NAME_armv7-unknown-linux-gnueabihf=lib$(1).so +CFG_STATIC_LIB_NAME_armv7-unknown-linux-gnueabihf=lib$(1).a +CFG_LIB_GLOB_armv7-unknown-linux-gnueabihf=lib$(1)-*.so +CFG_LIB_DSYM_GLOB_armv7-unknown-linux-gnueabihf=lib$(1)-*.dylib.dSYM +CFG_JEMALLOC_CFLAGS_armv7-unknown-linux-gnueabihf := -D__arm__ $(CFLAGS) +CFG_GCCISH_CFLAGS_armv7-unknown-linux-gnueabihf := -Wall -g -fPIC -D__arm__ $(CFLAGS) +CFG_GCCISH_CXXFLAGS_armv7-unknown-linux-gnueabihf := -fno-rtti $(CXXFLAGS) +CFG_GCCISH_LINK_FLAGS_armv7-unknown-linux-gnueabihf := -shared -fPIC -g +CFG_GCCISH_DEF_FLAG_armv7-unknown-linux-gnueabihf := -Wl,--export-dynamic,--dynamic-list= +CFG_LLC_FLAGS_armv7-unknown-linux-gnueabihf := +CFG_INSTALL_NAME_ar,-unknown-linux-gnueabihf = +CFG_EXE_SUFFIX_armv7-unknown-linux-gnueabihf := +CFG_WINDOWSY_armv7-unknown-linux-gnueabihf := +CFG_UNIXY_armv7-unknown-linux-gnueabihf := 1 +CFG_LDPATH_armv7-unknown-linux-gnueabihf := +CFG_RUN_armv7-unknown-linux-gnueabihf=$(2) +CFG_RUN_TARG_armv7-unknown-linux-gnueabihf=$(call CFG_RUN_armv7-unknown-linux-gnueabihf,,$(2)) +RUSTC_FLAGS_armv7-unknown-linux-gnueabihf := -C target-feature=+v7,+vfp2,+neon +RUSTC_CROSS_FLAGS_armv7-unknown-linux-gnueabihf := +CFG_GNU_TRIPLE_armv7-unknown-linux-gnueabihf := armv7-unknown-linux-gnueabihf diff --git a/mk/cfg/armv7s-apple-ios.mk b/mk/cfg/armv7s-apple-ios.mk index 96ca07648949f..efad43d25627d 100644 --- a/mk/cfg/armv7s-apple-ios.mk +++ b/mk/cfg/armv7s-apple-ios.mk @@ -14,8 +14,8 @@ CFG_LIB_GLOB_armv7s-apple-ios = lib$(1)-*.a CFG_INSTALL_ONLY_RLIB_armv7s-apple-ios = 1 CFG_STATIC_LIB_NAME_armv7s-apple-ios=lib$(1).a CFG_LIB_DSYM_GLOB_armv7s-apple-ios = lib$(1)-*.a.dSYM -CFG_JEMALLOC_CFLAGS_armv7s-apple-ios := -arch armv7s -mfpu=vfp4 $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios) -CFG_GCCISH_CFLAGS_armv7s-apple-ios := -Wall -Werror -g -fPIC $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios) -mfpu=vfp4 -arch armv7s +CFG_JEMALLOC_CFLAGS_armv7s-apple-ios := -arch armv7s $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios) +CFG_GCCISH_CFLAGS_armv7s-apple-ios := -Wall -Werror -g -fPIC $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios) -arch armv7s CFG_GCCISH_CXXFLAGS_armv7s-apple-ios := -fno-rtti $(CFG_IOS_SDK_FLAGS_armv7s-apple-ios) -I$(CFG_IOS_SDK_armv7s-apple-ios)/usr/include/c++/4.2.1 CFG_GCCISH_LINK_FLAGS_armv7s-apple-ios := -lpthread -syslibroot $(CFG_IOS_SDK_armv7s-apple-ios) -Wl,-no_compact_unwind CFG_GCCISH_DEF_FLAG_armv7s-apple-ios := -Wl,-exported_symbols_list, diff --git a/mk/cfg/powerpc64-unknown-linux-gnu.mk b/mk/cfg/powerpc64-unknown-linux-gnu.mk index a9e8585ad6db5..cf49c711ba61f 100644 --- a/mk/cfg/powerpc64-unknown-linux-gnu.mk +++ b/mk/cfg/powerpc64-unknown-linux-gnu.mk @@ -1,5 +1,5 @@ # powerpc64-unknown-linux-gnu configuration -CROSS_PREFIX_powerpc64-unknown-linux-gnu=powerpc64-linux-gnu- +CROSS_PREFIX_powerpc64-unknown-linux-gnu=powerpc-linux-gnu- CC_powerpc64-unknown-linux-gnu=$(CC) CXX_powerpc64-unknown-linux-gnu=$(CXX) CPP_powerpc64-unknown-linux-gnu=$(CPP) diff --git a/mk/tests.mk b/mk/tests.mk index b3f7278ad62cc..5ca27c489ed9a 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -310,6 +310,7 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-gdb-exec \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-lldb-exec \ check-stage$(1)-T-$(2)-H-$(3)-codegen-exec \ + check-stage$(1)-T-$(2)-H-$(3)-codegen-units-exec \ check-stage$(1)-T-$(2)-H-$(3)-doc-exec \ check-stage$(1)-T-$(2)-H-$(3)-pretty-exec @@ -473,6 +474,7 @@ DEBUGINFO_GDB_RS := $(wildcard $(S)src/test/debuginfo/*.rs) DEBUGINFO_LLDB_RS := $(wildcard $(S)src/test/debuginfo/*.rs) CODEGEN_RS := $(wildcard $(S)src/test/codegen/*.rs) CODEGEN_CC := $(wildcard $(S)src/test/codegen/*.cc) +CODEGEN_UNITS_RS := $(wildcard $(S)src/test/codegen-units/*.rs) RUSTDOCCK_RS := $(wildcard $(S)src/test/rustdoc/*.rs) RPASS_TESTS := $(RPASS_RS) @@ -488,6 +490,7 @@ PRETTY_TESTS := $(PRETTY_RS) DEBUGINFO_GDB_TESTS := $(DEBUGINFO_GDB_RS) DEBUGINFO_LLDB_TESTS := $(DEBUGINFO_LLDB_RS) CODEGEN_TESTS := $(CODEGEN_RS) $(CODEGEN_CC) +CODEGEN_UNITS_TESTS := $(CODEGEN_UNITS_RS) RUSTDOCCK_TESTS := $(RUSTDOCCK_RS) CTEST_SRC_BASE_rpass = run-pass @@ -550,6 +553,11 @@ CTEST_BUILD_BASE_codegen = codegen CTEST_MODE_codegen = codegen CTEST_RUNTOOL_codegen = $(CTEST_RUNTOOL) +CTEST_SRC_BASE_codegen-units = codegen-units +CTEST_BUILD_BASE_codegen-units = codegen-units +CTEST_MODE_codegen-units = codegen-units +CTEST_RUNTOOL_codegen-units = $(CTEST_RUNTOOL) + CTEST_SRC_BASE_rustdocck = rustdoc CTEST_BUILD_BASE_rustdocck = rustdoc CTEST_MODE_rustdocck = rustdoc @@ -673,6 +681,7 @@ CTEST_DEPS_debuginfo-lldb_$(1)-T-$(2)-H-$(3) = $$(DEBUGINFO_LLDB_TESTS) \ $(S)src/etc/lldb_batchmode.py \ $(S)src/etc/lldb_rust_formatters.py CTEST_DEPS_codegen_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_TESTS) +CTEST_DEPS_codegen-units_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_UNITS_TESTS) CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \ $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \ $(S)src/etc/htmldocck.py @@ -739,7 +748,7 @@ endif endef CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \ - bench debuginfo-gdb debuginfo-lldb codegen rustdocck + bench debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck $(foreach host,$(CFG_HOST), \ $(eval $(foreach target,$(CFG_TARGET), \ @@ -917,6 +926,7 @@ TEST_GROUPS = \ debuginfo-gdb \ debuginfo-lldb \ codegen \ + codegen-units \ doc \ $(foreach docname,$(DOC_NAMES),doc-$(docname)) \ pretty \ diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index eb6c29eefbe78..e66094dc3954b 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -25,6 +25,7 @@ pub enum Mode { DebugInfoLldb, Codegen, Rustdoc, + CodegenUnits } impl FromStr for Mode { @@ -41,6 +42,7 @@ impl FromStr for Mode { "debuginfo-gdb" => Ok(DebugInfoGdb), "codegen" => Ok(Codegen), "rustdoc" => Ok(Rustdoc), + "codegen-units" => Ok(CodegenUnits), _ => Err(()), } } @@ -59,6 +61,7 @@ impl fmt::Display for Mode { DebugInfoLldb => "debuginfo-lldb", Codegen => "codegen", Rustdoc => "rustdoc", + CodegenUnits => "codegen-units", }, f) } } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 459b43b4ffe5d..c7561248eb7fb 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -10,7 +10,7 @@ use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; -use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc}; +use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; use errors; use header::TestProps; use header; @@ -18,6 +18,7 @@ use procsrv; use util::logv; use std::env; +use std::collections::HashSet; use std::fmt; use std::fs::{self, File}; use std::io::BufReader; @@ -56,6 +57,7 @@ pub fn run(config: Config, testfile: &Path) { DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile), Codegen => run_codegen_test(&config, &props, &testfile), Rustdoc => run_rustdoc_test(&config, &props, &testfile), + CodegenUnits => run_codegen_units_test(&config, &props, &testfile), } } @@ -1747,3 +1749,44 @@ fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) { fatal_proc_rec("htmldocck failed!", &res); } } + +fn run_codegen_units_test(config: &Config, props: &TestProps, testfile: &Path) { + let proc_res = compile_test(config, props, testfile); + + if !proc_res.status.success() { + fatal_proc_rec("compilation failed!", &proc_res); + } + + check_no_compiler_crash(&proc_res); + + let prefix = "TRANS_ITEM "; + + let actual: HashSet = proc_res + .stdout + .lines() + .filter(|line| line.starts_with(prefix)) + .map(|s| (&s[prefix.len()..]).to_string()) + .collect(); + + let expected: HashSet = errors::load_errors(testfile) + .iter() + .map(|e| e.msg.trim().to_string()) + .collect(); + + if actual != expected { + let mut missing: Vec<_> = expected.difference(&actual).collect(); + missing.sort(); + + let mut too_much: Vec<_> = actual.difference(&expected).collect(); + too_much.sort(); + + println!("Expected and actual sets of codegen-items differ.\n\ + These items should have been contained but were not:\n\n\ + {}\n\n\ + These items were contained but should not have been:\n\n\ + {}\n\n", + missing.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2), + too_much.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2)); + panic!(); + } +} diff --git a/src/doc/book/crates-and-modules.md b/src/doc/book/crates-and-modules.md index 849c5f1212a57..0c9ed0bf12281 100644 --- a/src/doc/book/crates-and-modules.md +++ b/src/doc/book/crates-and-modules.md @@ -567,10 +567,11 @@ to it as "sayings". Similarly, the first `use` statement pulls in the `ja_greetings` as opposed to simply `greetings`. This can help to avoid ambiguity when importing similarly-named items from different places. -The second `use` statement uses a star glob to bring in _all_ symbols from the -`sayings::japanese::farewells` module. As you can see we can later refer to +The second `use` statement uses a star glob to bring in all public symbols from +the `sayings::japanese::farewells` module. As you can see we can later refer to the Japanese `goodbye` function with no module qualifiers. This kind of glob -should be used sparingly. +should be used sparingly. It’s worth noting that it only imports the public +symbols, even if the code doing the globbing is in the same module. The third `use` statement bears more explanation. It's using "brace expansion" globbing to compress three `use` statements into one (this sort of syntax diff --git a/src/doc/book/macros.md b/src/doc/book/macros.md index f5a0cd5e2c6a3..188abb316ab0b 100644 --- a/src/doc/book/macros.md +++ b/src/doc/book/macros.md @@ -478,9 +478,9 @@ which syntactic form it matches. There are additional rules regarding the next token after a metavariable: -* `expr` variables may only be followed by one of: `=> , ;` -* `ty` and `path` variables may only be followed by one of: `=> , : = > as` -* `pat` variables may only be followed by one of: `=> , = if in` +* `expr` and `stmt` variables may only be followed by one of: `=> , ;` +* `ty` and `path` variables may only be followed by one of: `=> , = | ; : > [ { as where` +* `pat` variables may only be followed by one of: `=> , = | if in` * Other variables may be followed by any token. These rules provide some flexibility for Rust’s syntax to evolve without diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md index f7e32943c638e..6782bdb4985ec 100644 --- a/src/doc/book/syntax-index.md +++ b/src/doc/book/syntax-index.md @@ -2,7 +2,7 @@ ## Keywords -* `as`: primitive casting. See [Casting Between Types (`as`)]. +* `as`: primitive casting, or disambiguating the specific trait containing an item. See [Casting Between Types (`as`)], [Universal Function Call Syntax (Angle-bracket Form)], [Associated Types]. * `break`: break out of loop. See [Loops (Ending Iteration Early)]. * `const`: constant items and constant raw pointers. See [`const` and `static`], [Raw Pointers]. * `continue`: continue to next loop iteration. See [Loops (Ending Iteration Early)]. @@ -115,8 +115,11 @@ * `::path`: path relative to the crate root (*i.e.* an explicitly absolute path). See [Crates and Modules (Re-exporting with `pub use`)]. * `self::path`: path relative to the current module (*i.e.* an explicitly relative path). See [Crates and Modules (Re-exporting with `pub use`)]. * `super::path`: path relative to the parent of the current module. See [Crates and Modules (Re-exporting with `pub use`)]. -* `type::ident`: associated constants, functions, and types. See [Associated Types]. +* `type::ident`, `::ident`: associated constants, functions, and types. See [Associated Types]. * `::…`: associated item for a type which cannot be directly named (*e.g.* `<&T>::…`, `<[T]>::…`, *etc.*). See [Associated Types]. +* `trait::method(…)`: disambiguating a method call by naming the trait which defines it. See [Universal Function Call Syntax]. +* `type::method(…)`: disambiguating a method call by naming the type for which it's defined. See [Universal Function Call Syntax]. +* `::method(…)`: disambiguating a method call by naming the trait _and_ type. See [Universal Function Call Syntax (Angle-bracket Form)]. @@ -132,7 +135,8 @@ * `T: U`: generic parameter `T` constrained to types that implement `U`. See [Traits]. -* `T: 'a`: generic type `T` must outlive lifetime `'a`. +* `T: 'a`: generic type `T` must outlive lifetime `'a`. When we say that a type 'outlives' the lifetime, we mean that it cannot transitively contain any references with lifetimes shorter than `'a`. +* `T : 'static`: The generic type `T` contains no borrowed references other than `'static` ones. * `'b: 'a`: generic lifetime `'b` must outlive lifetime `'a`. * `T: ?Sized`: allow generic type parameter to be a dynamically-sized type. See [Unsized Types (`?Sized`)]. * `'a + trait`, `trait + trait`: compound type constraint. See [Traits (Multiple Trait Bounds)]. @@ -234,6 +238,8 @@ [Traits (`where` clause)]: traits.html#where-clause [Traits (Multiple Trait Bounds)]: traits.html#multiple-trait-bounds [Traits]: traits.html +[Universal Function Call Syntax]: ufcs.html +[Universal Function Call Syntax (Angle-bracket Form)]: ufcs.html#angle-bracket-form [Unsafe]: unsafe.html [Unsized Types (`?Sized`)]: unsized-types.html#sized [Variable Bindings]: variable-bindings.html diff --git a/src/doc/reference.md b/src/doc/reference.md index f0fdae27ac7fb..9c0538e6a3c7a 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -104,7 +104,7 @@ comments (`/** ... */`), are interpreted as a special syntax for `doc` `#[doc="..."]` around the body of the comment, i.e., `/// Foo` turns into `#[doc="Foo"]`. -Line comments beginning with `//!` and block comments `/*! ... !*/` are +Line comments beginning with `//!` and block comments `/*! ... */` are doc comments that apply to the parent of the comment, rather than the item that follows. That is, they are equivalent to writing `#![doc="..."]` around the body of the comment. `//!` comments are usually used to document @@ -2095,7 +2095,7 @@ along with their default settings. [Compiler plugins](book/compiler-plugins.html#lint-plugins) can provide additional lint checks. ```{.ignore} -mod m1 { +pub mod m1 { // Missing documentation is ignored here #[allow(missing_docs)] pub fn undocumented_one() -> i32 { 1 } @@ -2115,9 +2115,9 @@ check on and off: ```{.ignore} #[warn(missing_docs)] -mod m2{ +pub mod m2{ #[allow(missing_docs)] - mod nested { + pub mod nested { // Missing documentation is ignored here pub fn undocumented_one() -> i32 { 1 } @@ -2137,7 +2137,7 @@ that lint check: ```{.ignore} #[forbid(missing_docs)] -mod m3 { +pub mod m3 { // Attempting to toggle warning signals an error here #[allow(missing_docs)] /// Returns 2. diff --git a/src/libcollections/btree/set.rs b/src/libcollections/btree/set.rs index 91fc8d8217f53..1cd50c2dcbe9e 100644 --- a/src/libcollections/btree/set.rs +++ b/src/libcollections/btree/set.rs @@ -12,6 +12,7 @@ // to TreeMap use core::cmp::Ordering::{self, Less, Greater, Equal}; +use core::cmp::{min, max}; use core::fmt::Debug; use core::fmt; use core::iter::{Peekable, FromIterator}; @@ -703,7 +704,9 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for Iter<'a, T> {} +impl<'a, T> ExactSizeIterator for Iter<'a, T> { + fn len(&self) -> usize { self.iter.len() } +} #[stable(feature = "rust1", since = "1.0.0")] @@ -724,7 +727,9 @@ impl DoubleEndedIterator for IntoIter { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter {} +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { self.iter.len() } +} impl<'a, T> Clone for Range<'a, T> { @@ -780,6 +785,12 @@ impl<'a, T: Ord> Iterator for Difference<'a, T> { } } } + + fn size_hint(&self) -> (usize, Option) { + let a_len = self.a.len(); + let b_len = self.b.len(); + (a_len.saturating_sub(b_len), Some(a_len)) + } } impl<'a, T> Clone for SymmetricDifference<'a, T> { @@ -806,6 +817,10 @@ impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { } } } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.a.len() + self.b.len())) + } } impl<'a, T> Clone for Intersection<'a, T> { @@ -842,6 +857,10 @@ impl<'a, T: Ord> Iterator for Intersection<'a, T> { } } } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(min(self.a.len(), self.b.len()))) + } } impl<'a, T> Clone for Union<'a, T> { @@ -868,4 +887,10 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { } } } + + fn size_hint(&self) -> (usize, Option) { + let a_len = self.a.len(); + let b_len = self.b.len(); + (max(a_len, b_len), Some(a_len + b_len)) + } } diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index d9cbc4488fca7..97c12043e7634 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -641,7 +641,7 @@ impl String { Cow::Owned(res) } - /// Decode a UTF-16 encoded vector `v` into a `String`, returning `None` + /// Decode a UTF-16 encoded vector `v` into a `String`, returning `Err` /// if `v` contains any invalid data. /// /// # Examples diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 394f7a975989a..f434053390e54 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -1994,9 +1994,9 @@ impl Ord for VecDeque { impl Hash for VecDeque { fn hash(&self, state: &mut H) { self.len().hash(state); - for elt in self { - elt.hash(state); - } + let (a, b) = self.as_slices(); + Hash::hash_slice(a, state); + Hash::hash_slice(b, state); } } diff --git a/src/libcollectionstest/vec_deque.rs b/src/libcollectionstest/vec_deque.rs index 5f587789bd865..a50886bfdf303 100644 --- a/src/libcollectionstest/vec_deque.rs +++ b/src/libcollectionstest/vec_deque.rs @@ -605,6 +605,25 @@ fn test_hash() { assert!(::hash(&x) == ::hash(&y)); } +#[test] +fn test_hash_after_rotation() { + // test that two deques hash equal even if elements are laid out differently + let len = 28; + let mut ring: VecDeque = (0..len as i32).collect(); + let orig = ring.clone(); + for _ in 0..ring.capacity() { + // shift values 1 step to the right by pop, sub one, push + ring.pop_front(); + for elt in &mut ring { + *elt -= 1; + } + ring.push_back(len - 1); + assert_eq!(::hash(&orig), ::hash(&ring)); + assert_eq!(orig, ring); + assert_eq!(ring, orig); + } +} + #[test] fn test_ord() { let x = VecDeque::new(); diff --git a/src/libcore/hash/mod.rs b/src/libcore/hash/mod.rs index 9ab55620e0aec..b8709e8649a69 100644 --- a/src/libcore/hash/mod.rs +++ b/src/libcore/hash/mod.rs @@ -194,8 +194,8 @@ pub trait Hasher { /// A `BuildHasher` is typically used as a factory for instances of `Hasher` /// which a `HashMap` can then use to hash keys independently. /// -/// Note that for each instance of `BuildHasher` the create hashers should be -/// identical. That is if the same stream of bytes is fed into each hasher the +/// Note that for each instance of `BuildHasher`, the created hashers should be +/// identical. That is, if the same stream of bytes is fed into each hasher, the /// same output will also be generated. #[stable(since = "1.7.0", feature = "build_hasher")] pub trait BuildHasher { diff --git a/src/liblibc b/src/liblibc index af77843345ec6..91ff43c736de6 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit af77843345ec6fc7e51113bfd692138d89024bc0 +Subproject commit 91ff43c736de664f8d3cd351e148c09cdea6731e diff --git a/src/librustc/README.md b/src/librustc/README.md index fd2e4e2fb7700..c24d3d82b2f72 100644 --- a/src/librustc/README.md +++ b/src/librustc/README.md @@ -21,7 +21,7 @@ Rustc consists of a number of crates, including `libsyntax`, (the names and divisions are not set in stone and may change; in general, a finer-grained division of crates is preferable): -- `libsyntax` contains those things concerned purely with syntax – +- [`libsyntax`][libsyntax] contains those things concerned purely with syntax – that is, the AST, parser, pretty-printer, lexer, macro expander, and utilities for traversing ASTs – are in a separate crate called "syntax", whose files are in `./../libsyntax`, where `.` is the @@ -32,32 +32,92 @@ in general, a finer-grained division of crates is preferable): passes, such as the type checker, borrow checker, and so forth. It is the heart of the compiler. -- `librustc_back` contains some very low-level details that are +- [`librustc_back`][back] contains some very low-level details that are specific to different LLVM targets and so forth. -- `librustc_trans` contains the code to convert from Rust IR into LLVM +- [`librustc_trans`][trans] contains the code to convert from Rust IR into LLVM IR, and then from LLVM IR into machine code, as well as the main driver that orchestrates all the other passes and various other bits of miscellany. In general it contains code that runs towards the end of the compilation process. -- `librustc_driver` invokes the compiler from `libsyntax`, then the - analysis phases from `librustc`, and finally the lowering and - codegen passes from `librustc_trans`. +- [`librustc_driver`][driver] invokes the compiler from + [`libsyntax`][libsyntax], then the analysis phases from `librustc`, and + finally the lowering and codegen passes from [`librustc_trans`][trans]. Roughly speaking the "order" of the three crates is as follows: - libsyntax -> librustc -> librustc_trans - | | - +-----------------+-------------------+ - | librustc_driver + | + +-----------------+-------------------+ + | | + libsyntax -> librustc -> librustc_trans -Modules in the rustc crate -========================== - -The rustc crate itself consists of the following submodules +The compiler process: +===================== + +The Rust compiler is comprised of six main compilation phases. + +1. Parsing input +2. Configuration & expanding (cfg rules & syntax extension expansion) +3. Running analysis passes +4. Translation to LLVM +5. LLVM passes +6. Linking + +Phase one is responsible for parsing & lexing the input to the compiler. The +output of this phase is an abstract syntax tree (AST). The AST at this point +includes all macro uses & attributes. This means code which will be later +expanded and/or removed due to `cfg` attributes is still present in this +version of the AST. Parsing abstracts away details about individual files which +have been read into the AST. + +Phase two handles configuration and macro expansion. You can think of this +phase as a function acting on the AST from the previous phase. The input for +this phase is the unexpanded AST from phase one, and the output is an expanded +version of the same AST. This phase will expand all macros & syntax +extensions and will evaluate all `cfg` attributes, potentially removing some +code. The resulting AST will not contain any macros or `macro_use` statements. + +The code for these first two phases is in [`libsyntax`][libsyntax]. + +After this phase, the compiler allocates IDs to each node in the AST +(technically not every node, but most of them). If we are writing out +dependencies, that happens now. + +The third phase is analysis. This is the most complex phase in the compiler, +and makes up much of the code. This phase included name resolution, type +checking, borrow checking, type & lifetime inference, trait selection, method +selection, linting and so on. Most of the error detection in the compiler comes +from this phase (with the exception of parse errors which arise during +parsing). The "output" of this phase is a set of side tables containing +semantic information about the source program. The analysis code is in +[`librustc`][rustc] and some other crates with the `librustc_` prefix. + +The fourth phase is translation. This phase translates the AST (and the side +tables from the previous phase) into LLVM IR (intermediate representation). +This is achieved by calling into the LLVM libraries. The code for this is in +[`librustc_trans`][trans]. + +Phase five runs the LLVM backend. This runs LLVM's optimization passes on the +generated IR and generates machine code resulting in object files. This phase +is not really part of the Rust compiler, as LLVM carries out all the work. +The interface between LLVM and Rust is in [`librustc_llvm`][llvm]. + +The final phase, phase six, links the object files into an executable. This is +again outsourced to other tools and not performed by the Rust compiler +directly. The interface is in [`librustc_back`][back] (which also contains some +things used primarily during translation). + +A module called the driver coordinates all these phases. It handles all the +highest level coordination of compilation from parsing command line arguments +all the way to invoking the linker to produce an executable. + +Modules in the librustc crate +============================= + +The librustc crate itself consists of the following submodules (mostly, but not entirely, in their own directories): - session: options and data that pertain to the compilation session as @@ -71,7 +131,7 @@ The rustc crate itself consists of the following submodules - util: ubiquitous types and helper functions - lib: bindings to LLVM -The entry-point for the compiler is main() in the librustc_driver +The entry-point for the compiler is main() in the [`librustc_driver`][driver] crate. The 3 central data structures: @@ -106,23 +166,9 @@ The 3 central data structures: Each of these is an opaque pointer to an LLVM type, manipulated through the `lib::llvm` interface. - -Control and information flow within the compiler: -------------------------------------------------- - -- main() in lib.rs assumes control on startup. Options are - parsed, platform is detected, etc. - -- `./../libsyntax/parse/parser.rs` parses the input files and produces - an AST that represents the input crate. - -- Multiple middle-end passes (`middle/resolve.rs`, `middle/typeck.rs`) - analyze the semantics of the resulting AST. Each pass generates new - information about the AST and stores it in various environment data - structures. The driver passes environments to each compiler pass - that needs to refer to them. - -- Finally, the `trans` module in `librustc_trans` translates the Rust - AST to LLVM bitcode in a type-directed way. When it's finished - synthesizing LLVM values, rustc asks LLVM to write them out in some - form (`.bc`, `.o`) and possibly run the system linker. +[libsyntax]: https://github.com/rust-lang/rust/tree/master/src/libsyntax/ +[trans]: https://github.com/rust-lang/rust/tree/master/src/librustc_trans/ +[llvm]: https://github.com/rust-lang/rust/tree/master/src/librustc_llvm/ +[back]: https://github.com/rust-lang/rust/tree/master/src/librustc_back/ +[rustc]: https://github.com/rust-lang/rust/tree/master/src/librustc/ +[driver]: https://github.com/rust-lang/rust/tree/master/src/librustc_driver diff --git a/src/librustc/front/map/definitions.rs b/src/librustc/front/map/definitions.rs index e903fcf6a56c2..10c1372cd86e4 100644 --- a/src/librustc/front/map/definitions.rs +++ b/src/librustc/front/map/definitions.rs @@ -196,33 +196,33 @@ impl DefPathData { PositionalField | Field(hir::StructFieldKind::UnnamedField(_)) => { - InternedString::new("") + InternedString::new("{{field}}") } // note that this does not show up in user printouts CrateRoot => { - InternedString::new("") + InternedString::new("{{root}}") } // note that this does not show up in user printouts InlinedRoot(_) => { - InternedString::new("") + InternedString::new("{{inlined-root}}") } Misc => { - InternedString::new("?") + InternedString::new("{{?}}") } ClosureExpr => { - InternedString::new("") + InternedString::new("{{closure}}") } StructCtor => { - InternedString::new("") + InternedString::new("{{constructor}}") } Initializer => { - InternedString::new("") + InternedString::new("{{initializer}}") } } } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 973fd65beb3d8..8355154ad21a5 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -223,6 +223,8 @@ pub trait CrateStore<'tcx> : Any { -> FoundAst<'tcx>; fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId) -> Option>; + fn is_item_mir_available(&self, def: DefId) -> bool; + // This is basically a 1-based range of ints, which is a little // silly - I may fix that. fn crates(&self) -> Vec; @@ -401,6 +403,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { -> FoundAst<'tcx> { unimplemented!() } fn maybe_get_item_mir(&self, tcx: &ty::ctxt<'tcx>, def: DefId) -> Option> { unimplemented!() } + fn is_item_mir_available(&self, def: DefId) -> bool { + unimplemented!() + } // This is basically a 1-based range of ints, which is a little // silly - I may fix that. diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 7010bae7ee5bf..0fd6b933ed528 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -563,7 +563,7 @@ impl<'tcx> ctxt<'tcx> { const_qualif_map: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), cast_kinds: RefCell::new(NodeMap()), - fragment_infos: RefCell::new(DefIdMap()), + fragment_infos: RefCell::new(DefIdMap()) }, f) } } diff --git a/src/librustc/middle/ty/maps.rs b/src/librustc/middle/ty/maps.rs index 7d5276f379ffe..8d8afc199f59e 100644 --- a/src/librustc/middle/ty/maps.rs +++ b/src/librustc/middle/ty/maps.rs @@ -13,7 +13,7 @@ use middle::def_id::DefId; use middle::ty; use std::marker::PhantomData; use std::rc::Rc; -use syntax::attr; +use syntax::{attr, ast}; macro_rules! dep_map_ty { ($ty_name:ident : $node_name:ident ($key:ty) -> $value:ty) => { @@ -42,3 +42,4 @@ dep_map_ty! { InherentImpls: InherentImpls(DefId) -> Rc> } dep_map_ty! { ImplItems: ImplItems(DefId) -> Vec } dep_map_ty! { TraitItems: TraitItems(DefId) -> Rc>> } dep_map_ty! { ReprHints: ReprHints(DefId) -> Rc> } +dep_map_ty! { InlinedClosures: Hir(DefId) -> ast::NodeId } diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 3e71d3ba9f397..0fe39dcb7f9ec 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -25,7 +25,7 @@ use std::{iter, u32}; use std::ops::{Index, IndexMut}; /// Lowered representation of a single function. -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct Mir<'tcx> { /// List of basic blocks. References to basic block use a newtyped index type `BasicBlock` /// that indexes into this vector. @@ -146,7 +146,7 @@ pub enum BorrowKind { // A "variable" is a binding declared by the user as part of the fn // decl, a let, etc. -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct VarDecl<'tcx> { pub mutability: Mutability, pub name: Name, @@ -155,7 +155,7 @@ pub struct VarDecl<'tcx> { // A "temp" is a temporary that we place on the stack. They are // anonymous, always mutable, and have only a type. -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct TempDecl<'tcx> { pub ty: Ty<'tcx>, } @@ -171,7 +171,7 @@ pub struct TempDecl<'tcx> { // // there is only one argument, of type `(i32, u32)`, but two bindings // (`x` and `y`). -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct ArgDecl<'tcx> { pub ty: Ty<'tcx>, } @@ -207,14 +207,14 @@ impl Debug for BasicBlock { /////////////////////////////////////////////////////////////////////////// // BasicBlock and Terminator -#[derive(Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { pub statements: Vec>, pub terminator: Option>, pub is_cleanup: bool, } -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub enum Terminator<'tcx> { /// block should have one successor in the graph; we jump there Goto { @@ -481,13 +481,13 @@ impl<'tcx> Terminator<'tcx> { /////////////////////////////////////////////////////////////////////////// // Statements -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, RustcEncodable, RustcDecodable)] pub struct Statement<'tcx> { pub span: Span, pub kind: StatementKind<'tcx>, } -#[derive(Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub enum StatementKind<'tcx> { Assign(Lvalue<'tcx>, Rvalue<'tcx>), Drop(DropKind, Lvalue<'tcx>), @@ -721,7 +721,7 @@ pub enum Rvalue<'tcx> { InlineAsm(InlineAsm), } -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum CastKind { Misc, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 1a99aba591a8c..6956ee8eac2de 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -643,6 +643,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "keep the AST after lowering it to HIR"), show_span: Option = (None, parse_opt_string, "show spans for compiler debugging (expr|pat|ty)"), + print_trans_items: Option = (None, parse_opt_string, + "print the result of the translation item collection pass"), } pub fn default_lib_output() -> CrateType { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index fbfdbd15116c9..a55a6918b5b96 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -15,7 +15,7 @@ use session::search_paths::PathKind; use util::nodemap::{NodeMap, FnvHashMap}; use syntax::ast::{NodeId, NodeIdAssigner, Name}; -use syntax::codemap::Span; +use syntax::codemap::{Span, MultiSpan}; use syntax::errors::{self, DiagnosticBuilder}; use syntax::errors::emitter::{Emitter, BasicEmitter, EmitterWriter}; use syntax::errors::json::JsonEmitter; @@ -47,7 +47,7 @@ pub struct Session { pub cstore: Rc CrateStore<'a>>, pub parse_sess: ParseSess, // For a library crate, this is always none - pub entry_fn: RefCell>, + pub entry_fn: RefCell>, pub entry_type: Cell>, pub plugin_registrar_fn: Cell>, pub default_sysroot: Option, @@ -57,7 +57,7 @@ pub struct Session { pub local_crate_source_file: Option, pub working_dir: PathBuf, pub lint_store: RefCell, - pub lints: RefCell>>, + pub lints: RefCell>>, pub plugin_llvm_passes: RefCell>, pub plugin_attributes: RefCell>, pub crate_types: RefCell>, @@ -81,36 +81,36 @@ pub struct Session { } impl Session { - pub fn struct_span_warn<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_warn<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_warn(sp, msg) } - pub fn struct_span_warn_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_warn_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_warn_with_code(sp, msg, code) } pub fn struct_warn<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.diagnostic().struct_warn(msg) } - pub fn struct_span_err<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_err<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { match split_msg_into_multilines(msg) { Some(ref msg) => self.diagnostic().struct_span_err(sp, msg), None => self.diagnostic().struct_span_err(sp, msg), } } - pub fn struct_span_err_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_err_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { match split_msg_into_multilines(msg) { Some(ref msg) => self.diagnostic().struct_span_err_with_code(sp, msg, code), None => self.diagnostic().struct_span_err_with_code(sp, msg, code), @@ -119,46 +119,46 @@ impl Session { pub fn struct_err<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.diagnostic().struct_err(msg) } - pub fn struct_span_fatal<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_fatal<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_fatal(sp, msg) } - pub fn struct_span_fatal_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_fatal_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { self.diagnostic().struct_span_fatal_with_code(sp, msg, code) } pub fn struct_fatal<'a>(&'a self, msg: &str) -> DiagnosticBuilder<'a> { self.diagnostic().struct_fatal(msg) } - pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { + pub fn span_fatal>(&self, sp: S, msg: &str) -> ! { panic!(self.diagnostic().span_fatal(sp, msg)) } - pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> ! { + pub fn span_fatal_with_code>(&self, sp: S, msg: &str, code: &str) -> ! { panic!(self.diagnostic().span_fatal_with_code(sp, msg, code)) } pub fn fatal(&self, msg: &str) -> ! { panic!(self.diagnostic().fatal(msg)) } - pub fn span_err_or_warn(&self, is_warning: bool, sp: Span, msg: &str) { + pub fn span_err_or_warn>(&self, is_warning: bool, sp: S, msg: &str) { if is_warning { self.span_warn(sp, msg); } else { self.span_err(sp, msg); } } - pub fn span_err(&self, sp: Span, msg: &str) { + pub fn span_err>(&self, sp: S, msg: &str) { match split_msg_into_multilines(msg) { Some(msg) => self.diagnostic().span_err(sp, &msg), None => self.diagnostic().span_err(sp, msg) } } - pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) { + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { match split_msg_into_multilines(msg) { Some(msg) => self.diagnostic().span_err_with_code(sp, &msg, code), None => self.diagnostic().span_err_with_code(sp, msg, code) @@ -179,9 +179,9 @@ impl Session { pub fn track_errors(&self, f: F) -> Result where F: FnOnce() -> T { - let mut count = self.err_count(); + let count = self.err_count(); let result = f(); - count -= self.err_count(); + let count = self.err_count() - count; if count == 0 { Ok(result) } else { @@ -199,32 +199,32 @@ impl Session { } } } - pub fn span_warn(&self, sp: Span, msg: &str) { + pub fn span_warn>(&self, sp: S, msg: &str) { self.diagnostic().span_warn(sp, msg) } - pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: &str) { self.diagnostic().span_warn_with_code(sp, msg, code) } pub fn warn(&self, msg: &str) { self.diagnostic().warn(msg) } - pub fn opt_span_warn(&self, opt_sp: Option, msg: &str) { + pub fn opt_span_warn>(&self, opt_sp: Option, msg: &str) { match opt_sp { Some(sp) => self.span_warn(sp, msg), None => self.warn(msg), } } - pub fn opt_span_bug(&self, opt_sp: Option, msg: &str) -> ! { + pub fn opt_span_bug>(&self, opt_sp: Option, msg: &str) -> ! { match opt_sp { Some(sp) => self.span_bug(sp, msg), None => self.bug(msg), } } /// Delay a span_bug() call until abort_if_errors() - pub fn delay_span_bug(&self, sp: Span, msg: &str) { + pub fn delay_span_bug>(&self, sp: S, msg: &str) { self.diagnostic().delay_span_bug(sp, msg) } - pub fn span_bug(&self, sp: Span, msg: &str) -> ! { + pub fn span_bug>(&self, sp: S, msg: &str) -> ! { self.diagnostic().span_bug(sp, msg) } pub fn bug(&self, msg: &str) -> ! { @@ -233,10 +233,10 @@ impl Session { pub fn note_without_error(&self, msg: &str) { self.diagnostic().note_without_error(msg) } - pub fn span_note_without_error(&self, sp: Span, msg: &str) { + pub fn span_note_without_error>(&self, sp: S, msg: &str) { self.diagnostic().span_note_without_error(sp, msg) } - pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { + pub fn span_unimpl>(&self, sp: S, msg: &str) -> ! { self.diagnostic().span_unimpl(sp, msg) } pub fn unimpl(&self, msg: &str) -> ! { @@ -273,7 +273,7 @@ impl Session { } // This exists to help with refactoring to eliminate impossible // cases later on - pub fn impossible_case(&self, sp: Span, msg: &str) -> ! { + pub fn impossible_case>(&self, sp: S, msg: &str) -> ! { self.span_bug(sp, &format!("impossible case reached: {}", msg)); } pub fn verbose(&self) -> bool { self.opts.debugging_opts.verbose } diff --git a/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs new file mode 100644 index 0000000000000..fed4649c985a6 --- /dev/null +++ b/src/librustc_back/target/armv7_unknown_linux_gnueabihf.rs @@ -0,0 +1,31 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::{Target, TargetOptions}; + +pub fn target() -> Target { + let base = super::linux_base::opts(); + Target { + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnueabihf".to_string(), + target_vendor: "unknown".to_string(), + + options: TargetOptions { + features: "+v7,+vfp2,+neon".to_string(), + cpu: "cortex-a7".to_string(), + .. base + } + } +} + diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 51149101e0c2d..331ec4a82ac51 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -417,6 +417,7 @@ impl Target { powerpc64le_unknown_linux_gnu, arm_unknown_linux_gnueabi, arm_unknown_linux_gnueabihf, + armv7_unknown_linux_gnueabihf, aarch64_unknown_linux_gnu, x86_64_unknown_linux_musl, diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 631149e69d77e..3ca9a17686485 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -991,7 +991,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { &format!("to force the closure to take ownership of {} \ (and any other referenced variables), \ use the `move` keyword, as shown:", - cmt_path_or_string), + cmt_path_or_string), suggestion) .emit(); } diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index b19628baa88be..a662081ac2123 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -32,7 +32,7 @@ use rustc::front::map as hir_map; use rustc::session::{self, config}; use std::rc::Rc; use syntax::{abi, ast}; -use syntax::codemap::{Span, CodeMap, DUMMY_SP}; +use syntax::codemap::{MultiSpan, CodeMap, DUMMY_SP}; use syntax::errors; use syntax::errors::emitter::Emitter; use syntax::errors::{Level, RenderSpan}; @@ -78,14 +78,14 @@ fn remove_message(e: &mut ExpectErrorEmitter, msg: &str, lvl: Level) { impl Emitter for ExpectErrorEmitter { fn emit(&mut self, - _sp: Option, + _sp: Option<&MultiSpan>, msg: &str, _: Option<&str>, lvl: Level) { remove_message(self, msg, lvl); } - fn custom_emit(&mut self, _sp: RenderSpan, msg: &str, lvl: Level) { + fn custom_emit(&mut self, _sp: &RenderSpan, msg: &str, lvl: Level) { remove_message(self, msg, lvl); } } diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index e91c7e6ac4506..cdbb684470314 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -177,6 +177,7 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, } _ => { } } + Ok(ii) } } diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index 802629e8f3e8b..bc52921e1b8ca 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -445,6 +445,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { decoder::maybe_get_item_mir(&*cdata, tcx, def.index) } + fn is_item_mir_available(&self, def: DefId) -> bool { + let cdata = self.get_crate_data(def.krate); + decoder::is_item_mir_available(&*cdata, def.index) + } + fn crates(&self) -> Vec { let mut result = vec![]; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index bca8cb3995ab1..326f68561b09d 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -830,6 +830,14 @@ pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, } } +pub fn is_item_mir_available<'tcx>(cdata: Cmd, id: DefIndex) -> bool { + if let Some(item_doc) = cdata.get_item(id) { + return reader::maybe_get_doc(item_doc, tag_mir as usize).is_some(); + } + + false +} + pub fn maybe_get_item_mir<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: DefIndex) @@ -849,6 +857,8 @@ pub fn maybe_get_item_mir<'tcx>(cdata: Cmd, }) }).unwrap(); + assert!(decoder.position() == mir_doc.end); + let mut def_id_and_span_translator = MirDefIdAndSpanTranslator { crate_metadata: cdata, codemap: tcx.sess.codemap(), diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2536a12dfcb7d..4b62e65bb0f13 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -44,7 +44,6 @@ use self::RibKind::*; use self::UseLexicalScopeFlag::*; use self::ModulePrefixResult::*; use self::AssocItemResolveResult::*; -use self::NameSearchType::*; use self::BareIdentifierPatternResolution::*; use self::ParentLink::*; use self::FallbackChecks::*; @@ -784,16 +783,6 @@ enum AssocItemResolveResult { ResolveAttempt(Option), } -#[derive(Copy, Clone, PartialEq)] -enum NameSearchType { - /// We're doing a name search in order to resolve a `use` directive. - ImportSearch, - - /// We're doing a name search in order to resolve a path type, a path - /// expression, or a path pattern. - PathSearch, -} - #[derive(Copy, Clone)] enum BareIdentifierPatternResolution { FoundStructOrEnumVariant(Def, LastPrivate), @@ -1370,7 +1359,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module_path: &[Name], index: usize, span: Span, - name_search_type: NameSearchType, lp: LastPrivate) -> ResolveResult<(Module<'a>, LastPrivate)> { fn search_parent_externals<'a>(needle: Name, module: Module<'a>) @@ -1396,11 +1384,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // modules as we go. while index < module_path_len { let name = module_path[index]; - match self.resolve_name_in_module(search_module, - name, - TypeNS, - name_search_type, - false) { + match self.resolve_name_in_module(search_module, name, TypeNS, false) { Failed(None) => { let segment_name = name.as_str(); let module_name = module_to_string(search_module); @@ -1477,8 +1461,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module_: Module<'a>, module_path: &[Name], use_lexical_scope: UseLexicalScopeFlag, - span: Span, - name_search_type: NameSearchType) + span: Span) -> ResolveResult<(Module<'a>, LastPrivate)> { let module_path_len = module_path.len(); assert!(module_path_len > 0); @@ -1559,7 +1542,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module_path, start_index, span, - name_search_type, last_private) } @@ -1658,11 +1640,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // Resolve the name in the parent module. - match self.resolve_name_in_module(search_module, - name, - namespace, - PathSearch, - true) { + match self.resolve_name_in_module(search_module, name, namespace, true) { Failed(Some((span, msg))) => { resolve_error(self, span, ResolutionError::FailedToResolve(&*msg)); } @@ -1787,7 +1765,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module_: Module<'a>, name: Name, namespace: Namespace, - name_search_type: NameSearchType, allow_private_imports: bool) -> ResolveResult<(Target<'a>, bool)> { debug!("(resolving name in module) resolving `{}` in `{}`", @@ -1811,14 +1788,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - // Next, check the module's imports if necessary. - - // If this is a search of all imports, we should be done with glob - // resolution at this point. - if name_search_type == PathSearch { - assert_eq!(module_.glob_count.get(), 0); - } - // Check the list of resolved imports. let children = module_.import_resolutions.borrow(); match children.get(&name) { @@ -2516,29 +2485,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.value_ribs.push(Rib::new(NormalRibKind)); } - // Check for imports appearing after non-item statements. - let mut found_non_item = false; - for statement in &block.stmts { - if let hir::StmtDecl(ref declaration, _) = statement.node { - if let hir::DeclItem(i) = declaration.node { - let i = self.ast_map.expect_item(i.id); - match i.node { - ItemExternCrate(_) | ItemUse(_) if found_non_item => { - span_err!(self.session, - i.span, - E0154, - "imports are not allowed after non-item statements"); - } - _ => {} - } - } else { - found_non_item = true - } - } else { - found_non_item = true; - } - } - // Descend into the block. intravisit::walk_block(self, block); @@ -2935,9 +2881,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - Indeterminate => { - panic!("unexpected indeterminate result"); - } + Indeterminate => return BareIdentifierPatternUnresolved, Failed(err) => { match err { Some((span, msg)) => { @@ -3177,11 +3121,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let containing_module; let last_private; let current_module = self.current_module; - match self.resolve_module_path(current_module, - &module_path[..], - UseLexicalScope, - span, - PathSearch) { + match self.resolve_module_path(current_module, &module_path, UseLexicalScope, span) { Failed(err) => { let (span, msg) = match err { Some((span, msg)) => (span, msg), @@ -3195,7 +3135,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { resolve_error(self, span, ResolutionError::FailedToResolve(&*msg)); return None; } - Indeterminate => panic!("indeterminate unexpected"), + Indeterminate => return None, Success((resulting_module, resulting_last_private)) => { containing_module = resulting_module; last_private = resulting_last_private; @@ -3203,11 +3143,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let name = segments.last().unwrap().identifier.name; - let def = match self.resolve_name_in_module(containing_module, - name, - namespace, - NameSearchType::PathSearch, - false) { + let def = match self.resolve_name_in_module(containing_module, name, namespace, false) { Success((Target { binding, .. }, _)) => { let (def, lp) = binding.def_and_lp(); (def, last_private.or(lp)) @@ -3242,7 +3178,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &module_path[..], 0, span, - PathSearch, LastMod(AllPublic)) { Failed(err) => { let (span, msg) = match err { @@ -3258,9 +3193,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { return None; } - Indeterminate => { - panic!("indeterminate unexpected"); - } + Indeterminate => return None, Success((resulting_module, resulting_last_private)) => { containing_module = resulting_module; @@ -3269,11 +3202,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let name = segments.last().unwrap().identifier.name; - match self.resolve_name_in_module(containing_module, - name, - namespace, - NameSearchType::PathSearch, - false) { + match self.resolve_name_in_module(containing_module, name, namespace, false) { Success((Target { binding, .. }, _)) => { let (def, lp) = binding.def_and_lp(); Some((def, last_private.or(lp))) @@ -3315,7 +3244,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let Success((target, _)) = self.resolve_name_in_module(module, ident.unhygienic_name, namespace, - PathSearch, true) { if let Some(def) = target.binding.def() { return Some(LocalDef::from_def(def)); @@ -3355,9 +3283,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } } - Indeterminate => { - panic!("unexpected indeterminate result"); - } + Indeterminate => None, Failed(err) => { debug!("(resolving item path by identifier in lexical scope) failed to resolve {}", name); @@ -3413,11 +3339,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } } else { - match this.resolve_module_path(root, - &name_path[..], - UseLexicalScope, - span, - PathSearch) { + match this.resolve_module_path(root, &name_path, UseLexicalScope, span) { Success((module, _)) => Some(module), _ => None, } @@ -3663,10 +3585,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let current_module = self.current_module; match self.resolve_module_path(current_module, - &name_path[..], - UseLexicalScope, - expr.span, - PathSearch) { + &name_path[..], + UseLexicalScope, + expr.span) { Success(_) => { context = UnresolvedNameContext::PathIsMod(expr.id); }, diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 7df71fadd89f2..364218d84137c 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -16,7 +16,6 @@ use Namespace::{self, TypeNS, ValueNS}; use {NameBindings, NameBinding}; use NamespaceResult::{BoundResult, UnboundResult, UnknownResult}; use NamespaceResult; -use NameSearchType; use ResolveResult; use Resolver; use UseLexicalScopeFlag; @@ -321,8 +320,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { match self.resolver.resolve_module_path(module_, &module_path[..], UseLexicalScopeFlag::DontUseLexicalScope, - import_directive.span, - NameSearchType::ImportSearch) { + import_directive.span) { ResolveResult::Failed(err) => { resolution_result = ResolveResult::Failed(err); None diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index bc007da7174a7..1068bca55d61b 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -109,7 +109,7 @@ impl SharedEmitter { } impl Emitter for SharedEmitter { - fn emit(&mut self, sp: Option, + fn emit(&mut self, sp: Option<&codemap::MultiSpan>, msg: &str, code: Option<&str>, lvl: Level) { assert!(sp.is_none(), "SharedEmitter doesn't support spans"); @@ -120,7 +120,7 @@ impl Emitter for SharedEmitter { }); } - fn custom_emit(&mut self, _sp: errors::RenderSpan, _msg: &str, _lvl: Level) { + fn custom_emit(&mut self, _sp: &errors::RenderSpan, _msg: &str, _lvl: Level) { panic!("SharedEmitter doesn't support custom_emit"); } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 7460ef82ebee4..76b1bad121469 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -41,8 +41,10 @@ use middle::infer; use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem}; use middle::weak_lang_items; use middle::pat_util::simple_name; -use middle::subst::Substs; +use middle::subst::{self, Substs}; +use middle::traits; use middle::ty::{self, Ty, TypeFoldable}; +use middle::ty::adjustment::CustomCoerceUnsized; use rustc::dep_graph::DepNode; use rustc::front::map as hir_map; use rustc::util::common::time; @@ -59,10 +61,11 @@ use trans::callee; use trans::cleanup::{self, CleanupMethods, DropHint}; use trans::closure; use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral}; +use trans::collector::{self, TransItem, TransItemState, TransItemCollectionMode}; use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef}; use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext}; use trans::common::{Result, NodeIdAndSpan, VariantInfo}; -use trans::common::{node_id_type, return_type_is_void}; +use trans::common::{node_id_type, return_type_is_void, fulfill_obligation}; use trans::common::{type_is_immediate, type_is_zero_size, val_ty}; use trans::common; use trans::consts; @@ -98,7 +101,7 @@ use std::collections::{HashMap, HashSet}; use std::str; use std::{i8, i16, i32, i64}; use syntax::abi::{Rust, RustCall, RustIntrinsic, PlatformIntrinsic, Abi}; -use syntax::codemap::Span; +use syntax::codemap::{Span, DUMMY_SP}; use syntax::parse::token::InternedString; use syntax::attr::AttrMetaMethods; use syntax::attr; @@ -736,6 +739,29 @@ pub fn coerce_unsized_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } +pub fn custom_coerce_unsize_info<'ccx, 'tcx>(ccx: &CrateContext<'ccx, 'tcx>, + source_ty: Ty<'tcx>, + target_ty: Ty<'tcx>) + -> CustomCoerceUnsized { + let trait_substs = Substs::erased(subst::VecPerParamSpace::new(vec![target_ty], + vec![source_ty], + Vec::new())); + let trait_ref = ty::Binder(ty::TraitRef { + def_id: ccx.tcx().lang_items.coerce_unsized_trait().unwrap(), + substs: ccx.tcx().mk_substs(trait_substs) + }); + + match fulfill_obligation(ccx, DUMMY_SP, trait_ref) { + traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { + ccx.tcx().custom_coerce_unsized_kind(impl_def_id) + } + vtable => { + ccx.sess().bug(&format!("invalid CoerceUnsized vtable: {:?}", + vtable)); + } + } +} + pub fn cast_shift_expr_rhs(cx: Block, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { cast_shift_rhs(op, lhs, rhs, |a, b| Trunc(cx, a, b), |a, b| ZExt(cx, a, b)) } @@ -1965,6 +1991,8 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, closure_env: closure::ClosureEnv<'b>) { ccx.stats().n_closures.set(ccx.stats().n_closures.get() + 1); + record_translation_item_as_generated(ccx, fn_ast_id, param_substs); + let _icx = push_ctxt("trans_closure"); attributes::emit_uwtable(llfndecl, true); @@ -2078,6 +2106,28 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Insert the mandatory first few basic blocks before lltop. finish_fn(&fcx, bcx, output_type, ret_debug_loc); + + fn record_translation_item_as_generated<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + node_id: ast::NodeId, + param_substs: &'tcx Substs<'tcx>) { + if !collector::collecting_debug_information(ccx) { + return; + } + + let def_id = match ccx.tcx().node_id_to_type(node_id).sty { + ty::TyClosure(def_id, _) => def_id, + _ => ccx.external_srcs() + .borrow() + .get(&node_id) + .map(|did| *did) + .unwrap_or_else(|| ccx.tcx().map.local_def_id(node_id)), + }; + + ccx.record_translation_item_as_generated(TransItem::Fn{ + def_id: def_id, + substs: ccx.tcx().mk_substs(ccx.tcx().erase_regions(param_substs)), + }); + } } /// Creates an LLVM function corresponding to a source language function. @@ -3161,6 +3211,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, // First, verify intrinsics. intrinsic::check_intrinsics(&ccx); + collect_translation_items(&ccx); + // Next, translate all items. See `TransModVisitor` for // details on why we walk in this particular way. { @@ -3168,6 +3220,8 @@ pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, intravisit::walk_mod(&mut TransItemsWithinModVisitor { ccx: &ccx }, &krate.module); krate.visit_all_items(&mut TransModVisitor { ccx: &ccx }); } + + collector::print_collection_results(&ccx); } for ccx in shared_ccx.iter() { @@ -3339,3 +3393,48 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TransItemsWithinModVisitor<'a, 'tcx> { } } } + +fn collect_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) { + let time_passes = ccx.sess().time_passes(); + + let collection_mode = match ccx.sess().opts.debugging_opts.print_trans_items { + Some(ref s) => { + let mode_string = s.to_lowercase(); + let mode_string = mode_string.trim(); + if mode_string == "eager" { + TransItemCollectionMode::Eager + } else { + if mode_string != "lazy" { + let message = format!("Unknown codegen-item collection mode '{}'. \ + Falling back to 'lazy' mode.", + mode_string); + ccx.sess().warn(&message); + } + + TransItemCollectionMode::Lazy + } + } + None => TransItemCollectionMode::Lazy + }; + + let items = time(time_passes, "translation item collection", || { + collector::collect_crate_translation_items(&ccx, collection_mode) + }); + + if ccx.sess().opts.debugging_opts.print_trans_items.is_some() { + let mut item_keys: Vec<_> = items.iter() + .map(|i| i.to_string(ccx)) + .collect(); + item_keys.sort(); + + for item in item_keys { + println!("TRANS_ITEM {}", item); + } + + let mut ccx_map = ccx.translation_items().borrow_mut(); + + for cgi in items { + ccx_map.insert(cgi, TransItemState::PredictedButNotGenerated); + } + } +} diff --git a/src/librustc_trans/trans/collector.rs b/src/librustc_trans/trans/collector.rs new file mode 100644 index 0000000000000..6ec7378859848 --- /dev/null +++ b/src/librustc_trans/trans/collector.rs @@ -0,0 +1,1597 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Translation Item Collection +//! =========================== +//! +//! This module is responsible for discovering all items that will contribute to +//! to code generation of the crate. The important part here is that it not only +//! needs to find syntax-level items (functions, structs, etc) but also all +//! their monomorphized instantiations. Every non-generic, non-const function +//! maps to one LLVM artifact. Every generic function can produce +//! from zero to N artifacts, depending on the sets of type arguments it +//! is instantiated with. +//! This also applies to generic items from other crates: A generic definition +//! in crate X might produce monomorphizations that are compiled into crate Y. +//! We also have to collect these here. +//! +//! The following kinds of "translation items" are handled here: +//! +//! - Functions +//! - Methods +//! - Closures +//! - Statics +//! - Drop glue +//! +//! The following things also result in LLVM artifacts, but are not collected +//! here, since we instantiate them locally on demand when needed in a given +//! codegen unit: +//! +//! - Constants +//! - Vtables +//! - Object Shims +//! +//! +//! General Algorithm +//! ----------------- +//! Let's define some terms first: +//! +//! - A "translation item" is something that results in a function or global in +//! the LLVM IR of a codegen unit. Translation items do not stand on their +//! own, they can reference other translation items. For example, if function +//! `foo()` calls function `bar()` then the translation item for `foo()` +//! references the translation item for function `bar()`. In general, the +//! definition for translation item A referencing a translation item B is that +//! the LLVM artifact produced for A references the LLVM artifact produced +//! for B. +//! +//! - Translation items and the references between them for a directed graph, +//! where the translation items are the nodes and references form the edges. +//! Let's call this graph the "translation item graph". +//! +//! - The translation item graph for a program contains all translation items +//! that are needed in order to produce the complete LLVM IR of the program. +//! +//! The purpose of the algorithm implemented in this module is to build the +//! translation item graph for the current crate. It runs in two phases: +//! +//! 1. Discover the roots of the graph by traversing the HIR of the crate. +//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR +//! representation of the item corresponding to a given node, until no more +//! new nodes are found. +//! +//! ### Discovering roots +//! +//! The roots of the translation item graph correspond to the non-generic +//! syntactic items in the source code. We find them by walking the HIR of the +//! crate, and whenever we hit upon a function, method, or static item, we +//! create a translation item consisting of the items DefId and, since we only +//! consider non-generic items, an empty type-substitution set. +//! +//! ### Finding neighbor nodes +//! Given a translation item node, we can discover neighbors by inspecting its +//! MIR. We walk the MIR and any time we hit upon something that signifies a +//! reference to another translation item, we have found a neighbor. Since the +//! translation item we are currently at is always monomorphic, we also know the +//! concrete type arguments of its neighbors, and so all neighbors again will be +//! monomorphic. The specific forms a reference to a neighboring node can take +//! in MIR are quite diverse. Here is an overview: +//! +//! #### Calling Functions/Methods +//! The most obvious form of one translation item referencing another is a +//! function or method call (represented by a CALL terminator in MIR). But +//! calls are not the only thing that might introduce a reference between two +//! function translation items, and as we will see below, they are just a +//! specialized of the form described next, and consequently will don't get any +//! special treatment in the algorithm. +//! +//! #### Taking a reference to a function or method +//! A function does not need to actually be called in order to be a neighbor of +//! another function. It suffices to just take a reference in order to introduce +//! an edge. Consider the following example: +//! +//! ```rust +//! fn print_val(x: T) { +//! println!("{}", x); +//! } +//! +//! fn call_fn(f: &Fn(i32), x: i32) { +//! f(x); +//! } +//! +//! fn main() { +//! let print_i32 = print_val::; +//! call_fn(&print_i32, 0); +//! } +//! ``` +//! The MIR of none of these functions will contain an explicit call to +//! `print_val::`. Nonetheless, in order to translate this program, we need +//! an instance of this function. Thus, whenever we encounter a function or +//! method in operand position, we treat it as a neighbor of the current +//! translation item. Calls are just a special case of that. +//! +//! #### Closures +//! In a way, closures are a simple case. Since every closure object needs to be +//! constructed somewhere, we can reliably discover them by observing +//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also +//! true for closures inlined from other crates. +//! +//! #### Drop glue +//! Drop glue translation items are introduced by MIR drop-statements. The +//! generated translation item will again have drop-glue item neighbors if the +//! type to be dropped contains nested values that also need to be dropped. It +//! might also have a function item neighbor for the explicit `Drop::drop` +//! implementation of its type. +//! +//! #### Unsizing Casts +//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! Since the resulting fat-pointer contains a reference to a vtable, we need to +//! instantiate all object-save methods of the trait, as we need to store +//! pointers to these functions even if they never get called anywhere. This can +//! be seen as a special case of taking a function reference. +//! +//! #### Boxes +//! Since `Box` expression have special compiler support, no explicit calls to +//! `exchange_malloc()` and `exchange_free()` may show up in MIR, even if the +//! compiler will generate them. We have to observe `Rvalue::Box` expressions +//! and Box-typed drop-statements for that purpose. +//! +//! +//! Interaction with Cross-Crate Inlining +//! ------------------------------------- +//! The binary of a crate will not only contain machine code for the items +//! defined in the source code of that crate. It will also contain monomorphic +//! instantiations of any extern generic functions and of functions marked with +//! #[inline]. +//! The collection algorithm handles this more or less transparently. If it is +//! about to create a translation item for something with an external `DefId`, +//! it will take a look if the MIR for that item is available, and if so just +//! proceed normally. If the MIR is not available, it assumes that that item is +//! just linked to and no node is created; which is exactly what we want, since +//! no machine code should be generated in the current crate for such an item. +//! +//! Eager and Lazy Collection Mode +//! ------------------------------ +//! Translation item collection can be performed in one of two modes: +//! +//! - Lazy mode means that items will only be instantiated when actually +//! referenced. The goal is to produce the least amount of machine code +//! possible. +//! +//! - Eager mode is meant to be used in conjunction with incremental compilation +//! where a stable set of translation items is more important than a minimal +//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! in the crate, even of no drop call for that type exists (yet). It will +//! also instantiate default implementations of trait methods, something that +//! otherwise is only done on demand. +//! +//! +//! Open Issues +//! ----------- +//! Some things are not yet fully implemented in the current version of this +//! module. +//! +//! ### Initializers of Constants and Statics +//! Since no MIR is constructed yet for initializer expressions of constants and +//! statics we cannot inspect these properly. +//! +//! ### Const Fns +//! Ideally, no translation item should be generated for const fns unless there +//! is a call to them that cannot be evaluated at compile time. At the moment +//! this is not implemented however: a translation item will be produced +//! regardless of whether it is actually needed or not. + +use rustc_front::hir; +use rustc_front::intravisit as hir_visit; + +use rustc::front::map as hir_map; +use rustc::middle::def_id::DefId; +use rustc::middle::lang_items::{ExchangeFreeFnLangItem, ExchangeMallocFnLangItem}; +use rustc::middle::{ty, traits}; +use rustc::middle::subst::{self, Substs, Subst}; +use rustc::middle::ty::adjustment::CustomCoerceUnsized; +use rustc::middle::ty::fold::TypeFoldable; +use rustc::mir::repr as mir; +use rustc::mir::visit as mir_visit; +use rustc::mir::visit::Visitor as MirVisitor; + +use syntax::ast::{self, NodeId}; +use syntax::codemap::DUMMY_SP; +use syntax::errors; +use syntax::parse::token; + +use trans::base::custom_coerce_unsize_info; +use trans::context::CrateContext; +use trans::common::{fulfill_obligation, normalize_and_test_predicates, + type_is_sized}; +use trans::glue; +use trans::meth; +use trans::monomorphize; +use util::nodemap::{FnvHashSet, FnvHashMap, DefIdMap}; + +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +pub enum TransItemCollectionMode { + Eager, + Lazy +} + +#[derive(Eq, Clone, Copy, Debug)] +pub enum TransItem<'tcx> { + DropGlue(ty::Ty<'tcx>), + Fn { + def_id: DefId, + substs: &'tcx Substs<'tcx> + }, + Static(NodeId) +} + +impl<'tcx> Hash for TransItem<'tcx> { + fn hash(&self, s: &mut H) { + match *self { + TransItem::DropGlue(t) => { + 0u8.hash(s); + t.hash(s); + }, + TransItem::Fn { def_id, substs } => { + 1u8.hash(s); + def_id.hash(s); + (substs as *const Substs<'tcx> as usize).hash(s); + } + TransItem::Static(node_id) => { + 3u8.hash(s); + node_id.hash(s); + } + }; + } +} + +impl<'tcx> PartialEq for TransItem<'tcx> { + fn eq(&self, other: &Self) -> bool { + match (*self, *other) { + (TransItem::DropGlue(t1), TransItem::DropGlue(t2)) => t1 == t2, + (TransItem::Fn { def_id: def_id1, substs: substs1 }, + TransItem::Fn { def_id: def_id2, substs: substs2 }) => { + def_id1 == def_id2 && substs1 == substs2 + }, + (TransItem::Static(node_id1), TransItem::Static(node_id2)) => { + node_id1 == node_id2 + }, + _ => false + } + } +} + +pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode) + -> FnvHashSet> { + // We are not tracking dependencies of this pass as it has to be re-executed + // every time no matter what. + ccx.tcx().dep_graph.with_ignore(|| { + let roots = collect_roots(ccx, mode); + + debug!("Building translation item graph, beginning at roots"); + let mut visited = FnvHashSet(); + let mut recursion_depths = DefIdMap(); + let mut mir_cache = DefIdMap(); + + for root in roots { + collect_items_rec(ccx, + root, + &mut visited, + &mut recursion_depths, + &mut mir_cache); + } + + visited + }) +} + +// Find all non-generic items by walking the HIR. These items serve as roots to +// start monomorphizing from. +fn collect_roots<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode) + -> Vec> { + debug!("Collecting roots"); + let mut roots = Vec::new(); + + { + let mut visitor = RootCollector { + ccx: ccx, + mode: mode, + output: &mut roots, + enclosing_item: None, + trans_empty_substs: ccx.tcx().mk_substs(Substs::trans_empty()), + }; + + ccx.tcx().map.krate().visit_all_items(&mut visitor); + } + + roots +} + +#[derive(Clone)] +enum CachedMir<'mir, 'tcx: 'mir> { + Ref(&'mir mir::Mir<'tcx>), + Owned(Rc>) +} + +impl<'mir, 'tcx: 'mir> CachedMir<'mir, 'tcx> { + fn get_ref<'a>(&'a self) -> &'a mir::Mir<'tcx> { + match *self { + CachedMir::Ref(r) => r, + CachedMir::Owned(ref rc) => &**rc, + } + } +} + +// Collect all monomorphized translation items reachable from `starting_point` +fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>, + starting_point: TransItem<'tcx>, + visited: &mut FnvHashSet>, + recursion_depths: &mut DefIdMap, + mir_cache: &mut DefIdMap>) { + if !visited.insert(starting_point.clone()) { + // We've been here already, no need to search again. + return; + } + debug!("BEGIN collect_items_rec({})", starting_point.to_string(ccx)); + + let mut neighbors = Vec::new(); + let recursion_depth_reset; + + match starting_point { + TransItem::DropGlue(t) => { + find_drop_glue_neighbors(ccx, t, &mut neighbors); + recursion_depth_reset = None; + } + TransItem::Static(_) => { + recursion_depth_reset = None; + } + TransItem::Fn { def_id, substs: ref param_substs } => { + // Keep track of the monomorphization recursion depth + recursion_depth_reset = Some(check_recursion_limit(ccx, + def_id, + recursion_depths)); + + // Scan the MIR in order to find function calls, closures, and + // drop-glue + let mir = load_mir(ccx, def_id, mir_cache); + + let mut visitor = MirNeighborCollector { + ccx: ccx, + mir: mir.get_ref(), + output: &mut neighbors, + param_substs: param_substs + }; + + visitor.visit_mir(mir.get_ref()); + } + } + + for neighbour in neighbors { + collect_items_rec(ccx, neighbour, visited, recursion_depths, mir_cache); + } + + if let Some((def_id, depth)) = recursion_depth_reset { + recursion_depths.insert(def_id, depth); + } + + debug!("END collect_items_rec({})", starting_point.to_string(ccx)); +} + +fn load_mir<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + mir_cache: &mut DefIdMap>) + -> CachedMir<'a, 'tcx> { + let mir_not_found_error_message = || { + format!("Could not find MIR for function: {}", + ccx.tcx().item_path_str(def_id)) + }; + + if def_id.is_local() { + let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap(); + let mir_opt = ccx.mir_map().get(&node_id); + let mir = errors::expect(ccx.sess().diagnostic(), + mir_opt, + mir_not_found_error_message); + CachedMir::Ref(mir) + } else { + if let Some(mir) = mir_cache.get(&def_id) { + return mir.clone(); + } + + let mir_opt = ccx.sess().cstore.maybe_get_item_mir(ccx.tcx(), def_id); + let mir = errors::expect(ccx.sess().diagnostic(), + mir_opt, + mir_not_found_error_message); + let cached = CachedMir::Owned(Rc::new(mir)); + mir_cache.insert(def_id, cached.clone()); + cached + } +} + +fn check_recursion_limit<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + recursion_depths: &mut DefIdMap) + -> (DefId, usize) { + let recursion_depth = recursion_depths.get(&def_id) + .map(|x| *x) + .unwrap_or(0); + debug!(" => recursion depth={}", recursion_depth); + + // Code that needs to instantiate the same function recursively + // more than the recursion limit is assumed to be causing an + // infinite expansion. + if recursion_depth > ccx.sess().recursion_limit.get() { + if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) { + ccx.sess().span_fatal(ccx.tcx().map.span(node_id), + "reached the recursion limit during monomorphization"); + } else { + let error = format!("reached the recursion limit during \ + monomorphization of '{}'", + ccx.tcx().item_path_str(def_id)); + ccx.sess().fatal(&error[..]); + } + } + + recursion_depths.insert(def_id, recursion_depth + 1); + + (def_id, recursion_depth) +} + +struct MirNeighborCollector<'a, 'tcx: 'a> { + ccx: &'a CrateContext<'a, 'tcx>, + mir: &'a mir::Mir<'tcx>, + output: &'a mut Vec>, + param_substs: &'tcx Substs<'tcx> +} + +impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { + + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>) { + debug!("visiting rvalue {:?}", *rvalue); + + match *rvalue { + mir::Rvalue::Aggregate(mir::AggregateKind::Closure(def_id, + ref substs), _) => { + assert!(can_have_local_instance(self.ccx, def_id)); + let trans_item = create_fn_trans_item(self.ccx, + def_id, + substs.func_substs, + self.param_substs); + self.output.push(trans_item); + } + // When doing an cast from a regular pointer to a fat pointer, we + // have to instantiate all methods of the trait being cast to, so we + // can build the appropriate vtable. + mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, target_ty) => { + let target_ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &target_ty); + let source_ty = self.mir.operand_ty(self.ccx.tcx(), operand); + let source_ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &source_ty); + let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.ccx, + source_ty, + target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if target_ty.is_trait() && !source_ty.is_trait() { + create_trans_items_for_vtable_methods(self.ccx, + target_ty, + source_ty, + self.output); + } + } + mir::Rvalue::Box(_) => { + let exchange_malloc_fn_def_id = + self.ccx + .tcx() + .lang_items + .require(ExchangeMallocFnLangItem) + .expect("Could not find ExchangeMallocFnLangItem"); + + assert!(can_have_local_instance(self.ccx, exchange_malloc_fn_def_id)); + let exchange_malloc_fn_trans_item = + create_fn_trans_item(self.ccx, + exchange_malloc_fn_def_id, + &Substs::trans_empty(), + self.param_substs); + + self.output.push(exchange_malloc_fn_trans_item); + } + _ => { /* not interesting */ } + } + + self.super_rvalue(rvalue); + } + + fn visit_lvalue(&mut self, + lvalue: &mir::Lvalue<'tcx>, + context: mir_visit::LvalueContext) { + debug!("visiting lvalue {:?}", *lvalue); + + if let mir_visit::LvalueContext::Drop = context { + let ty = self.mir.lvalue_ty(self.ccx.tcx(), lvalue) + .to_ty(self.ccx.tcx()); + + let ty = monomorphize::apply_param_substs(self.ccx.tcx(), + self.param_substs, + &ty); + let ty = self.ccx.tcx().erase_regions(&ty); + let ty = glue::get_drop_glue_type(self.ccx, ty); + self.output.push(TransItem::DropGlue(ty)); + } + + self.super_lvalue(lvalue, context); + } + + fn visit_operand(&mut self, operand: &mir::Operand<'tcx>) { + debug!("visiting operand {:?}", *operand); + + let callee = match *operand { + mir::Operand::Constant(mir::Constant { + literal: mir::Literal::Item { + def_id, + kind, + substs + }, + .. + }) if is_function_or_method(kind) => Some((def_id, substs)), + _ => None + }; + + if let Some((callee_def_id, callee_substs)) = callee { + debug!(" => operand is callable"); + + // `callee_def_id` might refer to a trait method instead of a + // concrete implementation, so we have to find the actual + // implementation. For example, the call might look like + // + // std::cmp::partial_cmp(0i32, 1i32) + // + // Calling do_static_dispatch() here will map the def_id of + // `std::cmp::partial_cmp` to the def_id of `i32::partial_cmp` + let dispatched = do_static_dispatch(self.ccx, + callee_def_id, + callee_substs, + self.param_substs); + + if let Some((callee_def_id, callee_substs)) = dispatched { + // if we have a concrete impl (which we might not have + // in the case of something compiler generated like an + // object shim or a closure that is handled differently), + // we check if the callee is something that will actually + // result in a translation item ... + if can_result_in_trans_item(self.ccx, callee_def_id) { + // ... and create one if it does. + let trans_item = create_fn_trans_item(self.ccx, + callee_def_id, + callee_substs, + self.param_substs); + self.output.push(trans_item); + } + } + } + + self.super_operand(operand); + + fn is_function_or_method(item_kind: mir::ItemKind) -> bool { + match item_kind { + mir::ItemKind::Constant => false, + mir::ItemKind::Function | + mir::ItemKind::Method => true + } + } + + fn can_result_in_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId) + -> bool { + if !match ccx.tcx().lookup_item_type(def_id).ty.sty { + ty::TyBareFn(Some(def_id), _) => { + // Some constructors also have type TyBareFn but they are + // always instantiated inline and don't result in + // translation item. + match ccx.tcx().map.get_if_local(def_id) { + Some(hir_map::NodeVariant(_)) | + Some(hir_map::NodeStructCtor(_)) => false, + Some(_) => true, + None => { + ccx.sess().cstore.variant_kind(def_id).is_none() + } + } + } + ty::TyClosure(..) => true, + _ => false + } { + return false; + } + + can_have_local_instance(ccx, def_id) + } + } +} + +fn can_have_local_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId) + -> bool { + // Take a look if we have the definition available. If not, we + // will not emit code for this item in the local crate, and thus + // don't create a translation item for it. + def_id.is_local() || ccx.sess().cstore.is_item_mir_available(def_id) +} + +fn find_drop_glue_neighbors<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>, + output: &mut Vec>) +{ + debug!("find_drop_glue_neighbors: {}", type_to_string(ccx, ty)); + + // Make sure the exchange_free_fn() lang-item gets translated if + // there is a boxed value. + if let ty::TyBox(_) = ty.sty { + let exchange_free_fn_def_id = ccx.tcx() + .lang_items + .require(ExchangeFreeFnLangItem) + .expect("Could not find ExchangeFreeFnLangItem"); + + assert!(can_have_local_instance(ccx, exchange_free_fn_def_id)); + let exchange_free_fn_trans_item = + create_fn_trans_item(ccx, + exchange_free_fn_def_id, + &Substs::trans_empty(), + &Substs::trans_empty()); + + output.push(exchange_free_fn_trans_item); + } + + // If the type implements Drop, also add a translation item for the + // monomorphized Drop::drop() implementation. + let destructor_did = match ty.sty { + ty::TyStruct(def, _) | + ty::TyEnum(def, _) => def.destructor(), + _ => None + }; + + if let Some(destructor_did) = destructor_did { + use rustc::middle::ty::ToPolyTraitRef; + + let drop_trait_def_id = ccx.tcx() + .lang_items + .drop_trait() + .unwrap(); + + let self_type_substs = ccx.tcx().mk_substs( + Substs::trans_empty().with_self_ty(ty)); + + let trait_ref = ty::TraitRef { + def_id: drop_trait_def_id, + substs: self_type_substs, + }.to_poly_trait_ref(); + + let substs = match fulfill_obligation(ccx, DUMMY_SP, trait_ref) { + traits::VtableImpl(data) => data.substs, + _ => unreachable!() + }; + + if can_have_local_instance(ccx, destructor_did) { + let trans_item = create_fn_trans_item(ccx, + destructor_did, + ccx.tcx().mk_substs(substs), + &Substs::trans_empty()); + output.push(trans_item); + } + } + + // Finally add the types of nested values + match ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyStr | + ty::TyFloat(_) | + ty::TyRawPtr(_) | + ty::TyRef(..) | + ty::TyBareFn(..) | + ty::TySlice(_) | + ty::TyTrait(_) => { + /* nothing to do */ + } + ty::TyStruct(ref adt_def, substs) | + ty::TyEnum(ref adt_def, substs) => { + for field in adt_def.all_fields() { + let field_type = monomorphize::apply_param_substs(ccx.tcx(), + substs, + &field.unsubst_ty()); + let field_type = glue::get_drop_glue_type(ccx, field_type); + + if glue::type_needs_drop(ccx.tcx(), field_type) { + output.push(TransItem::DropGlue(field_type)); + } + } + } + ty::TyClosure(_, ref substs) => { + for upvar_ty in &substs.upvar_tys { + let upvar_ty = glue::get_drop_glue_type(ccx, upvar_ty); + if glue::type_needs_drop(ccx.tcx(), upvar_ty) { + output.push(TransItem::DropGlue(upvar_ty)); + } + } + } + ty::TyBox(inner_type) | + ty::TyArray(inner_type, _) => { + let inner_type = glue::get_drop_glue_type(ccx, inner_type); + if glue::type_needs_drop(ccx.tcx(), inner_type) { + output.push(TransItem::DropGlue(inner_type)); + } + } + ty::TyTuple(ref args) => { + for arg in args { + let arg = glue::get_drop_glue_type(ccx, arg); + if glue::type_needs_drop(ccx.tcx(), arg) { + output.push(TransItem::DropGlue(arg)); + } + } + } + ty::TyProjection(_) | + ty::TyParam(_) | + ty::TyInfer(_) | + ty::TyError => { + ccx.sess().bug("encountered unexpected type"); + } + } +} + +fn do_static_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + fn_def_id: DefId, + fn_substs: &'tcx Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>) + -> Option<(DefId, &'tcx Substs<'tcx>)> { + debug!("do_static_dispatch(fn_def_id={}, fn_substs={:?}, param_substs={:?})", + def_id_to_string(ccx, fn_def_id, None), + fn_substs, + param_substs); + + let is_trait_method = ccx.tcx().trait_of_item(fn_def_id).is_some(); + + if is_trait_method { + match ccx.tcx().impl_or_trait_item(fn_def_id) { + ty::MethodTraitItem(ref method) => { + match method.container { + ty::TraitContainer(trait_def_id) => { + debug!(" => trait method, attempting to find impl"); + do_static_trait_method_dispatch(ccx, + method, + trait_def_id, + fn_substs, + param_substs) + } + ty::ImplContainer(_) => { + // This is already a concrete implementation + debug!(" => impl method"); + Some((fn_def_id, fn_substs)) + } + } + } + _ => unreachable!() + } + } else { + debug!(" => regular function"); + // The function is not part of an impl or trait, no dispatching + // to be done + Some((fn_def_id, fn_substs)) + } +} + +// Given a trait-method and substitution information, find out the actual +// implementation of the trait method. +fn do_static_trait_method_dispatch<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + trait_method: &ty::Method, + trait_id: DefId, + callee_substs: &'tcx Substs<'tcx>, + param_substs: &'tcx Substs<'tcx>) + -> Option<(DefId, &'tcx Substs<'tcx>)> { + let tcx = ccx.tcx(); + debug!("do_static_trait_method_dispatch(trait_method={}, \ + trait_id={}, \ + callee_substs={:?}, \ + param_substs={:?}", + def_id_to_string(ccx, trait_method.def_id, None), + def_id_to_string(ccx, trait_id, None), + callee_substs, + param_substs); + + let rcvr_substs = monomorphize::apply_param_substs(tcx, + param_substs, + callee_substs); + + let trait_ref = ty::Binder(rcvr_substs.to_trait_ref(tcx, trait_id)); + let vtbl = fulfill_obligation(ccx, DUMMY_SP, trait_ref); + + // Now that we know which impl is being used, we can dispatch to + // the actual function: + match vtbl { + traits::VtableImpl(traits::VtableImplData { + impl_def_id: impl_did, + substs: impl_substs, + nested: _ }) => + { + let callee_substs = impl_substs.with_method_from(&rcvr_substs); + let impl_method = tcx.get_impl_method(impl_did, + callee_substs, + trait_method.name); + Some((impl_method.method.def_id, tcx.mk_substs(impl_method.substs))) + } + // If we have a closure or a function pointer, we will also encounter + // the concrete closure/function somewhere else (during closure or fn + // pointer construction). That's where we track those things. + traits::VtableClosure(..) | + traits::VtableFnPointer(..) | + traits::VtableObject(..) => { + None + } + _ => { + tcx.sess.bug(&format!("static call to invalid vtable: {:?}", vtbl)) + } + } +} + +/// For given pair of source and target type that occur in an unsizing coercion, +/// this function finds the pair of types that determines the vtable linking +/// them. +/// +/// For example, the source type might be `&SomeStruct` and the target type\ +/// might be `&SomeTrait` in a cast like: +/// +/// let src: &SomeStruct = ...; +/// let target = src as &SomeTrait; +/// +/// Then the output of this function would be (SomeStruct, SomeTrait) since for +/// constructing the `target` fat-pointer we need the vtable for that pair. +/// +/// Things can get more complicated though because there's also the case where +/// the unsized type occurs as a field: +/// +/// ```rust +/// struct ComplexStruct { +/// a: u32, +/// b: f64, +/// c: T +/// } +/// ``` +/// +/// In this case, if `T` is sized, `&ComplexStruct` is a thin pointer. If `T` +/// is unsized, `&SomeStruct` is a fat pointer, and the vtable it points to is +/// for the pair of `T` (which is a trait) and the concrete type that `T` was +/// originally coerced from: +/// +/// let src: &ComplexStruct = ...; +/// let target = src as &ComplexStruct; +/// +/// Again, we want this `find_vtable_types_for_unsizing()` to provide the pair +/// `(SomeStruct, SomeTrait)`. +/// +/// Finally, there is also the case of custom unsizing coercions, e.g. for +/// smart pointers such as `Rc` and `Arc`. +fn find_vtable_types_for_unsizing<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + source_ty: ty::Ty<'tcx>, + target_ty: ty::Ty<'tcx>) + -> (ty::Ty<'tcx>, ty::Ty<'tcx>) { + match (&source_ty.sty, &target_ty.sty) { + (&ty::TyBox(a), &ty::TyBox(b)) | + (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), + &ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { + let (inner_source, inner_target) = (a, b); + + if !type_is_sized(ccx.tcx(), inner_source) { + (inner_source, inner_target) + } else { + ccx.tcx().struct_lockstep_tails(inner_source, inner_target) + } + } + + (&ty::TyStruct(source_adt_def, source_substs), + &ty::TyStruct(target_adt_def, target_substs)) => { + assert_eq!(source_adt_def, target_adt_def); + + let kind = custom_coerce_unsize_info(ccx, source_ty, target_ty); + + let coerce_index = match kind { + CustomCoerceUnsized::Struct(i) => i + }; + + let source_fields = &source_adt_def.struct_variant().fields; + let target_fields = &target_adt_def.struct_variant().fields; + + assert!(coerce_index < source_fields.len() && + source_fields.len() == target_fields.len()); + + find_vtable_types_for_unsizing(ccx, + source_fields[coerce_index].ty(ccx.tcx(), + source_substs), + target_fields[coerce_index].ty(ccx.tcx(), + target_substs)) + } + _ => ccx.sess() + .bug(&format!("find_vtable_types_for_unsizing: invalid coercion {:?} -> {:?}", + source_ty, + target_ty)) + } +} + +fn create_fn_trans_item<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + fn_substs: &Substs<'tcx>, + param_substs: &Substs<'tcx>) + -> TransItem<'tcx> +{ + debug!("create_fn_trans_item(def_id={}, fn_substs={:?}, param_substs={:?})", + def_id_to_string(ccx, def_id, None), + fn_substs, + param_substs); + + // We only get here, if fn_def_id either designates a local item or + // an inlineable external item. Non-inlineable external items are + // ignored because we don't want to generate any code for them. + let concrete_substs = monomorphize::apply_param_substs(ccx.tcx(), + param_substs, + fn_substs); + let concrete_substs = ccx.tcx().erase_regions(&concrete_substs); + + let trans_item = TransItem::Fn { + def_id: def_id, + substs: ccx.tcx().mk_substs(concrete_substs), + }; + + return trans_item; +} + +/// Creates a `TransItem` for each method that is referenced by the vtable for +/// the given trait/impl pair. +fn create_trans_items_for_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + trait_ty: ty::Ty<'tcx>, + impl_ty: ty::Ty<'tcx>, + output: &mut Vec>) { + assert!(!trait_ty.needs_subst() && !impl_ty.needs_subst()); + + if let ty::TyTrait(ref trait_ty) = trait_ty.sty { + let poly_trait_ref = trait_ty.principal_trait_ref_with_self_ty(ccx.tcx(), + impl_ty); + + // Walk all methods of the trait, including those of its supertraits + for trait_ref in traits::supertraits(ccx.tcx(), poly_trait_ref) { + let vtable = fulfill_obligation(ccx, DUMMY_SP, trait_ref); + match vtable { + traits::VtableImpl( + traits::VtableImplData { + impl_def_id, + substs, + nested: _ }) => { + let items = meth::get_vtable_methods(ccx, impl_def_id, substs) + .into_iter() + // filter out None values + .filter_map(|opt_impl_method| opt_impl_method) + // create translation items + .filter_map(|impl_method| { + if can_have_local_instance(ccx, impl_method.method.def_id) { + let substs = ccx.tcx().mk_substs(impl_method.substs); + Some(create_fn_trans_item(ccx, + impl_method.method.def_id, + substs, + &Substs::trans_empty())) + } else { + None + } + }) + .collect::>(); + + output.extend(items.into_iter()); + } + _ => { /* */ } + } + } + } +} + +//=----------------------------------------------------------------------------- +// Root Collection +//=----------------------------------------------------------------------------- + +struct RootCollector<'b, 'a: 'b, 'tcx: 'a + 'b> { + ccx: &'b CrateContext<'a, 'tcx>, + mode: TransItemCollectionMode, + output: &'b mut Vec>, + enclosing_item: Option<&'tcx hir::Item>, + trans_empty_substs: &'tcx Substs<'tcx> +} + +impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> { + fn visit_item(&mut self, item: &'v hir::Item) { + let old_enclosing_item = self.enclosing_item; + self.enclosing_item = Some(item); + + match item.node { + hir::ItemExternCrate(..) | + hir::ItemUse(..) | + hir::ItemForeignMod(..) | + hir::ItemTy(..) | + hir::ItemDefaultImpl(..) | + hir::ItemTrait(..) | + hir::ItemConst(..) | + hir::ItemMod(..) => { + // Nothing to do, just keep recursing... + } + + hir::ItemImpl(..) => { + if self.mode == TransItemCollectionMode::Eager { + create_trans_items_for_default_impls(self.ccx, + item, + self.trans_empty_substs, + self.output); + } + } + + hir::ItemEnum(_, ref generics) | + hir::ItemStruct(_, ref generics) => { + if !generics.is_parameterized() { + let ty = { + let tables = self.ccx.tcx().tables.borrow(); + tables.node_types[&item.id] + }; + + if self.mode == TransItemCollectionMode::Eager { + debug!("RootCollector: ADT drop-glue for {}", + def_id_to_string(self.ccx, + self.ccx.tcx().map.local_def_id(item.id), + None)); + + let ty = glue::get_drop_glue_type(self.ccx, ty); + self.output.push(TransItem::DropGlue(ty)); + } + } + } + hir::ItemStatic(..) => { + debug!("RootCollector: ItemStatic({})", + def_id_to_string(self.ccx, + self.ccx.tcx().map.local_def_id(item.id), + None)); + self.output.push(TransItem::Static(item.id)); + } + hir::ItemFn(_, _, constness, _, ref generics, _) => { + if !generics.is_type_parameterized() && + constness == hir::Constness::NotConst { + let def_id = self.ccx.tcx().map.local_def_id(item.id); + + debug!("RootCollector: ItemFn({})", + def_id_to_string(self.ccx, def_id, None)); + + self.output.push(TransItem::Fn { + def_id: def_id, + substs: self.trans_empty_substs + }); + } + } + } + + hir_visit::walk_item(self, item); + self.enclosing_item = old_enclosing_item; + } + + fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) { + match ii.node { + hir::ImplItemKind::Method(hir::MethodSig { + ref generics, + constness, + .. + }, _) if constness == hir::Constness::NotConst => { + let hir_map = &self.ccx.tcx().map; + let parent_node_id = hir_map.get_parent_node(ii.id); + let is_impl_generic = match hir_map.expect_item(parent_node_id) { + &hir::Item { + node: hir::ItemImpl(_, _, ref generics, _, _, _), + .. + } => { + generics.is_type_parameterized() + } + _ => { + unreachable!() + } + }; + + if !generics.is_type_parameterized() && !is_impl_generic { + let def_id = self.ccx.tcx().map.local_def_id(ii.id); + + debug!("RootCollector: MethodImplItem({})", + def_id_to_string(self.ccx, def_id, None)); + + self.output.push(TransItem::Fn { + def_id: def_id, + substs: self.trans_empty_substs + }); + } + } + _ => { /* Nothing to do here */ } + } + + hir_visit::walk_impl_item(self, ii) + } +} + +fn create_trans_items_for_default_impls<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + item: &'tcx hir::Item, + trans_empty_substs: &'tcx Substs<'tcx>, + output: &mut Vec>) { + match item.node { + hir::ItemImpl(_, + _, + ref generics, + _, + _, + ref items) => { + if generics.is_type_parameterized() { + return + } + + let tcx = ccx.tcx(); + let impl_def_id = tcx.map.local_def_id(item.id); + + debug!("create_trans_items_for_default_impls(item={})", + def_id_to_string(ccx, impl_def_id, None)); + + if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) { + let default_impls = tcx.provided_trait_methods(trait_ref.def_id); + let callee_substs = tcx.mk_substs(tcx.erase_regions(trait_ref.substs)); + let overridden_methods: FnvHashSet<_> = items.iter() + .map(|item| item.name) + .collect(); + for default_impl in default_impls { + if overridden_methods.contains(&default_impl.name) { + continue; + } + + if default_impl.generics.has_type_params(subst::FnSpace) { + continue; + } + + // The substitutions we have are on the impl, so we grab + // the method type from the impl to substitute into. + let mth = tcx.get_impl_method(impl_def_id, + callee_substs.clone(), + default_impl.name); + + assert!(mth.is_provided); + + let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); + if !normalize_and_test_predicates(ccx, predicates.into_vec()) { + continue; + } + + if can_have_local_instance(ccx, default_impl.def_id) { + let item = create_fn_trans_item(ccx, + default_impl.def_id, + callee_substs, + trans_empty_substs); + output.push(item); + } + } + } + } + _ => { + unreachable!() + } + } +} + +//=----------------------------------------------------------------------------- +// TransItem String Keys +//=----------------------------------------------------------------------------- + +// The code below allows for producing a unique string key for a trans item. +// These keys are used by the handwritten auto-tests, so they need to be +// predictable and human-readable. +// +// Note: A lot of this could looks very similar to what's already in the +// ppaux module. It would be good to refactor things so we only have one +// parameterizable implementation for printing types. + +/// Same as `unique_type_name()` but with the result pushed onto the given +/// `output` parameter. +pub fn push_unique_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: ty::Ty<'tcx>, + output: &mut String) { + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyInt(ast::TyIs) => output.push_str("isize"), + ty::TyInt(ast::TyI8) => output.push_str("i8"), + ty::TyInt(ast::TyI16) => output.push_str("i16"), + ty::TyInt(ast::TyI32) => output.push_str("i32"), + ty::TyInt(ast::TyI64) => output.push_str("i64"), + ty::TyUint(ast::TyUs) => output.push_str("usize"), + ty::TyUint(ast::TyU8) => output.push_str("u8"), + ty::TyUint(ast::TyU16) => output.push_str("u16"), + ty::TyUint(ast::TyU32) => output.push_str("u32"), + ty::TyUint(ast::TyU64) => output.push_str("u64"), + ty::TyFloat(ast::TyF32) => output.push_str("f32"), + ty::TyFloat(ast::TyF64) => output.push_str("f64"), + ty::TyStruct(adt_def, substs) | + ty::TyEnum(adt_def, substs) => { + push_item_name(cx, adt_def.did, output); + push_type_params(cx, substs, &[], output); + }, + ty::TyTuple(ref component_types) => { + output.push('('); + for &component_type in component_types { + push_unique_type_name(cx, component_type, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyBox(inner_type) => { + output.push_str("Box<"); + push_unique_type_name(cx, inner_type, output); + output.push('>'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + push_unique_type_name(cx, inner_type, output); + }, + ty::TyRef(_, ty::TypeAndMut { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + push_unique_type_name(cx, inner_type, output); + }, + ty::TyArray(inner_type, len) => { + output.push('['); + push_unique_type_name(cx, inner_type, output); + output.push_str(&format!("; {}", len)); + output.push(']'); + }, + ty::TySlice(inner_type) => { + output.push('['); + push_unique_type_name(cx, inner_type, output); + output.push(']'); + }, + ty::TyTrait(ref trait_data) => { + push_item_name(cx, trait_data.principal.skip_binder().def_id, output); + push_type_params(cx, + &trait_data.principal.skip_binder().substs, + &trait_data.bounds.projection_bounds, + output); + }, + ty::TyBareFn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => { + if unsafety == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + if abi != ::syntax::abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = cx.tcx().erase_late_bound_regions(sig); + if !sig.inputs.is_empty() { + for ¶meter_type in &sig.inputs { + push_unique_type_name(cx, parameter_type, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs.is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + match sig.output { + ty::FnConverging(result_type) if result_type.is_nil() => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_unique_type_name(cx, result_type, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } + } + }, + ty::TyClosure(def_id, ref closure_substs) => { + push_item_name(cx, def_id, output); + output.push_str("{"); + output.push_str(&format!("{}:{}", def_id.krate, def_id.index.as_usize())); + output.push_str("}"); + push_type_params(cx, closure_substs.func_substs, &[], output); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyParam(_) => { + cx.sess().bug(&format!("debuginfo: Trying to create type name for \ + unexpected type: {:?}", t)); + } + } +} + +fn push_item_name(ccx: &CrateContext, + def_id: DefId, + output: &mut String) { + if def_id.is_local() { + let node_id = ccx.tcx().map.as_local_node_id(def_id).unwrap(); + let inlined_from = ccx.external_srcs() + .borrow() + .get(&node_id) + .map(|def_id| *def_id); + + if let Some(extern_def_id) = inlined_from { + push_item_name(ccx, extern_def_id, output); + return; + } + + output.push_str(&ccx.link_meta().crate_name); + output.push_str("::"); + } + + for part in ccx.tcx().def_path(def_id) { + output.push_str(&format!("{}[{}]::", + part.data.as_interned_str(), + part.disambiguator)); + } + + output.pop(); + output.pop(); +} + +fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + substs: &Substs<'tcx>, + projections: &[ty::PolyProjectionPredicate<'tcx>], + output: &mut String) { + if substs.types.is_empty() && projections.is_empty() { + return; + } + + output.push('<'); + + for &type_parameter in &substs.types { + push_unique_type_name(cx, type_parameter, output); + output.push_str(", "); + } + + for projection in projections { + let projection = projection.skip_binder(); + let name = token::get_ident_interner().get(projection.projection_ty.item_name); + output.push_str(&name[..]); + output.push_str("="); + push_unique_type_name(cx, projection.ty, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); +} + +fn push_def_id_as_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: Option<&Substs<'tcx>>, + output: &mut String) { + push_item_name(ccx, def_id, output); + + if let Some(substs) = substs { + push_type_params(ccx, substs, &[], output); + } +} + +fn def_id_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + def_id: DefId, + substs: Option<&Substs<'tcx>>) + -> String { + let mut output = String::new(); + push_def_id_as_string(ccx, def_id, substs, &mut output); + output +} + +fn type_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + ty: ty::Ty<'tcx>) + -> String { + let mut output = String::new(); + push_unique_type_name(ccx, ty, &mut output); + output +} + +impl<'tcx> TransItem<'tcx> { + + pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String { + let hir_map = &ccx.tcx().map; + + return match *self { + TransItem::DropGlue(t) => { + let mut s = String::with_capacity(32); + s.push_str("drop-glue "); + push_unique_type_name(ccx, t, &mut s); + s + } + TransItem::Fn { def_id, ref substs } => { + to_string_internal(ccx, "fn ", def_id, Some(substs)) + }, + TransItem::Static(node_id) => { + let def_id = hir_map.local_def_id(node_id); + to_string_internal(ccx, "static ", def_id, None) + }, + }; + + fn to_string_internal<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + prefix: &str, + def_id: DefId, + substs: Option<&Substs<'tcx>>) + -> String { + let mut result = String::with_capacity(32); + result.push_str(prefix); + push_def_id_as_string(ccx, def_id, substs, &mut result); + result + } + } + + fn to_raw_string(&self) -> String { + match *self { + TransItem::DropGlue(t) => { + format!("DropGlue({})", t as *const _ as usize) + } + TransItem::Fn { def_id, substs } => { + format!("Fn({:?}, {})", + def_id, + substs as *const _ as usize) + } + TransItem::Static(id) => { + format!("Static({:?})", id) + } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TransItemState { + PredictedAndGenerated, + PredictedButNotGenerated, + NotPredictedButGenerated, +} + +pub fn collecting_debug_information(ccx: &CrateContext) -> bool { + return cfg!(debug_assertions) && + ccx.sess().opts.debugging_opts.print_trans_items.is_some(); +} + +pub fn print_collection_results<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) { + use std::hash::{Hash, SipHasher, Hasher}; + + if !collecting_debug_information(ccx) { + return; + } + + fn hash(t: &T) -> u64 { + let mut s = SipHasher::new(); + t.hash(&mut s); + s.finish() + } + + let trans_items = ccx.translation_items().borrow(); + + { + // Check for duplicate item keys + let mut item_keys = FnvHashMap(); + + for (item, item_state) in trans_items.iter() { + let k = item.to_string(&ccx); + + if item_keys.contains_key(&k) { + let prev: (TransItem, TransItemState) = item_keys[&k]; + debug!("DUPLICATE KEY: {}", k); + debug!(" (1) {:?}, {:?}, hash: {}, raw: {}", + prev.0, + prev.1, + hash(&prev.0), + prev.0.to_raw_string()); + + debug!(" (2) {:?}, {:?}, hash: {}, raw: {}", + *item, + *item_state, + hash(item), + item.to_raw_string()); + } else { + item_keys.insert(k, (*item, *item_state)); + } + } + } + + let mut predicted_but_not_generated = FnvHashSet(); + let mut not_predicted_but_generated = FnvHashSet(); + let mut predicted = FnvHashSet(); + let mut generated = FnvHashSet(); + + for (item, item_state) in trans_items.iter() { + let item_key = item.to_string(&ccx); + + match *item_state { + TransItemState::PredictedAndGenerated => { + predicted.insert(item_key.clone()); + generated.insert(item_key); + } + TransItemState::PredictedButNotGenerated => { + predicted_but_not_generated.insert(item_key.clone()); + predicted.insert(item_key); + } + TransItemState::NotPredictedButGenerated => { + not_predicted_but_generated.insert(item_key.clone()); + generated.insert(item_key); + } + } + } + + debug!("Total number of translation items predicted: {}", predicted.len()); + debug!("Total number of translation items generated: {}", generated.len()); + debug!("Total number of translation items predicted but not generated: {}", + predicted_but_not_generated.len()); + debug!("Total number of translation items not predicted but generated: {}", + not_predicted_but_generated.len()); + + if generated.len() > 0 { + debug!("Failed to predict {}% of translation items", + (100 * not_predicted_but_generated.len()) / generated.len()); + } + if generated.len() > 0 { + debug!("Predict {}% too many translation items", + (100 * predicted_but_not_generated.len()) / generated.len()); + } + + debug!(""); + debug!("Not predicted but generated:"); + debug!("============================"); + for item in not_predicted_but_generated { + debug!(" - {}", item); + } + + debug!(""); + debug!("Predicted but not generated:"); + debug!("============================"); + for item in predicted_but_not_generated { + debug!(" - {}", item); + } +} diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 0f5d8dbd94db0..0d6324f3e899a 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -30,6 +30,7 @@ use middle::def::Def; use middle::def_id::DefId; use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; +use trans::collector::{self, TransItem}; use trans::common::{self, type_is_sized, ExprOrMethodCall, node_id_substs, C_nil, const_get_elt}; use trans::common::{CrateContext, C_integral, C_floating, C_bool, C_str_slice, C_bytes, val_ty}; use trans::common::{C_struct, C_undef, const_to_opt_int, const_to_opt_uint, VariantInfo, C_uint}; @@ -1016,6 +1017,11 @@ pub fn trans_static(ccx: &CrateContext, id: ast::NodeId, attrs: &[ast::Attribute]) -> Result { + + if collector::collecting_debug_information(ccx) { + ccx.record_translation_item_as_generated(TransItem::Static(id)); + } + unsafe { let _icx = push_ctxt("trans_static"); let g = base::get_item_val(ccx, id); diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index d4d2f01f77426..e8868cdbcc759 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -10,6 +10,7 @@ use llvm; use llvm::{ContextRef, ModuleRef, ValueRef, BuilderRef}; +use rustc::dep_graph::{DepNode, DepTrackingMap, DepTrackingMapConfig}; use middle::cstore::LinkMeta; use middle::def::ExportMap; use middle::def_id::DefId; @@ -23,6 +24,7 @@ use trans::debuginfo; use trans::declare; use trans::glue::DropGlueKind; use trans::monomorphize::MonoId; +use trans::collector::{TransItem, TransItemState}; use trans::type_::{Type, TypeNames}; use middle::subst::Substs; use middle::ty::{self, Ty}; @@ -33,6 +35,7 @@ use util::nodemap::{NodeMap, NodeSet, DefIdMap, FnvHashMap, FnvHashSet}; use std::ffi::CString; use std::cell::{Cell, RefCell}; +use std::marker::PhantomData; use std::ptr; use std::rc::Rc; use syntax::ast; @@ -75,6 +78,8 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { available_drop_glues: RefCell, String>>, use_dll_storage_attrs: bool, + + translation_items: RefCell, TransItemState>>, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -161,8 +166,23 @@ pub struct LocalCrateContext<'tcx> { /// Depth of the current type-of computation - used to bail out type_of_depth: Cell, - trait_cache: RefCell, - traits::Vtable<'tcx, ()>>>, + trait_cache: RefCell>>, +} + +// Implement DepTrackingMapConfig for `trait_cache` +pub struct TraitSelectionCache<'tcx> { + data: PhantomData<&'tcx ()> +} + +impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { + type Key = ty::PolyTraitRef<'tcx>; + type Value = traits::Vtable<'tcx, ()>; + fn to_dep_node(key: &ty::PolyTraitRef<'tcx>) -> DepNode { + ty::tls::with(|tcx| { + let lifted_key = tcx.lift(key).unwrap(); + lifted_key.to_poly_trait_predicate().dep_node() + }) + } } pub struct CrateContext<'a, 'tcx: 'a> { @@ -228,7 +248,6 @@ impl<'a, 'tcx> Iterator for CrateContextMaybeIterator<'a, 'tcx> { } } - unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { let llcx = llvm::LLVMContextCreate(); let mod_name = CString::new(mod_name).unwrap(); @@ -337,6 +356,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { check_drop_flag_for_sanity: check_drop_flag_for_sanity, available_drop_glues: RefCell::new(FnvHashMap()), use_dll_storage_attrs: use_dll_storage_attrs, + translation_items: RefCell::new(FnvHashMap()), }; for i in 0..local_count { @@ -478,7 +498,9 @@ impl<'tcx> LocalCrateContext<'tcx> { intrinsics: RefCell::new(FnvHashMap()), n_llvm_insns: Cell::new(0), type_of_depth: Cell::new(0), - trait_cache: RefCell::new(FnvHashMap()), + trait_cache: RefCell::new(DepTrackingMap::new(shared.tcx + .dep_graph + .clone())), }; local_ccx.int_type = Type::int(&local_ccx.dummy_ccx(shared)); @@ -752,8 +774,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { self.local.n_llvm_insns.set(self.local.n_llvm_insns.get() + 1); } - pub fn trait_cache(&self) -> &RefCell, - traits::Vtable<'tcx, ()>>> { + pub fn trait_cache(&self) -> &RefCell>> { &self.local.trait_cache } @@ -811,6 +832,24 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { pub fn mir_map(&self) -> &'b MirMap<'tcx> { self.shared.mir_map } + + pub fn translation_items(&self) -> &RefCell, TransItemState>> { + &self.shared.translation_items + } + + pub fn record_translation_item_as_generated(&self, cgi: TransItem<'tcx>) { + if self.sess().opts.debugging_opts.print_trans_items.is_none() { + return; + } + + let mut codegen_items = self.translation_items().borrow_mut(); + + if codegen_items.contains_key(&cgi) { + codegen_items.insert(cgi, TransItemState::PredictedAndGenerated); + } else { + codegen_items.insert(cgi, TransItemState::NotPredictedButGenerated); + } + } } pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index ada37c5d8dfbe..245bf7c3bea1f 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -55,9 +55,7 @@ use back::abi; use llvm::{self, ValueRef, TypeKind}; use middle::const_qualif::ConstQualif; use middle::def::Def; -use middle::lang_items::CoerceUnsizedTraitLangItem; -use middle::subst::{Substs, VecPerParamSpace}; -use middle::traits; +use middle::subst::Substs; use trans::{_match, adt, asm, base, callee, closure, consts, controlflow}; use trans::base::*; use trans::build::*; @@ -151,6 +149,20 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }, } } + + // If we see a const here, that's because it evaluates to a type with zero size. We + // should be able to just discard it, since const expressions are guaranteed not to + // have side effects. This seems to be reached through tuple struct constructors being + // passed zero-size constants. + if let hir::ExprPath(..) = expr.node { + match bcx.def(expr.id) { + Def::Const(_) | Def::AssociatedConst(_) => { + return bcx; + } + _ => {} + } + } + // Even if we don't have a value to emit, and the expression // doesn't have any side-effects, we still have to translate the // body of any closures. @@ -162,7 +174,7 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, match expr.node { hir::ExprPath(..) => { match bcx.def(expr.id) { - Def::Const(did) => { + Def::Const(did) | Def::AssociatedConst(did) => { let empty_substs = bcx.tcx().mk_substs(Substs::trans_empty()); let const_expr = consts::get_const_expr(bcx.ccx(), did, expr, empty_substs); @@ -500,24 +512,7 @@ fn coerce_unsized<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let source = unpack_datum!(bcx, source.to_ref_datum(bcx)); assert!(target.kind.is_by_ref()); - let trait_substs = Substs::erased(VecPerParamSpace::new(vec![target.ty], - vec![source.ty], - Vec::new())); - let trait_ref = ty::Binder(ty::TraitRef { - def_id: langcall(bcx, Some(span), "coercion", - CoerceUnsizedTraitLangItem), - substs: bcx.tcx().mk_substs(trait_substs) - }); - - let kind = match fulfill_obligation(bcx.ccx(), span, trait_ref) { - traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { - bcx.tcx().custom_coerce_unsized_kind(impl_def_id) - } - vtable => { - bcx.sess().span_bug(span, &format!("invalid CoerceUnsized vtable: {:?}", - vtable)); - } - }; + let kind = custom_coerce_unsize_info(bcx.ccx(), source.ty, target.ty); let repr_source = adt::represent_type(bcx.ccx(), source.ty); let src_fields = match &*repr_source { @@ -915,7 +910,7 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let lval = Lvalue::new("expr::trans_def"); DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval))) } - Def::Const(_) => { + Def::Const(_) | Def::AssociatedConst(_) => { bcx.sess().span_bug(ref_expr.span, "constant expression should not reach expr::trans_def") } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index a1165ffe171d0..de4867398b99e 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -28,6 +28,7 @@ use trans::build::*; use trans::callee; use trans::cleanup; use trans::cleanup::CleanupMethods; +use trans::collector::{self, TransItem}; use trans::common::*; use trans::debuginfo::DebugLoc; use trans::declare; @@ -88,7 +89,7 @@ pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { +pub fn type_needs_drop<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { tcx.type_needs_drop_given_env(ty, &tcx.empty_parameter_environment()) } @@ -496,6 +497,13 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueKind<'tcx>) -> Block<'blk, 'tcx> { let t = g.ty(); + + if collector::collecting_debug_information(bcx.ccx()) { + bcx.ccx() + .record_translation_item_as_generated(TransItem::DropGlue(bcx.tcx() + .erase_regions(&t))); + } + let skip_dtor = match g { DropGlueKind::Ty(_) => false, DropGlueKind::TyContents(_) => true }; // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_drop_glue"); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 4695595d16f30..b8f577f654c68 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -495,7 +495,23 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, impl_def_id: id, substs, nested: _ }) => { - emit_vtable_methods(ccx, id, substs, param_substs).into_iter() + let nullptr = C_null(Type::nil(ccx).ptr_to()); + get_vtable_methods(ccx, id, substs) + .into_iter() + .map(|opt_mth| { + match opt_mth { + Some(mth) => { + trans_fn_ref_with_substs(ccx, + mth.method.def_id, + ExprId(0), + param_substs, + mth.substs).val + } + None => nullptr + } + }) + .collect::>() + .into_iter() } traits::VtableClosure( traits::VtableClosureData { @@ -549,18 +565,14 @@ pub fn get_vtable<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, vtable } -fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - impl_id: DefId, - substs: subst::Substs<'tcx>, - param_substs: &'tcx subst::Substs<'tcx>) - -> Vec +pub fn get_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, + impl_id: DefId, + substs: subst::Substs<'tcx>) + -> Vec>> { let tcx = ccx.tcx(); - debug!("emit_vtable_methods(impl_id={:?}, substs={:?}, param_substs={:?})", - impl_id, - substs, - param_substs); + debug!("get_vtable_methods(impl_id={:?}, substs={:?}", impl_id, substs); let trt_id = match tcx.impl_trait_ref(impl_id) { Some(t_id) => t_id.def_id, @@ -570,7 +582,6 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, tcx.populate_implementations_for_trait_if_necessary(trt_id); - let nullptr = C_null(Type::nil(ccx).ptr_to()); let trait_item_def_ids = tcx.trait_item_def_ids(trt_id); trait_item_def_ids .iter() @@ -587,7 +598,7 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // method could never be called from this object, just supply // null. .map(|trait_method_def_id| { - debug!("emit_vtable_methods: trait_method_def_id={:?}", + debug!("get_vtable_methods: trait_method_def_id={:?}", trait_method_def_id); let trait_method_type = match tcx.impl_or_trait_item(trait_method_def_id) { @@ -598,18 +609,18 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, // Some methods cannot be called on an object; skip those. if !traits::is_vtable_safe_method(tcx, trt_id, &trait_method_type) { - debug!("emit_vtable_methods: not vtable safe"); - return nullptr; + debug!("get_vtable_methods: not vtable safe"); + return None; } - debug!("emit_vtable_methods: trait_method_type={:?}", + debug!("get_vtable_methods: trait_method_type={:?}", trait_method_type); // The substitutions we have are on the impl, so we grab // the method type from the impl to substitute into. let mth = tcx.get_impl_method(impl_id, substs.clone(), name); - debug!("emit_vtable_methods: mth={:?}", mth); + debug!("get_vtable_methods: mth={:?}", mth); // If this is a default method, it's possible that it // relies on where clauses that do not hold for this @@ -619,16 +630,12 @@ fn emit_vtable_methods<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if mth.is_provided { let predicates = mth.method.predicates.predicates.subst(tcx, &mth.substs); if !normalize_and_test_predicates(ccx, predicates.into_vec()) { - debug!("emit_vtable_methods: predicates do not hold"); - return nullptr; + debug!("get_vtable_methods: predicates do not hold"); + return None; } } - trans_fn_ref_with_substs(ccx, - mth.method.def_id, - ExprId(0), - param_substs, - mth.substs).val + Some(mth) }) .collect() } diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index d87c17cbf88d4..898f260f8df43 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -58,6 +58,7 @@ mod _match; mod meth; mod mir; mod monomorphize; +mod collector; mod tvec; mod type_; mod type_of; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a8697f45d9156..af343e0b0790f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1072,8 +1072,8 @@ fn report_cast_to_unsized_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, match fcx.tcx().sess.codemap().span_to_snippet(t_span) { Ok(s) => { err.span_suggestion(t_span, - "try casting to a `Box` instead:", - format!("Box<{}>", s)); + "try casting to a `Box` instead:", + format!("Box<{}>", s)); }, Err(_) => span_help!(err, t_span, "did you mean `Box<{}>`?", tstr), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index b0df209d3dc52..9d5189cfd0b12 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -460,7 +460,7 @@ impl fmt::Display for clean::Type { [] => primitive_link(f, clean::PrimitiveTuple, "()"), [ref one] => { try!(primitive_link(f, clean::PrimitiveTuple, "(")); - try!(write!(f, "{}", one)); + try!(write!(f, "{},", one)); primitive_link(f, clean::PrimitiveTuple, ")") } many => { diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 9d505607a60c4..ee744b868dd73 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -60,6 +60,18 @@ use vec::Vec; /// } /// # } /// ``` +/// +/// # Safety +/// +/// `CString` is intended for working with traditional C-style strings +/// (a sequence of non-null bytes terminated by a single null byte); the +/// primary use case for these kinds of strings is interoperating with C-like +/// code. Often you will need to transfer ownership to/from that external +/// code. It is strongly recommended that you thoroughly read through the +/// documentation of `CString` before use, as improper ownership management +/// of `CString` instances can lead to invalid memory accesses, memory leaks, +/// and other memory errors. + #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct CString { @@ -207,11 +219,11 @@ impl CString { CString { inner: v.into_boxed_slice() } } - /// Retakes ownership of a CString that was transferred to C. + /// Retakes ownership of a `CString` that was transferred to C. /// - /// The only appropriate argument is a pointer obtained by calling - /// `into_raw`. The length of the string will be recalculated - /// using the pointer. + /// This should only ever be called with a pointer that was earlier + /// obtained by calling `into_raw` on a `CString`. Additionally, the length + /// of the string will be recalculated from the pointer. #[stable(feature = "cstr_memory", since = "1.4.0")] pub unsafe fn from_raw(ptr: *mut c_char) -> CString { let len = libc::strlen(ptr) + 1; // Including the NUL byte diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 9e28cf06d619a..0faa1465c324a 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -15,7 +15,6 @@ use cmp; #[cfg(not(target_env = "newlib"))] use ffi::CString; use io; -use libc::PTHREAD_STACK_MIN; use libc; use mem; use ptr; @@ -339,14 +338,20 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { }); match unsafe { __pthread_get_minstack } { - None => PTHREAD_STACK_MIN as usize, + None => libc::PTHREAD_STACK_MIN as usize, Some(f) => unsafe { f(attr) as usize }, } } // No point in looking up __pthread_get_minstack() on non-glibc // platforms. -#[cfg(not(target_os = "linux"))] +#[cfg(all(not(target_os = "linux"), + not(target_os = "netbsd")))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN as usize +} + +#[cfg(target_os = "netbsd")] fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - PTHREAD_STACK_MIN as usize + 2048 // just a guess } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index 432c1688536bb..ca01623fef90f 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -23,6 +23,7 @@ use std::cell::{Cell, RefCell}; use std::ops::{Add, Sub}; use std::path::Path; use std::rc::Rc; +use std::cmp; use std::{fmt, fs}; use std::io::{self, Read}; @@ -31,6 +32,8 @@ use serialize::{Encodable, Decodable, Encoder, Decoder}; use ast::Name; +use errors::emitter::MAX_HIGHLIGHT_LINES; + // _____________________________________________________________________________ // Pos, BytePos, CharPos // @@ -42,7 +45,7 @@ pub trait Pos { /// A byte offset. Keep this small (currently 32-bits), as AST contains /// a lot of them. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub struct BytePos(pub u32); /// A character offset. Because of multibyte utf8 characters, a byte offset @@ -109,7 +112,7 @@ impl Sub for CharPos { } // _____________________________________________________________________________ -// Span, Spanned +// Span, MultiSpan, Spanned // /// Spans represent a region of code, used for error reporting. Positions in spans @@ -129,6 +132,15 @@ pub struct Span { pub expn_id: ExpnId } +/// Spans are converted to MultiSpans just before error reporting, either automatically, +/// generated by line grouping, or manually constructed. +/// In the latter case care should be taken to ensure that spans are ordered, disjoint, +/// and point into the same FileMap. +#[derive(Clone)] +pub struct MultiSpan { + pub spans: Vec +} + pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), expn_id: NO_EXPANSION }; // Generic span to be used for code originating from the command line @@ -145,6 +157,33 @@ impl Span { pub fn contains(self, other: Span) -> bool { self.lo <= other.lo && other.hi <= self.hi } + + /// Returns `Some(span)`, a union of `self` and `other`, on overlap. + pub fn merge(self, other: Span) -> Option { + if self.expn_id != other.expn_id { + return None; + } + + if (self.lo <= other.lo && self.hi > other.lo) || + (self.lo >= other.lo && self.lo < other.hi) { + Some(Span { + lo: cmp::min(self.lo, other.lo), + hi: cmp::max(self.hi, other.hi), + expn_id: self.expn_id, + }) + } else { + None + } + } + + /// Returns `Some(span)`, where the start is trimmed by the end of `other` + pub fn trim_start(self, other: Span) -> Option { + if self.hi > other.hi { + Some(Span { lo: cmp::max(self.lo, other.hi), .. self }) + } else { + None + } + } } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] @@ -236,6 +275,102 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span { } } +impl MultiSpan { + pub fn new() -> MultiSpan { + MultiSpan { spans: Vec::new() } + } + + pub fn to_span_bounds(&self) -> Span { + assert!(!self.spans.is_empty()); + let Span { lo, expn_id, .. } = *self.spans.first().unwrap(); + let Span { hi, .. } = *self.spans.last().unwrap(); + Span { lo: lo, hi: hi, expn_id: expn_id } + } + + /// Merges or inserts the given span into itself. + pub fn push_merge(&mut self, mut sp: Span) { + let mut idx_merged = None; + + for idx in 0.. { + let cur = match self.spans.get(idx) { + Some(s) => *s, + None => break, + }; + // Try to merge with a contained Span + if let Some(union) = cur.merge(sp) { + self.spans[idx] = union; + sp = union; + idx_merged = Some(idx); + break; + } + // Or insert into the first sorted position + if sp.hi <= cur.lo { + self.spans.insert(idx, sp); + idx_merged = Some(idx); + break; + } + } + if let Some(idx) = idx_merged { + // Merge with spans trailing the insertion/merging position + while (idx + 1) < self.spans.len() { + if let Some(union) = self.spans[idx + 1].merge(sp) { + self.spans[idx] = union; + self.spans.remove(idx + 1); + } else { + break; + } + } + } else { + self.spans.push(sp); + } + } + + /// Inserts the given span into itself, for use with `end_highlight_lines`. + pub fn push_trim(&mut self, mut sp: Span) { + let mut prev = mk_sp(BytePos(0), BytePos(0)); + + if let Some(first) = self.spans.get_mut(0) { + if first.lo > sp.lo { + // Prevent us here from spanning fewer lines + // because of trimming the start of the span + // (this should not be visible, because this method ought + // to not be used in conjunction with `highlight_lines`) + first.lo = sp.lo; + } + } + + for idx in 0.. { + if let Some(sp_trim) = sp.trim_start(prev) { + // Implies `sp.hi > prev.hi` + let cur = match self.spans.as_slice().get(idx) { + Some(s) => *s, + None => { + sp = sp_trim; + break; + } + }; + // `cur` may overlap with `sp_trim` + if let Some(cur_trim) = cur.trim_start(sp_trim) { + // Implies `sp.hi < cur.hi` + self.spans.insert(idx, sp_trim); + self.spans[idx + 1] = cur_trim; + return; + } else if sp.hi == cur.hi { + return; + } + prev = cur; + } + } + self.spans.push(sp); + } +} + +impl From for MultiSpan { + fn from(span: Span) -> MultiSpan { + MultiSpan { spans: vec![span] } + } +} + // _____________________________________________________________________________ // Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos // @@ -1020,6 +1155,59 @@ impl CodeMap { } } + /// Groups and sorts spans by lines into `MultiSpan`s, where `push` adds them to their group, + /// specifying the unification behaviour for overlapping spans. + /// Spans overflowing a line are put into their own one-element-group. + pub fn custom_group_spans(&self, mut spans: Vec, push: F) -> Vec + where F: Fn(&mut MultiSpan, Span) + { + spans.sort_by(|a, b| a.lo.cmp(&b.lo)); + let mut groups = Vec::::new(); + let mut overflowing = vec![]; + let mut prev_expn = ExpnId(!2u32); + let mut prev_file = !0usize; + let mut prev_line = !0usize; + let mut err_size = 0; + + for sp in spans { + let line = self.lookup_char_pos(sp.lo).line; + let line_hi = self.lookup_char_pos(sp.hi).line; + if line != line_hi { + overflowing.push(sp.into()); + continue + } + let file = self.lookup_filemap_idx(sp.lo); + + if err_size < MAX_HIGHLIGHT_LINES && sp.expn_id == prev_expn && file == prev_file { + // `push` takes care of sorting, trimming, and merging + push(&mut groups.last_mut().unwrap(), sp); + if line != prev_line { + err_size += 1; + } + } else { + groups.push(sp.into()); + err_size = 1; + } + prev_expn = sp.expn_id; + prev_file = file; + prev_line = line; + } + groups.extend(overflowing); + groups + } + + /// Groups and sorts spans by lines into `MultiSpan`s, merging overlapping spans. + /// Spans overflowing a line are put into their own one-element-group. + pub fn group_spans(&self, spans: Vec) -> Vec { + self.custom_group_spans(spans, |msp, sp| msp.push_merge(sp)) + } + + /// Like `group_spans`, but trims overlapping spans instead of + /// merging them (for use with `end_highlight_lines`) + pub fn end_group_spans(&self, spans: Vec) -> Vec { + self.custom_group_spans(spans, |msp, sp| msp.push_trim(sp)) + } + pub fn get_filemap(&self, filename: &str) -> Rc { for fm in self.files.borrow().iter() { if filename == fm.name { @@ -1351,7 +1539,7 @@ mod tests { fn span_from_selection(input: &str, selection: &str) -> Span { assert_eq!(input.len(), selection.len()); let left_index = selection.find('^').unwrap() as u32; - let right_index = selection.rfind('~').unwrap() as u32; + let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index); Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } } @@ -1511,4 +1699,73 @@ r"blork2.rs:2:1: 2:12 "; assert_eq!(sstr, res_str); } + + #[test] + fn t13() { + // Test that collecting multiple spans into line-groups works correctly + let cm = CodeMap::new(); + let inp = "_aaaaa__bbb\nvv\nw\nx\ny\nz\ncccccc__ddddee__"; + let sp1 = " ^~~~~ \n \n \n \n \n \n "; + let sp2 = " \n \n \n \n \n^\n "; + let sp3 = " ^~~\n~~\n \n \n \n \n "; + let sp4 = " \n \n \n \n \n \n^~~~~~ "; + let sp5 = " \n \n \n \n \n \n ^~~~ "; + let sp6 = " \n \n \n \n \n \n ^~~~ "; + let sp_trim = " \n \n \n \n \n \n ^~ "; + let sp_merge = " \n \n \n \n \n \n ^~~~~~ "; + let sp7 = " \n ^\n \n \n \n \n "; + let sp8 = " \n \n^\n \n \n \n "; + let sp9 = " \n \n \n^\n \n \n "; + let sp10 = " \n \n \n \n^\n \n "; + + let span = |sp, expected| { + let sp = span_from_selection(inp, sp); + assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); + sp + }; + + cm.new_filemap_and_lines("blork.rs", inp); + let sp1 = span(sp1, "aaaaa"); + let sp2 = span(sp2, "z"); + let sp3 = span(sp3, "bbb\nvv"); + let sp4 = span(sp4, "cccccc"); + let sp5 = span(sp5, "dddd"); + let sp6 = span(sp6, "ddee"); + let sp7 = span(sp7, "v"); + let sp8 = span(sp8, "w"); + let sp9 = span(sp9, "x"); + let sp10 = span(sp10, "y"); + let sp_trim = span(sp_trim, "ee"); + let sp_merge = span(sp_merge, "ddddee"); + + let spans = vec![sp5, sp2, sp4, sp9, sp10, sp7, sp3, sp8, sp1, sp6]; + + macro_rules! check_next { + ($groups: expr, $expected: expr) => ({ + let actual = $groups.next().map(|g|&g.spans[..]); + let expected = $expected; + println!("actual:\n{:?}\n", actual); + println!("expected:\n{:?}\n", expected); + assert_eq!(actual, expected.as_ref().map(|x|&x[..])); + }); + } + + let _groups = cm.group_spans(spans.clone()); + let it = &mut _groups.iter(); + + check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2])); + // New group because we're exceeding MAX_HIGHLIGHT_LINES + check_next!(it, Some([sp4, sp_merge])); + check_next!(it, Some([sp3])); + check_next!(it, None::<[Span; 0]>); + + let _groups = cm.end_group_spans(spans); + let it = &mut _groups.iter(); + + check_next!(it, Some([sp1, sp7, sp8, sp9, sp10, sp2])); + // New group because we're exceeding MAX_HIGHLIGHT_LINES + check_next!(it, Some([sp4, sp5, sp_trim])); + check_next!(it, Some([sp3])); + check_next!(it, None::<[Span; 0]>); + } } diff --git a/src/libsyntax/diagnostics/plugin.rs b/src/libsyntax/diagnostics/plugin.rs index d17ca3892dc68..6e389e83591ea 100644 --- a/src/libsyntax/diagnostics/plugin.rs +++ b/src/libsyntax/diagnostics/plugin.rs @@ -168,20 +168,24 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt, }; // Output error metadata to `tmp/extended-errors//.json` - let target_triple = env::var("CFG_COMPILER_HOST_TRIPLE") - .ok().expect("unable to determine target arch from $CFG_COMPILER_HOST_TRIPLE"); - - with_registered_diagnostics(|diagnostics| { - if let Err(e) = output_metadata(ecx, - &target_triple, - &crate_name.name.as_str(), - &diagnostics) { - ecx.span_bug(span, &format!( - "error writing metadata for triple `{}` and crate `{}`, error: {}, cause: {:?}", - target_triple, crate_name, e.description(), e.cause() - )); - } - }); + if let Ok(target_triple) = env::var("CFG_COMPILER_HOST_TRIPLE") { + with_registered_diagnostics(|diagnostics| { + if let Err(e) = output_metadata(ecx, + &target_triple, + &crate_name.name.as_str(), + &diagnostics) { + ecx.span_bug(span, &format!( + "error writing metadata for triple `{}` and crate `{}`, error: {}, \ + cause: {:?}", + target_triple, crate_name, e.description(), e.cause() + )); + } + }); + } else { + ecx.span_err(span, &format!( + "failed to write metadata for crate `{}` because $CFG_COMPILER_HOST_TRIPLE is not set", + crate_name)); + } // Construct the output expression. let (count, expr) = diff --git a/src/libsyntax/errors/emitter.rs b/src/libsyntax/errors/emitter.rs index 51013d68930ea..c1239bfd66db8 100644 --- a/src/libsyntax/errors/emitter.rs +++ b/src/libsyntax/errors/emitter.rs @@ -10,10 +10,10 @@ use self::Destination::*; -use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, DUMMY_SP, Pos, Span}; +use codemap::{self, COMMAND_LINE_SP, COMMAND_LINE_EXPN, DUMMY_SP, Pos, Span, MultiSpan}; use diagnostics; -use errors::{Level, RenderSpan, DiagnosticBuilder}; +use errors::{Level, RenderSpan, CodeSuggestion, DiagnosticBuilder}; use errors::RenderSpan::*; use errors::Level::*; @@ -23,25 +23,27 @@ use std::io; use std::rc::Rc; use term; - pub trait Emitter { - fn emit(&mut self, span: Option, msg: &str, code: Option<&str>, lvl: Level); - fn custom_emit(&mut self, sp: RenderSpan, msg: &str, lvl: Level); + fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level); + fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, lvl: Level); /// Emit a structured diagnostic. fn emit_struct(&mut self, db: &DiagnosticBuilder) { - self.emit(db.span, &db.message, db.code.as_ref().map(|s| &**s), db.level); + self.emit(db.span.as_ref(), &db.message, db.code.as_ref().map(|s| &**s), db.level); for child in &db.children { match child.render_span { - Some(ref sp) => self.custom_emit(sp.clone(), &child.message, child.level), - None => self.emit(child.span, &child.message, None, child.level), + Some(ref sp) => self.custom_emit(sp, &child.message, child.level), + None => self.emit(child.span.as_ref(), &child.message, None, child.level), } } } } /// maximum number of lines we will print for each error; arbitrary. -const MAX_LINES: usize = 6; +pub const MAX_HIGHLIGHT_LINES: usize = 6; + +/// maximum number of lines we will print for each span; arbitrary. +const MAX_SP_LINES: usize = 6; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { @@ -68,18 +70,18 @@ pub struct BasicEmitter { impl Emitter for BasicEmitter { fn emit(&mut self, - sp: Option, + msp: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level) { - assert!(sp.is_none(), "BasicEmitter can't handle spans"); + assert!(msp.is_none(), "BasicEmitter can't handle spans"); if let Err(e) = print_diagnostic(&mut self.dst, "", lvl, msg, code) { panic!("failed to print diagnostics: {:?}", e); } } - fn custom_emit(&mut self, _: RenderSpan, _: &str, _: Level) { + fn custom_emit(&mut self, _: &RenderSpan, _: &str, _: Level) { panic!("BasicEmitter can't handle custom_emit"); } } @@ -103,14 +105,16 @@ pub struct EmitterWriter { impl Emitter for EmitterWriter { fn emit(&mut self, - sp: Option, + msp: Option<&MultiSpan>, msg: &str, code: Option<&str>, lvl: Level) { - let error = match sp { - Some(COMMAND_LINE_SP) => self.emit_(FileLine(COMMAND_LINE_SP), msg, code, lvl), - Some(DUMMY_SP) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code), - Some(sp) => self.emit_(FullSpan(sp), msg, code, lvl), + let error = match msp.map(|s|(s.to_span_bounds(), s)) { + Some((COMMAND_LINE_SP, msp)) => { + self.emit_(&FileLine(msp.clone()), msg, code, lvl) + }, + Some((DUMMY_SP, _)) | None => print_diagnostic(&mut self.dst, "", lvl, msg, code), + Some((_, msp)) => self.emit_(&FullSpan(msp.clone()), msg, code, lvl), }; if let Err(e) = error { @@ -119,10 +123,10 @@ impl Emitter for EmitterWriter { } fn custom_emit(&mut self, - sp: RenderSpan, + rsp: &RenderSpan, msg: &str, lvl: Level) { - if let Err(e) = self.emit_(sp, msg, None, lvl) { + if let Err(e) = self.emit_(rsp, msg, None, lvl) { panic!("failed to print diagnostics: {:?}", e); } } @@ -163,114 +167,93 @@ impl EmitterWriter { } fn emit_(&mut self, - rsp: RenderSpan, + rsp: &RenderSpan, msg: &str, code: Option<&str>, lvl: Level) -> io::Result<()> { - let sp = rsp.span(); + let msp = rsp.span(); + let bounds = msp.to_span_bounds(); // We cannot check equality directly with COMMAND_LINE_SP // since PartialEq is manually implemented to ignore the ExpnId - let ss = if sp.expn_id == COMMAND_LINE_EXPN { + let ss = if bounds.expn_id == COMMAND_LINE_EXPN { "".to_string() - } else if let EndSpan(_) = rsp { - let span_end = Span { lo: sp.hi, hi: sp.hi, expn_id: sp.expn_id}; + } else if let EndSpan(_) = *rsp { + let span_end = Span { lo: bounds.hi, hi: bounds.hi, expn_id: bounds.expn_id}; self.cm.span_to_string(span_end) } else { - self.cm.span_to_string(sp) + self.cm.span_to_string(bounds) }; try!(print_diagnostic(&mut self.dst, &ss[..], lvl, msg, code)); - match rsp { + match *rsp { FullSpan(_) => { - let lines = self.cm.span_to_lines(sp); - try!(self.highlight_lines(sp, lvl, lines)); - try!(self.print_macro_backtrace(sp)); + try!(self.highlight_lines(msp, lvl)); + try!(self.print_macro_backtrace(bounds)); } EndSpan(_) => { - let lines = self.cm.span_to_lines(sp); - try!(self.end_highlight_lines(sp, lvl, lines)); - try!(self.print_macro_backtrace(sp)); + try!(self.end_highlight_lines(msp, lvl)); + try!(self.print_macro_backtrace(bounds)); } - Suggestion(_, ref suggestion) => { - try!(self.highlight_suggestion(sp, suggestion)); - try!(self.print_macro_backtrace(sp)); + Suggestion(ref suggestion) => { + try!(self.highlight_suggestion(suggestion)); + try!(self.print_macro_backtrace(bounds)); } FileLine(..) => { // no source text in this case! } } - match code { - Some(code) => - match self.registry.as_ref().and_then(|registry| registry.find_description(code)) { - Some(_) => { - try!(print_diagnostic(&mut self.dst, &ss[..], Help, - &format!("run `rustc --explain {}` to see a \ - detailed explanation", code), None)); - } - None => () - }, - None => (), + if let Some(code) = code { + if let Some(_) = self.registry.as_ref() + .and_then(|registry| registry.find_description(code)) { + try!(print_diagnostic(&mut self.dst, &ss[..], Help, + &format!("run `rustc --explain {}` to see a \ + detailed explanation", code), None)); + } } Ok(()) } - fn highlight_suggestion(&mut self, - sp: Span, - suggestion: &str) - -> io::Result<()> + fn highlight_suggestion(&mut self, suggestion: &CodeSuggestion) -> io::Result<()> { - let lines = self.cm.span_to_lines(sp).unwrap(); + let lines = self.cm.span_to_lines(suggestion.msp.to_span_bounds()).unwrap(); assert!(!lines.lines.is_empty()); - // To build up the result, we want to take the snippet from the first - // line that precedes the span, prepend that with the suggestion, and - // then append the snippet from the last line that trails the span. - let fm = &lines.file; - - let first_line = &lines.lines[0]; - let prefix = fm.get_line(first_line.line_index) - .map(|l| &l[..first_line.start_col.0]) - .unwrap_or(""); + let complete = suggestion.splice_lines(&self.cm); + let line_count = cmp::min(lines.lines.len(), MAX_HIGHLIGHT_LINES); + let display_lines = &lines.lines[..line_count]; - let last_line = lines.lines.last().unwrap(); - let suffix = fm.get_line(last_line.line_index) - .map(|l| &l[last_line.end_col.0..]) - .unwrap_or(""); - - let complete = format!("{}{}{}", prefix, suggestion, suffix); + let fm = &*lines.file; + // Calculate the widest number to format evenly + let max_digits = line_num_max_digits(display_lines.last().unwrap()); // print the suggestion without any line numbers, but leave // space for them. This helps with lining up with previous // snippets from the actual error being reported. - let fm = &*lines.file; let mut lines = complete.lines(); - for (line, line_index) in lines.by_ref().take(MAX_LINES).zip(first_line.line_index..) { - let elided_line_num = format!("{}", line_index+1); + for line in lines.by_ref().take(MAX_HIGHLIGHT_LINES) { try!(write!(&mut self.dst, "{0}:{1:2$} {3}\n", - fm.name, "", elided_line_num.len(), line)); + fm.name, "", max_digits, line)); } // if we elided some lines, add an ellipsis - if lines.next().is_some() { - let elided_line_num = format!("{}", first_line.line_index + MAX_LINES + 1); + if let Some(_) = lines.next() { try!(write!(&mut self.dst, "{0:1$} {0:2$} ...\n", - "", fm.name.len(), elided_line_num.len())); + "", fm.name.len(), max_digits)); } Ok(()) } fn highlight_lines(&mut self, - sp: Span, - lvl: Level, - lines: codemap::FileLinesResult) + msp: &MultiSpan, + lvl: Level) -> io::Result<()> { - let lines = match lines { + let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { Ok(lines) => lines, Err(_) => { try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n")); @@ -279,73 +262,111 @@ impl EmitterWriter { }; let fm = &*lines.file; + if let None = fm.src { + return Ok(()); + } - let line_strings: Option> = - lines.lines.iter() - .map(|info| fm.get_line(info.line_index)) - .collect(); - - let line_strings = match line_strings { - None => { return Ok(()); } - Some(line_strings) => line_strings - }; - - // Display only the first MAX_LINES lines. - let all_lines = lines.lines.len(); - let display_lines = cmp::min(all_lines, MAX_LINES); - let display_line_infos = &lines.lines[..display_lines]; - let display_line_strings = &line_strings[..display_lines]; + let display_line_infos = &lines.lines[..]; + assert!(display_line_infos.len() > 0); // Calculate the widest number to format evenly and fix #11715 - assert!(display_line_infos.len() > 0); - let mut max_line_num = display_line_infos[display_line_infos.len() - 1].line_index + 1; - let mut digits = 0; - while max_line_num > 0 { - max_line_num /= 10; - digits += 1; - } + let digits = line_num_max_digits(display_line_infos.last().unwrap()); + let first_line_index = display_line_infos.first().unwrap().line_index; - // Print the offending lines - for (line_info, line) in display_line_infos.iter().zip(display_line_strings) { - try!(write!(&mut self.dst, "{}:{:>width$} {}\n", - fm.name, - line_info.line_index + 1, - line, - width=digits)); - } + let skip = fm.name.chars().count() + digits + 2; - // If we elided something, put an ellipsis. - if display_lines < all_lines { - let last_line_index = display_line_infos.last().unwrap().line_index; - let s = format!("{}:{} ", fm.name, last_line_index + 1); - try!(write!(&mut self.dst, "{0:1$}...\n", "", s.len())); - } + let mut spans = msp.spans.iter().peekable(); + let mut lines = display_line_infos.iter(); + let mut prev_line_index = first_line_index.wrapping_sub(1); - // FIXME (#3260) - // If there's one line at fault we can easily point to the problem - if lines.lines.len() == 1 { - let lo = self.cm.lookup_char_pos(sp.lo); - let mut digits = 0; - let mut num = (lines.lines[0].line_index + 1) / 10; + // Display at most MAX_HIGHLIGHT_LINES lines. + let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; - // how many digits must be indent past? - while num > 0 { num /= 10; digits += 1; } + // To emit a overflowed spans code-lines *AFTER* the rendered spans + let mut overflowed_buf = String::new(); + let mut overflowed = false; - let mut s = String::new(); - // Skip is the number of characters we need to skip because they are - // part of the 'filename:line ' part of the previous line. - let skip = fm.name.chars().count() + digits + 3; - for _ in 0..skip { - s.push(' '); + // FIXME (#8706) + 'l: loop { + if remaining_err_lines <= 0 { + break; } - if let Some(orig) = fm.get_line(lines.lines[0].line_index) { - let mut col = skip; - let mut lastc = ' '; - let mut iter = orig.chars().enumerate(); - for (pos, ch) in iter.by_ref() { + let line = match lines.next() { + Some(l) => l, + None => break, + }; + + // Skip is the number of characters we need to skip because they are + // part of the 'filename:line ' part of the code line. + let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); + let mut col = skip; + let mut lastc = ' '; + + let cur_line_str = fm.get_line(line.line_index).unwrap(); + let mut line_chars = cur_line_str.chars().enumerate().peekable(); + let mut line_spans = 0; + + // Assemble spans for this line + loop { + // Peek here to preserve the span if it doesn't belong to this line + let sp = match spans.peek() { + Some(sp) => **sp, + None => break, + }; + let lo = self.cm.lookup_char_pos(sp.lo); + let hi = self.cm.lookup_char_pos(sp.hi); + let line_num = line.line_index + 1; + + if !(lo.line <= line_num && hi.line >= line_num) { + // This line is not contained in the span + if overflowed { + // Never elide the final line of an overflowed span + prev_line_index = line.line_index - 1; + overflowed = false; + break; + } + + if line_spans == 0 { + continue 'l; + } else { + // This line is finished, now render the spans we've assembled + break; + } + } + spans.next(); + line_spans += 1; + + if lo.line != hi.line { + // Assemble extra code lines to be emitted after this lines spans + // (substract `2` because the first and last line are rendered normally) + let max_lines = cmp::min(remaining_err_lines, MAX_SP_LINES) - 2; + prev_line_index = line.line_index; + let count = cmp::min((hi.line - lo.line - 1), max_lines); + for _ in 0..count { + let line = match lines.next() { + Some(l) => l, + None => break, + }; + let line_str = fm.get_line(line.line_index).unwrap(); + overflowed_buf.push_str(&format!("{}:{:>width$} {}\n", + fm.name, + line.line_index + 1, + line_str, + width=digits)); + remaining_err_lines -= 1; + prev_line_index += 1 + } + // Remember that the span overflowed to ensure + // that we emit its last line exactly once + // (other spans may, or may not, start on it) + overflowed = true; + break; + } + + for (pos, ch) in line_chars.by_ref() { lastc = ch; if pos >= lo.col.to_usize() { break; } - // Whenever a tab occurs on the previous line, we insert one on + // Whenever a tab occurs on the code line, we insert one on // the error-point-squiggly-line as well (instead of a space). // That way the squiggly line will usually appear in the correct // position. @@ -361,8 +382,8 @@ impl EmitterWriter { } } - try!(write!(&mut self.dst, "{}", s)); - let mut s = String::from("^"); + s.push('^'); + let col_ptr = col; let count = match lastc { // Most terminals have a tab stop every eight columns by default '\t' => 8 - col%8, @@ -373,7 +394,13 @@ impl EmitterWriter { let hi = self.cm.lookup_char_pos(sp.hi); if hi.col != lo.col { - for (pos, ch) in iter { + let mut chars = line_chars.by_ref(); + loop { + // We peek here to preserve the value for the next span + let (pos, ch) = match chars.peek() { + Some(elem) => *elem, + None => break, + }; if pos >= hi.col.to_usize() { break; } let count = match ch { '\t' => 8 - col%8, @@ -381,34 +408,63 @@ impl EmitterWriter { }; col += count; s.extend(::std::iter::repeat('~').take(count)); + + chars.next(); } } - - if s.len() > 1 { + if (col - col_ptr) > 1 { // One extra squiggly is replaced by a "^" s.pop(); } + } + // If we elided something put an ellipsis. + if prev_line_index != line.line_index.wrapping_sub(1) && !overflowed { + try!(write!(&mut self.dst, "{0:1$}...\n", "", skip)); + } + + // Print offending code-line + remaining_err_lines -= 1; + try!(write!(&mut self.dst, "{}:{:>width$} {}\n", + fm.name, + line.line_index + 1, + cur_line_str, + width=digits)); + + if s.len() > skip { + // Render the spans we assembled previously (if any). try!(println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), "{}", s)); } + + if !overflowed_buf.is_empty() { + // Print code-lines trailing the rendered spans (when a span overflows) + try!(write!(&mut self.dst, "{}", &overflowed_buf)); + overflowed_buf.clear(); + } else { + prev_line_index = line.line_index; + } + } + + // If we elided something, put an ellipsis. + if lines.next().is_some() { + try!(write!(&mut self.dst, "{0:1$}...\n", "", skip)); } Ok(()) } /// Here are the differences between this and the normal `highlight_lines`: - /// `end_highlight_lines` will always put arrow on the last byte of the - /// span (instead of the first byte). Also, when the span is too long (more + /// `end_highlight_lines` will always put arrow on the last byte of each + /// span (instead of the first byte). Also, when a span is too long (more /// than 6 lines), `end_highlight_lines` will print the first line, then /// dot dot dot, then last line, whereas `highlight_lines` prints the first /// six lines. #[allow(deprecated)] fn end_highlight_lines(&mut self, - sp: Span, - lvl: Level, - lines: codemap::FileLinesResult) + msp: &MultiSpan, + lvl: Level) -> io::Result<()> { - let lines = match lines { + let lines = match self.cm.span_to_lines(msp.to_span_bounds()) { Ok(lines) => lines, Err(_) => { try!(write!(&mut self.dst, "(internal compiler error: unprintable span)\n")); @@ -417,52 +473,107 @@ impl EmitterWriter { }; let fm = &*lines.file; + if let None = fm.src { + return Ok(()); + } let lines = &lines.lines[..]; - if lines.len() > MAX_LINES { - if let Some(line) = fm.get_line(lines[0].line_index) { - try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, - lines[0].line_index + 1, line)); - } - try!(write!(&mut self.dst, "...\n")); - let last_line_index = lines[lines.len() - 1].line_index; - if let Some(last_line) = fm.get_line(last_line_index) { - try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, - last_line_index + 1, last_line)); + + // Calculate the widest number to format evenly + let first_line = lines.first().unwrap(); + let last_line = lines.last().unwrap(); + let digits = line_num_max_digits(last_line); + + let skip = fm.name.chars().count() + digits + 2; + + let mut spans = msp.spans.iter().peekable(); + let mut lines = lines.iter(); + let mut prev_line_index = first_line.line_index.wrapping_sub(1); + + // Display at most MAX_HIGHLIGHT_LINES lines. + let mut remaining_err_lines = MAX_HIGHLIGHT_LINES; + + 'l: loop { + if remaining_err_lines <= 0 { + break; } - } else { - for line_info in lines { - if let Some(line) = fm.get_line(line_info.line_index) { - try!(write!(&mut self.dst, "{}:{} {}\n", fm.name, - line_info.line_index + 1, line)); + let line = match lines.next() { + Some(line) => line, + None => break, + }; + + // Skip is the number of characters we need to skip because they are + // part of the 'filename:line ' part of the previous line. + let mut s: String = ::std::iter::repeat(' ').take(skip).collect(); + + let line_str = fm.get_line(line.line_index).unwrap(); + let mut line_chars = line_str.chars().enumerate(); + let mut line_spans = 0; + + loop { + // Peek here to preserve the span if it doesn't belong to this line + let sp = match spans.peek() { + Some(sp) => **sp, + None => break, + }; + let lo = self.cm.lookup_char_pos(sp.lo); + let hi = self.cm.lookup_char_pos(sp.hi); + let elide_sp = (lo.line - hi.line) > MAX_SP_LINES; + + let line_num = line.line_index + 1; + if !(lo.line <= line_num && hi.line >= line_num) { + // This line is not contained in the span + if line_spans == 0 { + continue 'l; + } else { + // This line is finished, now render the spans we've assembled + break + } + } else if hi.line > line_num { + if elide_sp && lo.line < line_num { + // This line is inbetween the first and last line of the span, + // so we may want to elide it. + continue 'l; + } else { + break + } } - } - } - let last_line_start = format!("{}:{} ", fm.name, lines[lines.len()-1].line_index + 1); - let hi = self.cm.lookup_char_pos(sp.hi); - let skip = last_line_start.chars().count(); - let mut s = String::new(); - for _ in 0..skip { - s.push(' '); - } - if let Some(orig) = fm.get_line(lines[0].line_index) { - let iter = orig.chars().enumerate(); - for (pos, ch) in iter { - // Span seems to use half-opened interval, so subtract 1 - if pos >= hi.col.to_usize() - 1 { break; } - // Whenever a tab occurs on the previous line, we insert one on - // the error-point-squiggly-line as well (instead of a space). - // That way the squiggly line will usually appear in the correct - // position. - match ch { - '\t' => s.push('\t'), - _ => s.push(' '), + line_spans += 1; + spans.next(); + + for (pos, ch) in line_chars.by_ref() { + // Span seems to use half-opened interval, so subtract 1 + if pos >= hi.col.to_usize() - 1 { break; } + // Whenever a tab occurs on the previous line, we insert one on + // the error-point-squiggly-line as well (instead of a space). + // That way the squiggly line will usually appear in the correct + // position. + match ch { + '\t' => s.push('\t'), + _ => s.push(' '), + } } + s.push('^'); + } + + if prev_line_index != line.line_index.wrapping_sub(1) { + // If we elided something, put an ellipsis. + try!(write!(&mut self.dst, "{0:1$}...\n", "", skip)); } + + // Print offending code-lines + try!(write!(&mut self.dst, "{}:{:>width$} {}\n", fm.name, + line.line_index + 1, line_str, width=digits)); + remaining_err_lines -= 1; + + if s.len() > skip { + // Render the spans we assembled previously (if any) + try!(println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), + "{}", s)); + } + prev_line_index = line.line_index; } - s.push('^'); - println_maybe_styled!(&mut self.dst, term::Attr::ForegroundColor(lvl.color()), - "{}", s) + Ok(()) } fn print_macro_backtrace(&mut self, @@ -512,6 +623,16 @@ impl EmitterWriter { } } +fn line_num_max_digits(line: &codemap::LineInfo) -> usize { + let mut max_line_num = line.line_index + 1; + let mut digits = 0; + while max_line_num > 0 { + max_line_num /= 10; + digits += 1; + } + digits +} + fn print_diagnostic(dst: &mut Destination, topic: &str, lvl: Level, @@ -526,12 +647,9 @@ fn print_diagnostic(dst: &mut Destination, "{}: ", lvl.to_string())); try!(print_maybe_styled!(dst, term::Attr::Bold, "{}", msg)); - match code { - Some(code) => { - let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); - try!(print_maybe_styled!(dst, style, " [{}]", code.clone())); - } - None => () + if let Some(code) = code { + let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); + try!(print_maybe_styled!(dst, style, " [{}]", code.clone())); } try!(write!(dst, "\n")); Ok(()) @@ -632,24 +750,36 @@ impl Write for Destination { #[cfg(test)] mod test { - use errors::Level; + use errors::{Level, CodeSuggestion}; use super::EmitterWriter; - use codemap::{mk_sp, CodeMap}; + use codemap::{mk_sp, CodeMap, Span, MultiSpan, BytePos, NO_EXPANSION}; use std::sync::{Arc, Mutex}; use std::io::{self, Write}; use std::str::from_utf8; use std::rc::Rc; + struct Sink(Arc>>); + impl Write for Sink { + fn write(&mut self, data: &[u8]) -> io::Result { + Write::write(&mut *self.0.lock().unwrap(), data) + } + fn flush(&mut self) -> io::Result<()> { Ok(()) } + } + + /// Given a string like " ^~~~~~~~~~~~ ", produces a span + /// coverting that range. The idea is that the string has the same + /// length as the input, and we uncover the byte positions. Note + /// that this can span lines and so on. + fn span_from_selection(input: &str, selection: &str) -> Span { + assert_eq!(input.len(), selection.len()); + let left_index = selection.find('^').unwrap() as u32; + let right_index = selection.rfind('~').map(|x|x as u32).unwrap_or(left_index); + Span { lo: BytePos(left_index), hi: BytePos(right_index + 1), expn_id: NO_EXPANSION } + } + // Diagnostic doesn't align properly in span where line number increases by one digit #[test] fn test_hilight_suggestion_issue_11715() { - struct Sink(Arc>>); - impl Write for Sink { - fn write(&mut self, data: &[u8]) -> io::Result { - Write::write(&mut *self.0.lock().unwrap(), data) - } - fn flush(&mut self) -> io::Result<()> { Ok(()) } - } let data = Arc::new(Mutex::new(Vec::new())); let cm = Rc::new(CodeMap::new()); let mut ew = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); @@ -672,10 +802,8 @@ mod test { let end = file.lines.borrow()[11]; let sp = mk_sp(start, end); let lvl = Level::Error; - println!("span_to_lines"); - let lines = cm.span_to_lines(sp); println!("highlight_lines"); - ew.highlight_lines(sp, lvl, lines).unwrap(); + ew.highlight_lines(&sp.into(), lvl).unwrap(); println!("done"); let vec = data.lock().unwrap().clone(); let vec: &[u8] = &vec; @@ -687,4 +815,287 @@ mod test { dummy.txt:11 e-lä-vän\n\ dummy.txt:12 tolv\n"); } + + #[test] + fn test_single_span_splice() { + // Test that a `MultiSpan` containing a single span splices a substition correctly + let cm = CodeMap::new(); + let inputtext = "aaaaa\nbbbbBB\nCCC\nDDDDDddddd\neee\n"; + let selection = " \n ^~\n~~~\n~~~~~ \n \n"; + cm.new_filemap_and_lines("blork.rs", inputtext); + let sp = span_from_selection(inputtext, selection); + let msp: MultiSpan = sp.into(); + + // check that we are extracting the text we thought we were extracting + assert_eq!(&cm.span_to_snippet(sp).unwrap(), "BB\nCCC\nDDDDD"); + + let substitute = "ZZZZZZ".to_owned(); + let expected = "bbbbZZZZZZddddd"; + let suggest = CodeSuggestion { + msp: msp, + substitutes: vec![substitute], + }; + assert_eq!(suggest.splice_lines(&cm), expected); + } + + #[test] + fn test_multiple_span_splice() { + // Test that a `MultiSpan` containing multiple spans splices substitions on + // several lines correctly + let cm = CodeMap::new(); + let inp = "aaaaabbbbBB\nZZ\nZZ\nCCCDDDDDdddddeee"; + let sp1 = " ^~~~~~\n \n \n "; + let sp2 = " \n \n \n^~~~~~ "; + let sp3 = " \n \n \n ^~~ "; + let sp4 = " \n \n \n ^~~~ "; + + let span_eq = |sp, eq| assert_eq!(&cm.span_to_snippet(sp).unwrap(), eq); + + cm.new_filemap_and_lines("blork.rs", inp); + let sp1 = span_from_selection(inp, sp1); + let sp2 = span_from_selection(inp, sp2); + let sp3 = span_from_selection(inp, sp3); + let sp4 = span_from_selection(inp, sp4); + span_eq(sp1, "bbbbBB"); + span_eq(sp2, "CCCDDD"); + span_eq(sp3, "ddd"); + span_eq(sp4, "ddee"); + + let substitutes: Vec = ["1", "2", "3", "4"].iter().map(|x|x.to_string()).collect(); + let expected = "aaaaa1\nZZ\nZZ\n2DD34e"; + + let test = |msp| { + let suggest = CodeSuggestion { + msp: msp, + substitutes: substitutes.clone(), + }; + let actual = suggest.splice_lines(&cm); + assert_eq!(actual, expected); + }; + test(MultiSpan { spans: vec![sp1, sp2, sp3, sp4] }); + + // Test ordering and merging by `MultiSpan::push` + let mut msp = MultiSpan::new(); + msp.push_merge(sp2); + msp.push_merge(sp1); + assert_eq!(&msp.spans, &[sp1, sp2]); + msp.push_merge(sp4); + assert_eq!(&msp.spans, &[sp1, sp2, sp4]); + msp.push_merge(sp3); + assert_eq!(&msp.spans, &[sp1, sp2, sp3, sp4]); + test(msp); + } + + #[test] + fn test_multispan_highlight() { + let data = Arc::new(Mutex::new(Vec::new())); + let cm = Rc::new(CodeMap::new()); + let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); + + let inp = "_____aaaaaa____bbbbbb__cccccdd_"; + let sp1 = " ^~~~~~ "; + let sp2 = " ^~~~~~ "; + let sp3 = " ^~~~~ "; + let sp4 = " ^~~~ "; + let sp34 = " ^~~~~~~ "; + let sp4_end = " ^~ "; + + let expect_start = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ + \x20 ^~~~~~ ^~~~~~ ^~~~~~~\n"; + let expect_end = "dummy.txt:1 _____aaaaaa____bbbbbb__cccccdd_\n\ + \x20 ^ ^ ^ ^\n"; + + let span = |sp, expected| { + let sp = span_from_selection(inp, sp); + assert_eq!(&cm.span_to_snippet(sp).unwrap(), expected); + sp + }; + cm.new_filemap_and_lines("dummy.txt", inp); + let sp1 = span(sp1, "aaaaaa"); + let sp2 = span(sp2, "bbbbbb"); + let sp3 = span(sp3, "ccccc"); + let sp4 = span(sp4, "ccdd"); + let sp34 = span(sp34, "cccccdd"); + let sp4_end = span(sp4_end, "dd"); + + let spans = vec![sp1, sp2, sp3, sp4]; + + let test = |expected, highlight: &mut FnMut()| { + data.lock().unwrap().clear(); + highlight(); + let vec = data.lock().unwrap().clone(); + let actual = from_utf8(&vec[..]).unwrap(); + assert_eq!(actual, expected); + }; + + let msp = MultiSpan { spans: vec![sp1, sp2, sp34] }; + let msp_end = MultiSpan { spans: vec![sp1, sp2, sp3, sp4_end] }; + test(expect_start, &mut || { + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + test(expect_end, &mut || { + diag.end_highlight_lines(&msp_end, Level::Error).unwrap(); + }); + test(expect_start, &mut || { + for msp in cm.group_spans(spans.clone()) { + diag.highlight_lines(&msp, Level::Error).unwrap(); + } + }); + test(expect_end, &mut || { + for msp in cm.end_group_spans(spans.clone()) { + diag.end_highlight_lines(&msp, Level::Error).unwrap(); + } + }); + } + + #[test] + fn test_huge_multispan_highlight() { + let data = Arc::new(Mutex::new(Vec::new())); + let cm = Rc::new(CodeMap::new()); + let mut diag = EmitterWriter::new(Box::new(Sink(data.clone())), None, cm.clone()); + + let inp = "aaaaa\n\ + aaaaa\n\ + aaaaa\n\ + bbbbb\n\ + ccccc\n\ + xxxxx\n\ + yyyyy\n\ + _____\n\ + ddd__eee_\n\ + elided\n\ + _ff_gg"; + let file = cm.new_filemap_and_lines("dummy.txt", inp); + + let span = |lo, hi, (off_lo, off_hi)| { + let lines = file.lines.borrow(); + let (mut lo, mut hi): (BytePos, BytePos) = (lines[lo], lines[hi]); + lo.0 += off_lo; + hi.0 += off_hi; + mk_sp(lo, hi) + }; + let sp0 = span(4, 6, (0, 5)); + let sp1 = span(0, 6, (0, 5)); + let sp2 = span(8, 8, (0, 3)); + let sp3 = span(8, 8, (5, 8)); + let sp4 = span(10, 10, (1, 3)); + let sp5 = span(10, 10, (4, 6)); + + let expect0 = "dummy.txt: 5 ccccc\n\ + dummy.txt: 6 xxxxx\n\ + dummy.txt: 7 yyyyy\n\ + \x20 ...\n\ + dummy.txt: 9 ddd__eee_\n\ + \x20 ^~~ ^~~\n\ + \x20 ...\n\ + dummy.txt:11 _ff_gg\n\ + \x20 ^~ ^~\n"; + + let expect = "dummy.txt: 1 aaaaa\n\ + dummy.txt: 2 aaaaa\n\ + dummy.txt: 3 aaaaa\n\ + dummy.txt: 4 bbbbb\n\ + dummy.txt: 5 ccccc\n\ + dummy.txt: 6 xxxxx\n\ + \x20 ...\n"; + + let expect_g1 = "dummy.txt:1 aaaaa\n\ + dummy.txt:2 aaaaa\n\ + dummy.txt:3 aaaaa\n\ + dummy.txt:4 bbbbb\n\ + dummy.txt:5 ccccc\n\ + dummy.txt:6 xxxxx\n\ + \x20 ...\n"; + + let expect2 = "dummy.txt: 9 ddd__eee_\n\ + \x20 ^~~ ^~~\n\ + \x20 ...\n\ + dummy.txt:11 _ff_gg\n\ + \x20 ^~ ^~\n"; + + + let expect_end = "dummy.txt: 1 aaaaa\n\ + \x20 ...\n\ + dummy.txt: 7 yyyyy\n\ + \x20 ^\n\ + \x20 ...\n\ + dummy.txt: 9 ddd__eee_\n\ + \x20 ^ ^\n\ + \x20 ...\n\ + dummy.txt:11 _ff_gg\n\ + \x20 ^ ^\n"; + + let expect0_end = "dummy.txt: 5 ccccc\n\ + \x20 ...\n\ + dummy.txt: 7 yyyyy\n\ + \x20 ^\n\ + \x20 ...\n\ + dummy.txt: 9 ddd__eee_\n\ + \x20 ^ ^\n\ + \x20 ...\n\ + dummy.txt:11 _ff_gg\n\ + \x20 ^ ^\n"; + + let expect_end_g1 = "dummy.txt:1 aaaaa\n\ + \x20 ...\n\ + dummy.txt:7 yyyyy\n\ + \x20 ^\n"; + + let expect2_end = "dummy.txt: 9 ddd__eee_\n\ + \x20 ^ ^\n\ + \x20 ...\n\ + dummy.txt:11 _ff_gg\n\ + \x20 ^ ^\n"; + + let expect_groups = [expect2, expect_g1]; + let expect_end_groups = [expect2_end, expect_end_g1]; + let spans = vec![sp3, sp1, sp4, sp2, sp5]; + + macro_rules! test { + ($expected: expr, $highlight: expr) => ({ + data.lock().unwrap().clear(); + $highlight(); + let vec = data.lock().unwrap().clone(); + let actual = from_utf8(&vec[..]).unwrap(); + println!("actual:"); + println!("{}", actual); + println!("expected:"); + println!("{}", $expected); + assert_eq!(&actual[..], &$expected[..]); + }); + } + + let msp0 = MultiSpan { spans: vec![sp0, sp2, sp3, sp4, sp5] }; + let msp = MultiSpan { spans: vec![sp1, sp2, sp3, sp4, sp5] }; + let msp2 = MultiSpan { spans: vec![sp2, sp3, sp4, sp5] }; + + test!(expect0, || { + diag.highlight_lines(&msp0, Level::Error).unwrap(); + }); + test!(expect0_end, || { + diag.end_highlight_lines(&msp0, Level::Error).unwrap(); + }); + test!(expect, || { + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + test!(expect_end, || { + diag.end_highlight_lines(&msp, Level::Error).unwrap(); + }); + test!(expect2, || { + diag.highlight_lines(&msp2, Level::Error).unwrap(); + }); + test!(expect2_end, || { + diag.end_highlight_lines(&msp2, Level::Error).unwrap(); + }); + for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_groups.iter()) { + test!(expect, || { + diag.highlight_lines(&msp, Level::Error).unwrap(); + }); + } + for (msp, expect) in cm.group_spans(spans.clone()).iter().zip(expect_end_groups.iter()) { + test!(expect, || { + diag.end_highlight_lines(&msp, Level::Error).unwrap(); + }); + } + } } diff --git a/src/libsyntax/errors/json.rs b/src/libsyntax/errors/json.rs index 713190ef419d6..5bb5f4757e013 100644 --- a/src/libsyntax/errors/json.rs +++ b/src/libsyntax/errors/json.rs @@ -20,9 +20,9 @@ // FIXME spec the JSON output properly. -use codemap::{Span, CodeMap}; +use codemap::{MultiSpan, CodeMap}; use diagnostics::registry::Registry; -use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan}; +use errors::{Level, DiagnosticBuilder, SubDiagnostic, RenderSpan, CodeSuggestion}; use errors::emitter::Emitter; use std::rc::Rc; @@ -52,15 +52,15 @@ impl JsonEmitter { } impl Emitter for JsonEmitter { - fn emit(&mut self, span: Option, msg: &str, code: Option<&str>, level: Level) { + fn emit(&mut self, span: Option<&MultiSpan>, msg: &str, code: Option<&str>, level: Level) { let data = Diagnostic::new(span, msg, code, level, self); if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { panic!("failed to print diagnostics: {:?}", e); } } - fn custom_emit(&mut self, sp: RenderSpan, msg: &str, level: Level) { - let data = Diagnostic::from_render_span(&sp, msg, level, self); + fn custom_emit(&mut self, sp: &RenderSpan, msg: &str, level: Level) { + let data = Diagnostic::from_render_span(sp, msg, level, self); if let Err(e) = writeln!(&mut self.dst, "{}", as_json(&data)) { panic!("failed to print diagnostics: {:?}", e); } @@ -83,7 +83,7 @@ struct Diagnostic<'a> { code: Option, /// "error: internal compiler error", "error", "warning", "note", "help". level: &'static str, - span: Option, + spans: Vec, /// Assocaited diagnostic messages. children: Vec>, } @@ -110,7 +110,7 @@ struct DiagnosticCode { } impl<'a> Diagnostic<'a> { - fn new(span: Option, + fn new(msp: Option<&MultiSpan>, msg: &'a str, code: Option<&str>, level: Level, @@ -120,7 +120,7 @@ impl<'a> Diagnostic<'a> { message: msg, code: DiagnosticCode::map_opt_string(code.map(|c| c.to_owned()), je), level: level.to_str(), - span: span.map(|sp| DiagnosticSpan::from_span(sp, je)), + spans: msp.map_or(vec![], |msp| DiagnosticSpan::from_multispan(msp, je)), children: vec![], } } @@ -134,7 +134,7 @@ impl<'a> Diagnostic<'a> { message: msg, code: None, level: level.to_str(), - span: Some(DiagnosticSpan::from_render_span(span, je)), + spans: DiagnosticSpan::from_render_span(span, je), children: vec![], } } @@ -146,7 +146,7 @@ impl<'a> Diagnostic<'a> { message: &db.message, code: DiagnosticCode::map_opt_string(db.code.clone(), je), level: db.level.to_str(), - span: db.span.map(|sp| DiagnosticSpan::from_span(sp, je)), + spans: db.span.as_ref().map_or(vec![], |sp| DiagnosticSpan::from_multispan(sp, je)), children: db.children.iter().map(|c| { Diagnostic::from_sub_diagnostic(c, je) }).collect(), @@ -158,59 +158,67 @@ impl<'a> Diagnostic<'a> { message: &db.message, code: None, level: db.level.to_str(), - span: db.render_span.as_ref() - .map(|sp| DiagnosticSpan::from_render_span(sp, je)) - .or_else(|| db.span.map(|sp| DiagnosticSpan::from_span(sp, je))), + spans: db.render_span.as_ref() + .map(|sp| DiagnosticSpan::from_render_span(sp, je)) + .or_else(|| db.span.as_ref().map(|s| DiagnosticSpan::from_multispan(s, je))) + .unwrap_or(vec![]), children: vec![], } } } impl DiagnosticSpan { - fn from_span(span: Span, je: &JsonEmitter) -> DiagnosticSpan { - let start = je.cm.lookup_char_pos(span.lo); - let end = je.cm.lookup_char_pos(span.hi); - DiagnosticSpan { - file_name: start.file.name.clone(), - byte_start: span.lo.0, - byte_end: span.hi.0, - line_start: start.line, - line_end: end.line, - column_start: start.col.0 + 1, - column_end: end.col.0 + 1, - } + fn from_multispan(msp: &MultiSpan, je: &JsonEmitter) -> Vec { + msp.spans.iter().map(|span| { + let start = je.cm.lookup_char_pos(span.lo); + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: start.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: start.line, + line_end: end.line, + column_start: start.col.0 + 1, + column_end: end.col.0 + 1, + } + }).collect() } - fn from_render_span(span: &RenderSpan, je: &JsonEmitter) -> DiagnosticSpan { - match *span { + fn from_render_span(rsp: &RenderSpan, je: &JsonEmitter) -> Vec { + match *rsp { // FIXME(#30701) handle Suggestion properly - RenderSpan::FullSpan(sp) | RenderSpan::Suggestion(sp, _) => { - DiagnosticSpan::from_span(sp, je) + RenderSpan::FullSpan(ref msp) | + RenderSpan::Suggestion(CodeSuggestion { ref msp, .. }) => { + DiagnosticSpan::from_multispan(msp, je) } - RenderSpan::EndSpan(span) => { - let end = je.cm.lookup_char_pos(span.hi); - DiagnosticSpan { - file_name: end.file.name.clone(), - byte_start: span.lo.0, - byte_end: span.hi.0, - line_start: 0, - line_end: end.line, - column_start: 0, - column_end: end.col.0 + 1, - } + RenderSpan::EndSpan(ref msp) => { + msp.spans.iter().map(|span| { + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: end.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: 0, + line_end: end.line, + column_start: 0, + column_end: end.col.0 + 1, + } + }).collect() } - RenderSpan::FileLine(span) => { - let start = je.cm.lookup_char_pos(span.lo); - let end = je.cm.lookup_char_pos(span.hi); - DiagnosticSpan { - file_name: start.file.name.clone(), - byte_start: span.lo.0, - byte_end: span.hi.0, - line_start: start.line, - line_end: end.line, - column_start: 0, - column_end: 0, - } + RenderSpan::FileLine(ref msp) => { + msp.spans.iter().map(|span| { + let start = je.cm.lookup_char_pos(span.lo); + let end = je.cm.lookup_char_pos(span.hi); + DiagnosticSpan { + file_name: start.file.name.clone(), + byte_start: span.lo.0, + byte_end: span.hi.0, + line_start: start.line, + line_end: end.line, + column_start: 0, + column_end: 0, + } + }).collect() } } } diff --git a/src/libsyntax/errors/mod.rs b/src/libsyntax/errors/mod.rs index a7a4ddc3b2a63..9e1cb60f54f67 100644 --- a/src/libsyntax/errors/mod.rs +++ b/src/libsyntax/errors/mod.rs @@ -13,7 +13,7 @@ pub use errors::emitter::ColorConfig; use self::Level::*; use self::RenderSpan::*; -use codemap::{self, Span}; +use codemap::{self, CodeMap, MultiSpan}; use diagnostics; use errors::emitter::{Emitter, EmitterWriter}; @@ -31,35 +31,112 @@ pub enum RenderSpan { /// A FullSpan renders with both with an initial line for the /// message, prefixed by file:linenum, followed by a summary of /// the source code covered by the span. - FullSpan(Span), + FullSpan(MultiSpan), /// Similar to a FullSpan, but the cited position is the end of /// the span, instead of the start. Used, at least, for telling /// compiletest/runtest to look at the last line of the span /// (since `end_highlight_lines` displays an arrow to the end /// of the span). - EndSpan(Span), + EndSpan(MultiSpan), /// A suggestion renders with both with an initial line for the /// message, prefixed by file:linenum, followed by a summary - /// of hypothetical source code, where the `String` is spliced - /// into the lines in place of the code covered by the span. - Suggestion(Span, String), + /// of hypothetical source code, where each `String` is spliced + /// into the lines in place of the code covered by each span. + Suggestion(CodeSuggestion), /// A FileLine renders with just a line for the message prefixed /// by file:linenum. - FileLine(Span), + FileLine(MultiSpan), +} + +#[derive(Clone)] +pub struct CodeSuggestion { + msp: MultiSpan, + substitutes: Vec, } impl RenderSpan { - fn span(&self) -> Span { + fn span(&self) -> &MultiSpan { match *self { - FullSpan(s) | - Suggestion(s, _) | - EndSpan(s) | - FileLine(s) => - s + FullSpan(ref msp) | + Suggestion(CodeSuggestion { ref msp, .. }) | + EndSpan(ref msp) | + FileLine(ref msp) => + msp + } + } +} + +impl CodeSuggestion { + /// Returns the assembled code suggestion. + pub fn splice_lines(&self, cm: &CodeMap) -> String { + use codemap::{CharPos, Loc, Pos}; + + fn push_trailing(buf: &mut String, line_opt: Option<&str>, + lo: &Loc, hi_opt: Option<&Loc>) { + let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi|hi.col.to_usize())); + if let Some(line) = line_opt { + if line.len() > lo { + buf.push_str(match hi_opt { + Some(hi) => &line[lo..hi], + None => &line[lo..], + }); + } + if let None = hi_opt { + buf.push('\n'); + } + } + } + let bounds = self.msp.to_span_bounds(); + let lines = cm.span_to_lines(bounds).unwrap(); + assert!(!lines.lines.is_empty()); + + // This isn't strictly necessary, but would in all likelyhood be an error + assert_eq!(self.msp.spans.len(), self.substitutes.len()); + + // To build up the result, we do this for each span: + // - push the line segment trailing the previous span + // (at the beginning a "phantom" span pointing at the start of the line) + // - push lines between the previous and current span (if any) + // - if the previous and current span are not on the same line + // push the line segment leading up to the current span + // - splice in the span substitution + // + // Finally push the trailing line segment of the last span + let fm = &lines.file; + let mut prev_hi = cm.lookup_char_pos(bounds.lo); + prev_hi.col = CharPos::from_usize(0); + + let mut prev_line = fm.get_line(lines.lines[0].line_index); + let mut buf = String::new(); + + for (sp, substitute) in self.msp.spans.iter().zip(self.substitutes.iter()) { + let cur_lo = cm.lookup_char_pos(sp.lo); + if prev_hi.line == cur_lo.line { + push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo)); + } else { + push_trailing(&mut buf, prev_line, &prev_hi, None); + // push lines between the previous and current span (if any) + for idx in prev_hi.line..(cur_lo.line - 1) { + if let Some(line) = fm.get_line(idx) { + buf.push_str(line); + buf.push('\n'); + } + } + if let Some(cur_line) = fm.get_line(cur_lo.line - 1) { + buf.push_str(&cur_line[.. cur_lo.col.to_usize()]); + } + } + buf.push_str(substitute); + prev_hi = cm.lookup_char_pos(sp.hi); + prev_line = fm.get_line(prev_hi.line - 1); } + push_trailing(&mut buf, prev_line, &prev_hi, None); + // remove trailing newline + buf.pop(); + buf } } @@ -106,7 +183,7 @@ pub struct DiagnosticBuilder<'a> { level: Level, message: String, code: Option, - span: Option, + span: Option, children: Vec, } @@ -114,7 +191,7 @@ pub struct DiagnosticBuilder<'a> { struct SubDiagnostic { level: Level, message: String, - span: Option, + span: Option, render_span: Option, } @@ -150,81 +227,84 @@ impl<'a> DiagnosticBuilder<'a> { self.level == Level::Fatal } - pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { + pub fn note(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { self.sub(Level::Note, msg, None, None); self } - pub fn span_note(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, Some(sp), None); + pub fn span_note>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, Some(sp.into()), None); self } - pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { + pub fn warn(&mut self, msg: &str) -> &mut DiagnosticBuilder<'a> { self.sub(Level::Warning, msg, None, None); self } - pub fn span_warn(&mut self, - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, Some(sp), None); + pub fn span_warn>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Warning, msg, Some(sp.into()), None); self } - pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { + pub fn help(&mut self , msg: &str) -> &mut DiagnosticBuilder<'a> { self.sub(Level::Help, msg, None, None); self } - pub fn span_help(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, Some(sp), None); + pub fn span_help>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, Some(sp.into()), None); self } /// Prints out a message with a suggested edit of the code. /// /// See `diagnostic::RenderSpan::Suggestion` for more information. - pub fn span_suggestion(&mut self , - sp: Span, - msg: &str, - suggestion: String) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, Some(sp), Some(Suggestion(sp, suggestion))); + pub fn span_suggestion>(&mut self, + sp: S, + msg: &str, + suggestion: String) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, None, Some(Suggestion(CodeSuggestion { + msp: sp.into(), + substitutes: vec![suggestion], + }))); self } - pub fn span_end_note(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, Some(sp), Some(EndSpan(sp))); + pub fn span_end_note>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, None, Some(EndSpan(sp.into()))); self } - pub fn fileline_warn(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Warning, msg, Some(sp), Some(FileLine(sp))); + pub fn fileline_warn>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Warning, msg, None, Some(FileLine(sp.into()))); self } - pub fn fileline_note(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Note, msg, Some(sp), Some(FileLine(sp))); + pub fn fileline_note>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Note, msg, None, Some(FileLine(sp.into()))); self } - pub fn fileline_help(&mut self , - sp: Span, - msg: &str) - -> &mut DiagnosticBuilder<'a> { - self.sub(Level::Help, msg, Some(sp), Some(FileLine(sp))); + pub fn fileline_help>(&mut self, + sp: S, + msg: &str) + -> &mut DiagnosticBuilder<'a> { + self.sub(Level::Help, msg, None, Some(FileLine(sp.into()))); self } - pub fn span(&mut self, sp: Span) -> &mut Self { - self.span = Some(sp); + pub fn span>(&mut self, sp: S) -> &mut Self { + self.span = Some(sp.into()); self } @@ -237,7 +317,7 @@ impl<'a> DiagnosticBuilder<'a> { /// struct_* methods on Handler. fn new(emitter: &'a RefCell>, level: Level, - message: &str) -> DiagnosticBuilder<'a> { + message: &str) -> DiagnosticBuilder<'a> { DiagnosticBuilder { emitter: emitter, level: level, @@ -253,7 +333,7 @@ impl<'a> DiagnosticBuilder<'a> { fn sub(&mut self, level: Level, message: &str, - span: Option, + span: Option, render_span: Option) { let sub = SubDiagnostic { level: level, @@ -290,7 +370,7 @@ pub struct Handler { emit: RefCell>, pub can_emit_warnings: bool, treat_err_as_bug: bool, - delayed_span_bug: RefCell>, + delayed_span_bug: RefCell>, } impl Handler { @@ -320,10 +400,10 @@ impl Handler { DiagnosticBuilder::new(&self.emit, Level::Cancelled, "") } - pub fn struct_span_warn<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_warn<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); result.span(sp); if !self.can_emit_warnings { @@ -331,11 +411,11 @@ impl Handler { } result } - pub fn struct_span_warn_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_warn_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { let mut result = DiagnosticBuilder::new(&self.emit, Level::Warning, msg); result.span(sp); result.code(code.to_owned()); @@ -351,20 +431,20 @@ impl Handler { } result } - pub fn struct_span_err<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_err<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); result.span(sp); result } - pub fn struct_span_err_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_err_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Error, msg); result.span(sp); @@ -375,20 +455,20 @@ impl Handler { self.bump_err_count(); DiagnosticBuilder::new(&self.emit, Level::Error, msg) } - pub fn struct_span_fatal<'a>(&'a self, - sp: Span, - msg: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_fatal<'a, S: Into>(&'a self, + sp: S, + msg: &str) + -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); result.span(sp); result } - pub fn struct_span_fatal_with_code<'a>(&'a self, - sp: Span, - msg: &str, - code: &str) - -> DiagnosticBuilder<'a> { + pub fn struct_span_fatal_with_code<'a, S: Into>(&'a self, + sp: S, + msg: &str, + code: &str) + -> DiagnosticBuilder<'a> { self.bump_err_count(); let mut result = DiagnosticBuilder::new(&self.emit, Level::Fatal, msg); result.span(sp); @@ -408,58 +488,59 @@ impl Handler { err.cancel(); } - pub fn span_fatal(&self, sp: Span, msg: &str) -> FatalError { + pub fn span_fatal>(&self, sp: S, msg: &str) -> FatalError { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit(Some(sp), msg, Fatal); + self.emit(Some(&sp.into()), msg, Fatal); self.bump_err_count(); return FatalError; } - pub fn span_fatal_with_code(&self, sp: Span, msg: &str, code: &str) -> FatalError { + pub fn span_fatal_with_code>(&self, sp: S, msg: &str, code: &str) + -> FatalError { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit_with_code(Some(sp), msg, code, Fatal); + self.emit_with_code(Some(&sp.into()), msg, code, Fatal); self.bump_err_count(); return FatalError; } - pub fn span_err(&self, sp: Span, msg: &str) { + pub fn span_err>(&self, sp: S, msg: &str) { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit(Some(sp), msg, Error); + self.emit(Some(&sp.into()), msg, Error); self.bump_err_count(); } - pub fn span_err_with_code(&self, sp: Span, msg: &str, code: &str) { + pub fn span_err_with_code>(&self, sp: S, msg: &str, code: &str) { if self.treat_err_as_bug { self.span_bug(sp, msg); } - self.emit_with_code(Some(sp), msg, code, Error); + self.emit_with_code(Some(&sp.into()), msg, code, Error); self.bump_err_count(); } - pub fn span_warn(&self, sp: Span, msg: &str) { - self.emit(Some(sp), msg, Warning); + pub fn span_warn>(&self, sp: S, msg: &str) { + self.emit(Some(&sp.into()), msg, Warning); } - pub fn span_warn_with_code(&self, sp: Span, msg: &str, code: &str) { - self.emit_with_code(Some(sp), msg, code, Warning); + pub fn span_warn_with_code>(&self, sp: S, msg: &str, code: &str) { + self.emit_with_code(Some(&sp.into()), msg, code, Warning); } - pub fn span_bug(&self, sp: Span, msg: &str) -> ! { - self.emit(Some(sp), msg, Bug); + pub fn span_bug>(&self, sp: S, msg: &str) -> ! { + self.emit(Some(&sp.into()), msg, Bug); panic!(ExplicitBug); } - pub fn delay_span_bug(&self, sp: Span, msg: &str) { + pub fn delay_span_bug>(&self, sp: S, msg: &str) { let mut delayed = self.delayed_span_bug.borrow_mut(); - *delayed = Some((sp, msg.to_string())); + *delayed = Some((sp.into(), msg.to_string())); } - pub fn span_bug_no_panic(&self, sp: Span, msg: &str) { - self.emit(Some(sp), msg, Bug); + pub fn span_bug_no_panic>(&self, sp: S, msg: &str) { + self.emit(Some(&sp.into()), msg, Bug); self.bump_err_count(); } - pub fn span_note_without_error(&self, sp: Span, msg: &str) { - self.emit.borrow_mut().emit(Some(sp), msg, None, Note); + pub fn span_note_without_error>(&self, sp: S, msg: &str) { + self.emit.borrow_mut().emit(Some(&sp.into()), msg, None, Note); } - pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { + pub fn span_unimpl>(&self, sp: S, msg: &str) -> ! { self.span_bug(sp, &format!("unimplemented {}", msg)); } pub fn fatal(&self, msg: &str) -> FatalError { @@ -502,15 +583,14 @@ impl Handler { pub fn has_errors(&self) -> bool { self.err_count.get() > 0 } - pub fn abort_if_errors(&self) { let s; match self.err_count.get() { 0 => { let delayed_bug = self.delayed_span_bug.borrow(); match *delayed_bug { - Some((span, ref errmsg)) => { - self.span_bug(span, errmsg); + Some((ref span, ref errmsg)) => { + self.span_bug(span.clone(), errmsg); }, _ => {} } @@ -526,27 +606,24 @@ impl Handler { panic!(self.fatal(&s)); } - pub fn emit(&self, - sp: Option, + msp: Option<&MultiSpan>, msg: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(sp, msg, None, lvl); + self.emit.borrow_mut().emit(msp, msg, None, lvl); } - pub fn emit_with_code(&self, - sp: Option, + msp: Option<&MultiSpan>, msg: &str, code: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().emit(sp, msg, Some(code), lvl); + self.emit.borrow_mut().emit(msp, msg, Some(code), lvl); } - - pub fn custom_emit(&self, sp: RenderSpan, msg: &str, lvl: Level) { + pub fn custom_emit(&self, rsp: RenderSpan, msg: &str, lvl: Level) { if lvl == Warning && !self.can_emit_warnings { return } - self.emit.borrow_mut().custom_emit(sp, msg, lvl); + self.emit.borrow_mut().custom_emit(&rsp, msg, lvl); } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index bfd76db0359bd..82fa0f8a8b2a4 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -972,6 +972,7 @@ fn can_be_followed_by_any(frag: &str) -> bool { /// we expanded `expr` to include a new binary operator, we might /// break macros that were relying on that binary operator as a /// separator. +// when changing this do not forget to update doc/book/macros.md! fn is_in_follow(_: &ExtCtxt, tok: &Token, frag: &str) -> Result { if let &CloseDelim(_) = tok { // closing a token tree can never be matched by any fragment; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2249faac6d701..b8e5642474c78 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -233,7 +233,6 @@ macro_rules! maybe_whole { ) } - fn maybe_append(mut lhs: Vec, rhs: Option>) -> Vec { if let Some(ref attrs) = rhs { @@ -255,6 +254,7 @@ pub struct Parser<'a> { pub cfg: CrateConfig, /// the previous token or None (only stashed sometimes). pub last_token: Option>, + last_token_interpolated: bool, pub buffer: [TokenAndSpan; 4], pub buffer_start: isize, pub buffer_end: isize, @@ -362,6 +362,7 @@ impl<'a> Parser<'a> { span: span, last_span: span, last_token: None, + last_token_interpolated: false, buffer: [ placeholder.clone(), placeholder.clone(), @@ -542,6 +543,19 @@ impl<'a> Parser<'a> { self.commit_stmt(&[edible], &[]) } + /// returns the span of expr, if it was not interpolated or the span of the interpolated token + fn interpolated_or_expr_span(&self, + expr: PResult<'a, P>) + -> PResult<'a, (Span, P)> { + expr.map(|e| { + if self.last_token_interpolated { + (self.last_span, e) + } else { + (e.span, e) + } + }) + } + pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> { self.check_strict_keywords(); self.check_reserved_keywords(); @@ -933,6 +947,7 @@ impl<'a> Parser<'a> { } else { None }; + self.last_token_interpolated = self.token.is_interpolated(); let next = if self.buffer_start == self.buffer_end { self.reader.real_token() } else { @@ -2328,18 +2343,20 @@ impl<'a> Parser<'a> { -> PResult<'a, P> { let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); - let b = try!(self.parse_bottom_expr()); - self.parse_dot_or_call_expr_with(b, attrs) + let b = self.parse_bottom_expr(); + let (span, b) = try!(self.interpolated_or_expr_span(b)); + self.parse_dot_or_call_expr_with(b, span.lo, attrs) } pub fn parse_dot_or_call_expr_with(&mut self, e0: P, + lo: BytePos, attrs: ThinAttributes) -> PResult<'a, P> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - self.parse_dot_or_call_expr_with_(e0) + self.parse_dot_or_call_expr_with_(e0, lo) .map(|expr| expr.map(|mut expr| { expr.attrs.update(|a| a.prepend(attrs)); @@ -2366,7 +2383,8 @@ impl<'a> Parser<'a> { fn parse_dot_suffix(&mut self, ident: Ident, ident_span: Span, - self_value: P) + self_value: P, + lo: BytePos) -> PResult<'a, P> { let (_, tys, bindings) = if self.eat(&token::ModSep) { try!(self.expect_lt()); @@ -2380,8 +2398,6 @@ impl<'a> Parser<'a> { self.span_err(last_span, "type bindings are only permitted on trait paths"); } - let lo = self_value.span.lo; - Ok(match self.token { // expr.f() method call. token::OpenDelim(token::Paren) => { @@ -2414,9 +2430,8 @@ impl<'a> Parser<'a> { }) } - fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult<'a, P> { + fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: BytePos) -> PResult<'a, P> { let mut e = e0; - let lo = e.span.lo; let mut hi; loop { // expr.f @@ -2427,7 +2442,7 @@ impl<'a> Parser<'a> { hi = self.span.hi; self.bump(); - e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e)); + e = try!(self.parse_dot_suffix(i, mk_sp(dot_pos, hi), e, lo)); } token::Literal(token::Integer(n), suf) => { let sp = self.span; @@ -2480,7 +2495,7 @@ impl<'a> Parser<'a> { let dot_pos = self.last_span.hi; e = try!(self.parse_dot_suffix(special_idents::invalid, mk_sp(dot_pos, dot_pos), - e)); + e, lo)); } } continue; @@ -2715,27 +2730,31 @@ impl<'a> Parser<'a> { let ex = match self.token { token::Not => { self.bump(); - let e = try!(self.parse_prefix_expr(None)); - hi = e.span.hi; + let e = self.parse_prefix_expr(None); + let (span, e) = try!(self.interpolated_or_expr_span(e)); + hi = span.hi; self.mk_unary(UnNot, e) } token::BinOp(token::Minus) => { self.bump(); - let e = try!(self.parse_prefix_expr(None)); - hi = e.span.hi; + let e = self.parse_prefix_expr(None); + let (span, e) = try!(self.interpolated_or_expr_span(e)); + hi = span.hi; self.mk_unary(UnNeg, e) } token::BinOp(token::Star) => { self.bump(); - let e = try!(self.parse_prefix_expr(None)); - hi = e.span.hi; + let e = self.parse_prefix_expr(None); + let (span, e) = try!(self.interpolated_or_expr_span(e)); + hi = span.hi; self.mk_unary(UnDeref, e) } token::BinOp(token::And) | token::AndAnd => { try!(self.expect_and()); let m = try!(self.parse_mutability()); - let e = try!(self.parse_prefix_expr(None)); - hi = e.span.hi; + let e = self.parse_prefix_expr(None); + let (span, e) = try!(self.interpolated_or_expr_span(e)); + hi = span.hi; ExprAddrOf(m, e) } token::Ident(..) if self.token.is_keyword(keywords::In) => { @@ -2753,9 +2772,10 @@ impl<'a> Parser<'a> { } token::Ident(..) if self.token.is_keyword(keywords::Box) => { self.bump(); - let subexpression = try!(self.parse_prefix_expr(None)); - hi = subexpression.span.hi; - ExprBox(subexpression) + let e = self.parse_prefix_expr(None); + let (span, e) = try!(self.interpolated_or_expr_span(e)); + hi = span.hi; + ExprBox(e) } _ => return self.parse_dot_or_call_expr(Some(attrs)) }; @@ -2790,12 +2810,21 @@ impl<'a> Parser<'a> { try!(self.parse_prefix_expr(attrs)) } }; + + if self.expr_is_complete(&*lhs) { // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 return Ok(lhs); } self.expected_tokens.push(TokenType::Operator); while let Some(op) = AssocOp::from_token(&self.token) { + + let lhs_span = if self.last_token_interpolated { + self.last_span + } else { + lhs.span + }; + let cur_op_span = self.span; let restrictions = if op.is_assign_like() { self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL @@ -2812,12 +2841,12 @@ impl<'a> Parser<'a> { // Special cases: if op == AssocOp::As { let rhs = try!(self.parse_ty()); - lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, + lhs = self.mk_expr(lhs_span.lo, rhs.span.hi, ExprCast(lhs, rhs), None); continue } else if op == AssocOp::Colon { let rhs = try!(self.parse_ty()); - lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, + lhs = self.mk_expr(lhs_span.lo, rhs.span.hi, ExprType(lhs, rhs), None); continue } else if op == AssocOp::DotDot { @@ -2839,7 +2868,7 @@ impl<'a> Parser<'a> { } else { None }; - let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs { + let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs { x.span } else { cur_op_span @@ -2879,14 +2908,14 @@ impl<'a> Parser<'a> { AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual | AssocOp::Greater | AssocOp::GreaterEqual => { let ast_op = op.to_ast_binop().unwrap(); - let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let (lhs_span, rhs_span) = (lhs_span, rhs.span); let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs); self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None) } AssocOp::Assign => - self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None), + self.mk_expr(lhs_span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None), AssocOp::Inplace => - self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None), + self.mk_expr(lhs_span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BiAdd, @@ -2900,7 +2929,7 @@ impl<'a> Parser<'a> { token::Shl => BiShl, token::Shr => BiShr }; - let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let (lhs_span, rhs_span) = (lhs_span, rhs.span); let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None) } @@ -3834,7 +3863,8 @@ impl<'a> Parser<'a> { let e = self.mk_mac_expr(span.lo, span.hi, mac.and_then(|m| m.node), None); - let e = try!(self.parse_dot_or_call_expr_with(e, attrs)); + let lo = e.span.lo; + let e = try!(self.parse_dot_or_call_expr_with(e, lo, attrs)); let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))); try!(self.handle_expression_like_statement( e, diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 242626154fc8c..220d0aff2e3af 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -223,6 +223,14 @@ impl Token { } } + /// Returns `true` if the token is interpolated. + pub fn is_interpolated(&self) -> bool { + match *self { + Interpolated(..) => true, + _ => false, + } + } + /// Returns `true` if the token is an interpolated path. pub fn is_path(&self) -> bool { match *self { diff --git a/src/test/auxiliary/cgu_export_trait_method.rs b/src/test/auxiliary/cgu_export_trait_method.rs new file mode 100644 index 0000000000000..49b8e43836e52 --- /dev/null +++ b/src/test/auxiliary/cgu_export_trait_method.rs @@ -0,0 +1,34 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub trait Trait : Sized { + fn without_self() -> u32; + fn without_self_default() -> u32 { 0 } + + fn with_default_impl(self) -> Self { self } + fn with_default_impl_generic(self, x: T) -> (Self, T) { (self, x) } + + fn without_default_impl(x: u32) -> (Self, u32); + fn without_default_impl_generic(x: T) -> (Self, T); +} + +impl Trait for char { + fn without_self() -> u32 { 2 } + fn without_default_impl(x: u32) -> (Self, u32) { ('c', x) } + fn without_default_impl_generic(x: T) -> (Self, T) { ('c', x) } +} + +impl Trait for u32 { + fn without_self() -> u32 { 1 } + fn without_default_impl(x: u32) -> (Self, u32) { (0, x) } + fn without_default_impl_generic(x: T) -> (Self, T) { (0, x) } +} diff --git a/src/test/auxiliary/cgu_extern_closures.rs b/src/test/auxiliary/cgu_extern_closures.rs new file mode 100644 index 0000000000000..944d85db50806 --- /dev/null +++ b/src/test/auxiliary/cgu_extern_closures.rs @@ -0,0 +1,33 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +#[inline] +pub fn inlined_fn(x: i32, y: i32) -> i32 { + + let closure = |a, b| { a + b }; + + closure(x, y) +} + +pub fn inlined_fn_generic(x: i32, y: i32, z: T) -> (i32, T) { + + let closure = |a, b| { a + b }; + + (closure(x, y), z) +} + +pub fn non_inlined_fn(x: i32, y: i32) -> i32 { + + let closure = |a, b| { a + b }; + + closure(x, y) +} diff --git a/src/test/auxiliary/cgu_generic_function.rs b/src/test/auxiliary/cgu_generic_function.rs new file mode 100644 index 0000000000000..83bb65bc2b7f0 --- /dev/null +++ b/src/test/auxiliary/cgu_generic_function.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +struct Struct(u32); + +pub fn foo(x: T) -> (T, u32, i8) { + let (x, Struct(y)) = bar(x); + (x, y, 2) +} + + +fn bar(x: T) -> (T, Struct) { + let _ = not_exported_and_not_generic(0); + (x, Struct(1)) +} + +// These should not contribute to the codegen items of other crates. +#[inline(never)] +pub fn exported_but_not_generic(x: i32) -> i64 { + x as i64 +} + +#[inline(never)] +fn not_exported_and_not_generic(x: u32) -> u64 { + x as u64 +} + diff --git a/src/test/codegen-units/cross-crate-closures.rs b/src/test/codegen-units/cross-crate-closures.rs new file mode 100644 index 0000000000000..32b07d42fec44 --- /dev/null +++ b/src/test/codegen-units/cross-crate-closures.rs @@ -0,0 +1,35 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_extern_closures.rs +extern crate cgu_extern_closures; + +//~ TRANS_ITEM fn cross_crate_closures::main[0] +fn main() { + + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0] + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn[0]::{{closure}}[0] + let _ = cgu_extern_closures::inlined_fn(1, 2); + + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0] + //~ TRANS_ITEM fn cgu_extern_closures[0]::inlined_fn_generic[0]::{{closure}}[0] + let _ = cgu_extern_closures::inlined_fn_generic(3, 4, 5i32); + + // Nothing should be generated for this call, we just link to the instance instance + // in the extern crate. + let _ = cgu_extern_closures::non_inlined_fn(6, 7); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/cross-crate-generic-functions.rs b/src/test/codegen-units/cross-crate-generic-functions.rs new file mode 100644 index 0000000000000..82d940a154852 --- /dev/null +++ b/src/test/codegen-units/cross-crate-generic-functions.rs @@ -0,0 +1,34 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_generic_function.rs +extern crate cgu_generic_function; + +//~ TRANS_ITEM fn cross_crate_generic_functions::main[0] +fn main() +{ + //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0] + //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0] + let _ = cgu_generic_function::foo(1u32); + + //~ TRANS_ITEM fn cgu_generic_function[0]::bar[0] + //~ TRANS_ITEM fn cgu_generic_function[0]::foo[0] + let _ = cgu_generic_function::foo(2u64); + + // This should not introduce a codegen item + let _ = cgu_generic_function::exported_but_not_generic(3); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/cross-crate-trait-method.rs b/src/test/codegen-units/cross-crate-trait-method.rs new file mode 100644 index 0000000000000..aa1f6b06c8135 --- /dev/null +++ b/src/test/codegen-units/cross-crate-trait-method.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +// aux-build:cgu_export_trait_method.rs +extern crate cgu_export_trait_method; + +use cgu_export_trait_method::Trait; + +//~ TRANS_ITEM fn cross_crate_trait_method::main[0] +fn main() +{ + // The object code of these methods is contained in the external crate, so + // calling them should *not* introduce codegen items in the current crate. + let _: (u32, u32) = Trait::without_default_impl(0); + let _: (char, u32) = Trait::without_default_impl(0); + + // Currently, no object code is generated for trait methods with default + // implemenations, unless they are actually called from somewhere. Therefore + // we cannot import the implementations and have to create our own inline. + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0] + let _ = Trait::with_default_impl(0u32); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl[0] + let _ = Trait::with_default_impl('c'); + + + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic(0u32, "abc"); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic(0u32, false); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic('x', 1i16); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::Trait[0]::with_default_impl_generic[0] + let _ = Trait::with_default_impl_generic('y', 0i32); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0] + let _: (u32, char) = Trait::without_default_impl_generic('c'); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::u32.Trait[0]::without_default_impl_generic[0] + let _: (u32, bool) = Trait::without_default_impl_generic(false); + + //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0] + let _: (char, char) = Trait::without_default_impl_generic('c'); + //~ TRANS_ITEM fn cgu_export_trait_method[0]::char.Trait[0]::without_default_impl_generic[0] + let _: (char, bool) = Trait::without_default_impl_generic(false); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/function-as-argument.rs b/src/test/codegen-units/function-as-argument.rs new file mode 100644 index 0000000000000..3a9d56c2a8bf7 --- /dev/null +++ b/src/test/codegen-units/function-as-argument.rs @@ -0,0 +1,46 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn take_fn_once(f: F, x: T1, y: T2) { + (f)(x, y) +} + +fn function(_: T1, _: T2) {} + +fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { + (f)(x, y) +} + +//~ TRANS_ITEM fn function_as_argument::main[0] +fn main() { + + //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_once(function, 0u32, "abc"); + + //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_once(function, 'c', 0f64); + + //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_pointer(function, 0i32, ()); + + //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] + //~ TRANS_ITEM fn function_as_argument::function[0] + take_fn_pointer(function, 0f32, 0i64); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-drop-glue.rs b/src/test/codegen-units/generic-drop-glue.rs new file mode 100644 index 0000000000000..f89d6e61bc552 --- /dev/null +++ b/src/test/codegen-units/generic-drop-glue.rs @@ -0,0 +1,98 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +struct StructWithDrop { + x: T1, + y: T2, +} + +impl Drop for StructWithDrop { + fn drop(&mut self) {} +} + +struct StructNoDrop { + x: T1, + y: T2, +} + +enum EnumWithDrop { + A(T1), + B(T2) +} + +impl Drop for EnumWithDrop { + fn drop(&mut self) {} +} + +enum EnumNoDrop { + A(T1), + B(T2) +} + + +struct NonGenericNoDrop(i32); + +struct NonGenericWithDrop(i32); +//~ TRANS_ITEM drop-glue generic_drop_glue::NonGenericWithDrop[0] + +impl Drop for NonGenericWithDrop { + fn drop(&mut self) {} +//~ TRANS_ITEM fn generic_drop_glue::NonGenericWithDrop.Drop[0]::drop[0] +} + +//~ TRANS_ITEM fn generic_drop_glue::main[0] +fn main() { + //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop.Drop[0]::drop[0] + let _ = StructWithDrop { x: 0i8, y: 'a' }.x; + + //~ TRANS_ITEM drop-glue generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ TRANS_ITEM fn generic_drop_glue::StructWithDrop.Drop[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; + + // Should produce no drop glue + let _ = StructNoDrop { x: 'a', y: 0u32 }.x; + + // This is supposed to generate drop-glue because it contains a field that + // needs to be dropped. + //~ TRANS_ITEM drop-glue generic_drop_glue::StructNoDrop[0] + let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; + + //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + let _ = match EnumWithDrop::A::(0) { + EnumWithDrop::A(x) => x, + EnumWithDrop::B(x) => x as i32 + }; + + //~ TRANS_ITEM drop-glue generic_drop_glue::EnumWithDrop[0] + //~ TRANS_ITEM fn generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + let _ = match EnumWithDrop::B::(1.0) { + EnumWithDrop::A(x) => x, + EnumWithDrop::B(x) => x as f64 + }; + + let _ = match EnumNoDrop::A::(0) { + EnumNoDrop::A(x) => x, + EnumNoDrop::B(x) => x as i32 + }; + + let _ = match EnumNoDrop::B::(1.0) { + EnumNoDrop::A(x) => x, + EnumNoDrop::B(x) => x as f64 + }; +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-functions.rs b/src/test/codegen-units/generic-functions.rs new file mode 100644 index 0000000000000..5ec1f7fbc3ca3 --- /dev/null +++ b/src/test/codegen-units/generic-functions.rs @@ -0,0 +1,64 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn foo1(a: T1) -> (T1, u32) { + (a, 1) +} + +fn foo2(a: T1, b: T2) -> (T1, T2) { + (a, b) +} + +fn foo3(a: T1, b: T2, c: T3) -> (T1, T2, T3) { + (a, b, c) +} + +// This function should be instantiated even if no used +//~ TRANS_ITEM fn generic_functions::lifetime_only[0] +pub fn lifetime_only<'a>(a: &'a u32) -> &'a u32 { + a +} + +//~ TRANS_ITEM fn generic_functions::main[0] +fn main() { + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1(2i32); + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1(2i64); + //~ TRANS_ITEM fn generic_functions::foo1[0]<&str> + let _ = foo1("abc"); + //~ TRANS_ITEM fn generic_functions::foo1[0] + let _ = foo1('v'); + + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2(2i32, 2i32); + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2(2i64, "abc"); + //~ TRANS_ITEM fn generic_functions::foo2[0]<&str, usize> + let _ = foo2("a", 2usize); + //~ TRANS_ITEM fn generic_functions::foo2[0] + let _ = foo2('v', ()); + + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(2i32, 2i32, 2i32); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(2i64, "abc", 'c'); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3(0i16, "a", 2usize); + //~ TRANS_ITEM fn generic_functions::foo3[0] + let _ = foo3('v', (), ()); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/generic-impl.rs b/src/test/codegen-units/generic-impl.rs new file mode 100644 index 0000000000000..6e6bb5cbf53c8 --- /dev/null +++ b/src/test/codegen-units/generic-impl.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +struct Struct { + x: T, + f: fn(x: T) -> T, +} + +fn id(x: T) -> T { x } + +impl Struct { + + fn new(x: T) -> Struct { + Struct { + x: x, + f: id + } + } + + fn get(self, x: T2) -> (T, T2) { + (self.x, x) + } +} + +pub struct LifeTimeOnly<'a> { + _a: &'a u32 +} + +impl<'a> LifeTimeOnly<'a> { + + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::foo[0] + pub fn foo(&self) {} + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::bar[0] + pub fn bar(&'a self) {} + //~ TRANS_ITEM fn generic_impl::LifeTimeOnly<'a>[0]::baz[0] + pub fn baz<'b>(&'b self) {} + + pub fn non_instantiated(&self) {} +} + + +//~ TRANS_ITEM fn generic_impl::main[0] +fn main() { + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new(0i32).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new(0i64).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0] + //~ TRANS_ITEM fn generic_impl::id[0] + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0] + let _ = Struct::new('c').get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0]<&str> + //~ TRANS_ITEM fn generic_impl::id[0]<&str> + //~ TRANS_ITEM fn generic_impl::Struct[0]::get[0], i16> + let _ = Struct::new(Struct::new("str")).get(0i16); + + //~ TRANS_ITEM fn generic_impl::Struct[0]::new[0]> + //~ TRANS_ITEM fn generic_impl::id[0]> + let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str")); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/impl-in-non-instantiated-generic.rs new file mode 100644 index 0000000000000..e17a1a7094f2f --- /dev/null +++ b/src/test/codegen-units/impl-in-non-instantiated-generic.rs @@ -0,0 +1,36 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait SomeTrait { + fn foo(&self); +} + +// This function is never instantiated but the contained impl must still be +// discovered. +pub fn generic_function(x: T) -> (T, i32) { + impl SomeTrait for i64 { + //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::i64.SomeTrait[0]::foo[0] + fn foo(&self) {} + } + + (x, 0) +} + +//~ TRANS_ITEM fn impl_in_non_instantiated_generic::main[0] +fn main() { + 0i64.foo(); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/instantiation-through-vtable.rs b/src/test/codegen-units/instantiation-through-vtable.rs new file mode 100644 index 0000000000000..46587b2b0a1b2 --- /dev/null +++ b/src/test/codegen-units/instantiation-through-vtable.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait Trait { + fn foo(&self) -> u32; + fn bar(&self); +} + +struct Struct { + _a: T +} + +impl Trait for Struct { + fn foo(&self) -> u32 { 0 } + fn bar(&self) {} +} + +//~ TRANS_ITEM fn instantiation_through_vtable::main[0] +fn main() { + let s1 = Struct { _a: 0u32 }; + + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::foo[0] + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::bar[0] + let _ = &s1 as &Trait; + + let s1 = Struct { _a: 0u64 }; + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::foo[0] + //~ TRANS_ITEM fn instantiation_through_vtable::Struct.Trait[0]::bar[0] + let _ = &s1 as &Trait; +} diff --git a/src/test/codegen-units/items-within-generic-items.rs b/src/test/codegen-units/items-within-generic-items.rs new file mode 100644 index 0000000000000..a2dcd81b6750c --- /dev/null +++ b/src/test/codegen-units/items-within-generic-items.rs @@ -0,0 +1,44 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +fn generic_fn(a: T) -> (T, i32) { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] + fn nested_fn(a: i32) -> i32 { + a + 1 + } + + let x = { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] + fn nested_fn(a: i32) -> i32 { + a + 2 + } + + 1 + nested_fn(1) + }; + + return (a, x + nested_fn(0)); +} + +//~ TRANS_ITEM fn items_within_generic_items::main[0] +fn main() { + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0i64); + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0u16); + //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + let _ = generic_fn(0i8); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-closures.rs b/src/test/codegen-units/non-generic-closures.rs new file mode 100644 index 0000000000000..bf8804e12ce49 --- /dev/null +++ b/src/test/codegen-units/non-generic-closures.rs @@ -0,0 +1,63 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM fn non_generic_closures::temporary[0] +fn temporary() { + //~ TRANS_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] + (|a: u32| { + let _ = a; + })(4); +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] +fn assigned_to_variable_but_not_executed() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] + let _x = |a: i16| { + let _ = a + 1; + }; +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] +fn assigned_to_variable_executed_indirectly() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] + let f = |a: i32| { + let _ = a + 2; + }; + run_closure(&f); +} + +//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] +fn assigned_to_variable_executed_directly() { + //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] + let f = |a: i64| { + let _ = a + 3; + }; + f(4); +} + +//~ TRANS_ITEM fn non_generic_closures::main[0] +fn main() { + temporary(); + assigned_to_variable_but_not_executed(); + assigned_to_variable_executed_directly(); + assigned_to_variable_executed_indirectly(); +} + +//~ TRANS_ITEM fn non_generic_closures::run_closure[0] +fn run_closure(f: &Fn(i32)) { + f(3); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-drop-glue.rs b/src/test/codegen-units/non-generic-drop-glue.rs new file mode 100644 index 0000000000000..a82e85b7a5315 --- /dev/null +++ b/src/test/codegen-units/non-generic-drop-glue.rs @@ -0,0 +1,56 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue non_generic_drop_glue::StructWithDrop[0] +struct StructWithDrop { + x: i32 +} + +impl Drop for StructWithDrop { + //~ TRANS_ITEM fn non_generic_drop_glue::StructWithDrop.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +struct StructNoDrop { + x: i32 +} + +//~ TRANS_ITEM drop-glue non_generic_drop_glue::EnumWithDrop[0] +enum EnumWithDrop { + A(i32) +} + +impl Drop for EnumWithDrop { + //~ TRANS_ITEM fn non_generic_drop_glue::EnumWithDrop.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +enum EnumNoDrop { + A(i32) +} + +//~ TRANS_ITEM fn non_generic_drop_glue::main[0] +fn main() { + let _ = StructWithDrop { x: 0 }.x; + let _ = StructNoDrop { x: 0 }.x; + let _ = match EnumWithDrop::A(0) { + EnumWithDrop::A(x) => x + }; + let _ = match EnumNoDrop::A(0) { + EnumNoDrop::A(x) => x + }; +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/non-generic-functions.rs b/src/test/codegen-units/non-generic-functions.rs new file mode 100644 index 0000000000000..687ce7fa05cb4 --- /dev/null +++ b/src/test/codegen-units/non-generic-functions.rs @@ -0,0 +1,81 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM fn non_generic_functions::foo[0] +fn foo() { + { + //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[1] + fn foo() {} + foo(); + } +} + +//~ TRANS_ITEM fn non_generic_functions::bar[0] +fn bar() { + //~ TRANS_ITEM fn non_generic_functions::bar[0]::baz[0] + fn baz() {} + baz(); +} + +struct Struct { _x: i32 } + +impl Struct { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0] + fn foo() { + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::foo[0]::foo[1] + fn foo() {} + foo(); + } + } + + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0] + fn bar(&self) { + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[0] + fn foo() {} + foo(); + } + + { + //~ TRANS_ITEM fn non_generic_functions::Struct[0]::bar[0]::foo[1] + fn foo() {} + foo(); + } + } +} + +//~ TRANS_ITEM fn non_generic_functions::main[0] +fn main() { + foo(); + bar(); + Struct::foo(); + let x = Struct { _x: 0 }; + x.bar(); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/overloaded-operators.rs b/src/test/codegen-units/overloaded-operators.rs new file mode 100644 index 0000000000000..134110222f392 --- /dev/null +++ b/src/test/codegen-units/overloaded-operators.rs @@ -0,0 +1,72 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] +#![crate_type="lib"] + +use std::ops::{Index, IndexMut, Add, Deref}; + +pub struct Indexable { + data: [u8; 3] +} + +impl Index for Indexable { + type Output = u8; + + //~ TRANS_ITEM fn overloaded_operators::Indexable.Index[0]::index[0] + fn index(&self, index: usize) -> &Self::Output { + if index >= 3 { + &self.data[0] + } else { + &self.data[index] + } + } +} + +impl IndexMut for Indexable { + //~ TRANS_ITEM fn overloaded_operators::Indexable.IndexMut[0]::index_mut[0] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + if index >= 3 { + &mut self.data[0] + } else { + &mut self.data[index] + } + } +} + + +//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::eq[0] +//~ TRANS_ITEM fn overloaded_operators::Equatable.::std::cmp::PartialEq[0]::ne[0] +#[derive(PartialEq)] +pub struct Equatable(u32); + + +impl Add for Equatable { + type Output = u32; + + //~ TRANS_ITEM fn overloaded_operators::Equatable.Add[0]::add[0] + fn add(self, rhs: u32) -> u32 { + self.0 + rhs + } +} + +impl Deref for Equatable { + type Target = u32; + + //~ TRANS_ITEM fn overloaded_operators::Equatable.Deref[0]::deref[0] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/statics-and-consts.rs b/src/test/codegen-units/statics-and-consts.rs new file mode 100644 index 0000000000000..7c8b2b117ef7c --- /dev/null +++ b/src/test/codegen-units/statics-and-consts.rs @@ -0,0 +1,64 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +static STATIC1: i64 = { + const STATIC1_CONST1: i64 = 2; + 1 + CONST1 as i64 + STATIC1_CONST1 +}; + +const CONST1: i64 = { + const CONST1_1: i64 = { + const CONST1_1_1: i64 = 2; + CONST1_1_1 + 1 + }; + 1 + CONST1_1 as i64 +}; + +fn foo() { + let _ = { + const CONST2: i64 = 0; + static STATIC2: i64 = CONST2; + + let x = { + const CONST2: i64 = 1; + static STATIC2: i64 = CONST2; + STATIC2 + }; + + x + STATIC2 + }; + + let _ = { + const CONST2: i64 = 0; + static STATIC2: i64 = CONST2; + STATIC2 + }; +} + +fn main() { + foo(); + let _ = STATIC1; +} + +//~ TRANS_ITEM static statics_and_consts::STATIC1[0] + +//~ TRANS_ITEM fn statics_and_consts::foo[0] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[0] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[1] +//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[2] + +//~ TRANS_ITEM fn statics_and_consts::main[0] + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-implementations.rs b/src/test/codegen-units/trait-implementations.rs new file mode 100644 index 0000000000000..590859f15a3e1 --- /dev/null +++ b/src/test/codegen-units/trait-implementations.rs @@ -0,0 +1,82 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +pub trait SomeTrait { + fn foo(&self); + fn bar(&self, x: T); +} + +impl SomeTrait for i64 { + + //~ TRANS_ITEM fn trait_implementations::i64.SomeTrait[0]::foo[0] + fn foo(&self) {} + + fn bar(&self, _: T) {} +} + +impl SomeTrait for i32 { + + //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::foo[0] + fn foo(&self) {} + + fn bar(&self, _: T) {} +} + +pub trait SomeGenericTrait { + fn foo(&self, x: T); + fn bar(&self, x: T, y: T2); +} + +// Concrete impl of generic trait +impl SomeGenericTrait for f64 { + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::foo[0] + fn foo(&self, _: u32) {} + + fn bar(&self, _: u32, _: T2) {} +} + +// Generic impl of generic trait +impl SomeGenericTrait for f32 { + + fn foo(&self, _: T) {} + fn bar(&self, _: T, _: T2) {} +} + +//~ TRANS_ITEM fn trait_implementations::main[0] +fn main() { + //~ TRANS_ITEM fn trait_implementations::i32.SomeTrait[0]::bar[0] + 0i32.bar('x'); + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::bar[0]<&str> + 0f64.bar(0u32, "&str"); + + //~ TRANS_ITEM fn trait_implementations::f64.SomeGenericTrait[0]::bar[0]<()> + 0f64.bar(0u32, ()); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::foo[0] + 0f32.foo('x'); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::foo[0] + 0f32.foo(-1i64); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::bar[0] + 0f32.bar(0u32, ()); + + //~ TRANS_ITEM fn trait_implementations::f32.SomeGenericTrait[0]::bar[0]<&str, &str> + 0f32.bar("&str", "&str"); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-method-as-argument.rs b/src/test/codegen-units/trait-method-as-argument.rs new file mode 100644 index 0000000000000..fdf63df547111 --- /dev/null +++ b/src/test/codegen-units/trait-method-as-argument.rs @@ -0,0 +1,62 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait Trait : Sized { + fn foo(self) -> Self { self } +} + +impl Trait for u32 { + fn foo(self) -> u32 { self } +} + +impl Trait for char { +} + +fn take_foo_once T>(f: F, arg: T) -> T { + (f)(arg) +} + +fn take_foo T>(f: F, arg: T) -> T { + (f)(arg) +} + +fn take_foo_mut T>(mut f: F, arg: T) -> T { + (f)(arg) +} + +//~ TRANS_ITEM fn trait_method_as_argument::main[0] +fn main() { + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> + //~ TRANS_ITEM fn trait_method_as_argument::u32.Trait[0]::foo[0] + take_foo_once(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] char> + //~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0] + take_foo_once(Trait::foo, 'c'); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> + take_foo(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> + take_foo(Trait::foo, 'c'); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + take_foo_mut(Trait::foo, 0u32); + + //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + take_foo_mut(Trait::foo, 'c'); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/trait-method-default-impl.rs b/src/test/codegen-units/trait-method-default-impl.rs new file mode 100644 index 0000000000000..2b3b83cb7ec63 --- /dev/null +++ b/src/test/codegen-units/trait-method-default-impl.rs @@ -0,0 +1,70 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +trait SomeTrait { + fn foo(&self) { } + fn bar(&self, x: T) -> T { x } +} + +impl SomeTrait for i8 { + // take the default implementations + + // For the non-generic foo(), we should generate a codegen-item even if it + // is not called anywhere + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] +} + +trait SomeGenericTrait { + fn foo(&self) { } + fn bar(&self, x: T1, y: T2) {} +} + +// Non-generic impl of generic trait +impl SomeGenericTrait for i32 { + // take the default implementations + + // For the non-generic foo(), we should generate a codegen-item even if it + // is not called anywhere + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] +} + +// Non-generic impl of generic trait +impl SomeGenericTrait for u32 { + // take the default implementations + // since nothing is monomorphic here, nothing should be generated unless used somewhere. +} + +//~ TRANS_ITEM fn trait_method_default_impl::main[0] +fn main() { + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + let _ = 1i8.bar('c'); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + let _ = 2i8.bar("&str"); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0i32.bar(0u64, 'c'); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0i32.bar(0u64, "&str"); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0u32.bar(0i8, &['c']); + + //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + 0u32.bar(0i16, ()); +} + +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/transitive-drop-glue.rs b/src/test/codegen-units/transitive-drop-glue.rs new file mode 100644 index 0000000000000..6982cb9299a55 --- /dev/null +++ b/src/test/codegen-units/transitive-drop-glue.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct Root(Intermediate); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Intermediate[0] +struct Intermediate(Leaf); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Leaf[0] +struct Leaf; + +impl Drop for Leaf { + //~ TRANS_ITEM fn transitive_drop_glue::Leaf.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct RootGen(IntermediateGen); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct IntermediateGen(LeafGen); +//~ TRANS_ITEM drop-glue transitive_drop_glue::Root[0] +struct LeafGen(T); + +impl Drop for LeafGen { + fn drop(&mut self) {} +} + +//~ TRANS_ITEM fn transitive_drop_glue::main[0] +fn main() { + + let _ = Root(Intermediate(Leaf)); + + //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn transitive_drop_glue::LeafGen.Drop[0]::drop[0] + let _ = RootGen(IntermediateGen(LeafGen(0u32))); + + //~ TRANS_ITEM drop-glue transitive_drop_glue::RootGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::IntermediateGen[0] + //~ TRANS_ITEM drop-glue transitive_drop_glue::LeafGen[0] + //~ TRANS_ITEM fn transitive_drop_glue::LeafGen.Drop[0]::drop[0] + let _ = RootGen(IntermediateGen(LeafGen(0i16))); +} diff --git a/src/test/codegen-units/tuple-drop-glue.rs b/src/test/codegen-units/tuple-drop-glue.rs new file mode 100644 index 0000000000000..87fcb00eab8c2 --- /dev/null +++ b/src/test/codegen-units/tuple-drop-glue.rs @@ -0,0 +1,32 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] + +//~ TRANS_ITEM drop-glue tuple_drop_glue::Dropped[0] +struct Dropped; + +impl Drop for Dropped { + //~ TRANS_ITEM fn tuple_drop_glue::Dropped.Drop[0]::drop[0] + fn drop(&mut self) {} +} + +//~ TRANS_ITEM fn tuple_drop_glue::main[0] +fn main() { + //~ TRANS_ITEM drop-glue (u32, tuple_drop_glue::Dropped[0]) + let x = (0u32, Dropped); + + //~ TRANS_ITEM drop-glue (i16, (tuple_drop_glue::Dropped[0], bool)) + //~ TRANS_ITEM drop-glue (tuple_drop_glue::Dropped[0], bool) + let x = (0i16, (Dropped, true)); +} diff --git a/src/test/codegen-units/unsizing.rs b/src/test/codegen-units/unsizing.rs new file mode 100644 index 0000000000000..dd90d32858f11 --- /dev/null +++ b/src/test/codegen-units/unsizing.rs @@ -0,0 +1,80 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![deny(dead_code)] +#![feature(coerce_unsized)] +#![feature(unsize)] + +use std::marker::Unsize; +use std::ops::CoerceUnsized; + +trait Trait { + fn foo(&self); +} + +// Simple Case +impl Trait for bool { + fn foo(&self) {} +} + +impl Trait for char { + fn foo(&self) {} +} + +// Struct Field Case +struct Struct { + _a: u32, + _b: i32, + _c: T +} + +impl Trait for f64 { + fn foo(&self) {} +} + +// Custom Coercion Case +impl Trait for u32 { + fn foo(&self) {} +} + +#[derive(Clone, Copy)] +struct Wrapper(*const T); + +impl, U: ?Sized> CoerceUnsized> for Wrapper {} + +//~ TRANS_ITEM fn unsizing::main[0] +fn main() +{ + // simple case + let bool_sized = &true; + //~ TRANS_ITEM fn unsizing::bool.Trait[0]::foo[0] + let _bool_unsized = bool_sized as &Trait; + + let char_sized = &true; + //~ TRANS_ITEM fn unsizing::char.Trait[0]::foo[0] + let _char_unsized = char_sized as &Trait; + + // struct field + let struct_sized = &Struct { + _a: 1, + _b: 2, + _c: 3.0f64 + }; + //~ TRANS_ITEM fn unsizing::f64.Trait[0]::foo[0] + let _struct_unsized = struct_sized as &Struct; + + // custom coercion + let wrapper_sized = Wrapper(&0u32); + //~ TRANS_ITEM fn unsizing::u32.Trait[0]::foo[0] + let _wrapper_sized = wrapper_sized as Wrapper; +} diff --git a/src/test/codegen-units/unused-traits-and-generics.rs b/src/test/codegen-units/unused-traits-and-generics.rs new file mode 100644 index 0000000000000..a4c5099ab9751 --- /dev/null +++ b/src/test/codegen-units/unused-traits-and-generics.rs @@ -0,0 +1,89 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength +// compile-flags:-Zprint-trans-items=eager + +#![crate_type="lib"] +#![deny(dead_code)] + +// This test asserts that no codegen items are generated for generic items that +// are never instantiated in the local crate. + +pub trait Trait { + fn foo() {} + fn bar(&self) {} +} + +pub fn foo(x: T) -> (T, T) { + (x, x) +} + +pub struct Struct { + x: T +} + +impl Struct { + pub fn foo(self) -> T { + self.x + } + + pub fn bar() {} +} + +pub enum Enum { + A(T), + B { x: T } +} + +impl Enum { + pub fn foo(self) -> T { + match self { + Enum::A(x) => x, + Enum::B { x } => x, + } + } + + pub fn bar() {} +} + +pub struct TupleStruct(T); + +impl TupleStruct { + pub fn foo(self) -> T { + self.0 + } + + pub fn bar() {} +} + +pub type Pair = (T, T); + +pub struct NonGeneric { + x: i32 +} + +impl NonGeneric { + pub fn foo(self) -> i32 { + self.x + } + + pub fn generic_foo(&self, x: T) -> (T, i32) { + (x, self.x) + } + + pub fn generic_bar(x: T) -> (T, T) { + (x, x) + } +} + +// Only the non-generic methods should be instantiated: +//~ TRANS_ITEM fn unused_traits_and_generics::NonGeneric[0]::foo[0] +//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/compile-fail/blind-item-block-middle.rs b/src/test/compile-fail/blind-item-block-middle.rs index fbb0730f01461..24a1e4e24d81a 100644 --- a/src/test/compile-fail/blind-item-block-middle.rs +++ b/src/test/compile-fail/blind-item-block-middle.rs @@ -14,5 +14,4 @@ fn main() { let bar = 5; //~^ ERROR declaration of `bar` shadows an enum variant or unit-like struct in scope use foo::bar; - //~^ ERROR imports are not allowed after non-item statements } diff --git a/src/test/compile-fail/issue-25385.rs b/src/test/compile-fail/issue-25385.rs new file mode 100644 index 0000000000000..4aacb6840e9d5 --- /dev/null +++ b/src/test/compile-fail/issue-25385.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +macro_rules! foo { + ($e:expr) => { $e.foo() } + //~^ ERROR no method named `foo` found for type `i32` in the current scope +} + +fn main() { + let a = 1i32; + foo!(a); + //~^ NOTE in this expansion of foo! + + foo!(1i32.foo()); + //~^ ERROR no method named `foo` found for type `i32` in the current scope +} diff --git a/src/test/compile-fail/issue-25386.rs b/src/test/compile-fail/issue-25386.rs new file mode 100644 index 0000000000000..297d3aacfd51e --- /dev/null +++ b/src/test/compile-fail/issue-25386.rs @@ -0,0 +1,40 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod stuff { + pub struct Item { + c_object: Box, + } + pub struct CObj { + name: Option, + } + impl Item { + pub fn new() -> Item { + Item { + c_object: Box::new(CObj { name: None }), + } + } + } +} + +macro_rules! check_ptr_exist { + ($var:expr, $member:ident) => ( + (*$var.c_object).$member.is_some() + //~^ ERROR field `name` of struct `stuff::CObj` is private + //~^^ ERROR field `c_object` of struct `stuff::Item` is private + ); +} + +fn main() { + let item = stuff::Item::new(); + println!("{}", check_ptr_exist!(item, name)); + //~^ NOTE in this expansion of check_ptr_exist! + //~^^ NOTE in this expansion of check_ptr_exist! +} diff --git a/src/test/compile-fail/issue-25793.rs b/src/test/compile-fail/issue-25793.rs new file mode 100644 index 0000000000000..fd3e3186bc5c9 --- /dev/null +++ b/src/test/compile-fail/issue-25793.rs @@ -0,0 +1,33 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! width( + ($this:expr) => { + $this.width.unwrap() + //~^ ERROR cannot use `self.width` because it was mutably borrowed + } +); + +struct HasInfo { + width: Option +} + +impl HasInfo { + fn get_size(&mut self, n: usize) -> usize { + n + } + + fn get_other(&mut self) -> usize { + self.get_size(width!(self)) + //~^ NOTE in this expansion of width! + } +} + +fn main() {} diff --git a/src/test/compile-fail/issue-26093.rs b/src/test/compile-fail/issue-26093.rs new file mode 100644 index 0000000000000..2f43388b7afb0 --- /dev/null +++ b/src/test/compile-fail/issue-26093.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! not_an_lvalue { + ($thing:expr) => { + $thing = 42; + //~^ ERROR invalid left-hand side expression + } +} + +fn main() { + not_an_lvalue!(99); + //~^ NOTE in this expansion of not_an_lvalue! +} diff --git a/src/test/compile-fail/issue-26094.rs b/src/test/compile-fail/issue-26094.rs new file mode 100644 index 0000000000000..99add95e806f6 --- /dev/null +++ b/src/test/compile-fail/issue-26094.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! some_macro { + ($other: expr) => ({ + $other(None) + //~^ this function takes 0 parameters but 1 parameter was supplied + }) +} + +fn some_function() {} + +fn main() { + some_macro!(some_function); + //~^ in this expansion of some_macro! +} diff --git a/src/test/compile-fail/issue-26237.rs b/src/test/compile-fail/issue-26237.rs new file mode 100644 index 0000000000000..11e236d22126b --- /dev/null +++ b/src/test/compile-fail/issue-26237.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! macro_panic { + ($not_a_function:expr, $some_argument:ident) => { + $not_a_function($some_argument) + //~^ ERROR expected function, found `_` + } +} + +fn main() { + let mut value_a = 0; + let mut value_b = 0; + macro_panic!(value_a, value_b); + //~^ in this expansion of macro_panic! +} diff --git a/src/test/compile-fail/issue-26480.rs b/src/test/compile-fail/issue-26480.rs new file mode 100644 index 0000000000000..903df42291c63 --- /dev/null +++ b/src/test/compile-fail/issue-26480.rs @@ -0,0 +1,44 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern { + fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64; +} + +#[inline(always)] +fn size_of(_: T) -> usize { + ::std::mem::size_of::() +} + +macro_rules! write { + ($arr:expr) => {{ + #[allow(non_upper_case_globals)] + const stdout: i32 = 1; + unsafe { + write(stdout, $arr.as_ptr() as *const i8, + $arr.len() * size_of($arr[0])); + //~^ ERROR mismatched types + } + }} +} + +macro_rules! cast { + ($x:expr) => ($x as ()) + //~^ ERROR non-scalar cast: `i32` as `()` +} + +fn main() { + let hello = ['H', 'e', 'y']; + write!(hello); + //~^ NOTE in this expansion of write! + + cast!(2); + //~^ NOTE in this expansion of cast! +} diff --git a/src/test/compile-fail/issue-28308.rs b/src/test/compile-fail/issue-28308.rs new file mode 100644 index 0000000000000..b0c44b5f33af1 --- /dev/null +++ b/src/test/compile-fail/issue-28308.rs @@ -0,0 +1,17 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// this error is dispayed in `` +// error-pattern:cannot apply unary operator `!` to type `&'static str` +// error-pattern:in this expansion of assert! + +fn main() { + assert!("foo"); +} diff --git a/src/test/compile-fail/issue-29084.rs b/src/test/compile-fail/issue-29084.rs new file mode 100644 index 0000000000000..78913e759a1cc --- /dev/null +++ b/src/test/compile-fail/issue-29084.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! foo { + ($d:expr) => {{ + fn bar(d: u8) { } + bar(&mut $d); + //~^ ERROR mismatched types + }} +} + +fn main() { + foo!(0u8); + //~^ NOTE in this expansion of foo! +} diff --git a/src/test/compile-fail/issue-31011.rs b/src/test/compile-fail/issue-31011.rs new file mode 100644 index 0000000000000..b828b11030d71 --- /dev/null +++ b/src/test/compile-fail/issue-31011.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +macro_rules! log { + ( $ctx:expr, $( $args:expr),* ) => { + if $ctx.trace { + //~^ ERROR attempted access of field `trace` on type `&T`, but no field with that name + println!( $( $args, )* ); + } + } +} + +// Create a structure. +struct Foo { + trace: bool, +} + +// Generic wrapper calls log! with a structure. +fn wrap(context: &T) -> () +{ + log!(context, "entered wrapper"); + //~^ in this expansion of log! +} + +fn main() { + // Create a structure. + let x = Foo { trace: true }; + log!(x, "run started"); + // Apply a closure which accesses internal fields. + wrap(&x); + log!(x, "run finished"); +} diff --git a/src/test/compile-fail/issue-31212.rs b/src/test/compile-fail/issue-31212.rs new file mode 100644 index 0000000000000..b9a4de701f513 --- /dev/null +++ b/src/test/compile-fail/issue-31212.rs @@ -0,0 +1,20 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This checks that a path that cannot be resolved because of an indeterminate import +// does not trigger an ICE. + +mod foo { + pub use self::*; //~ ERROR unresolved +} + +fn main() { + foo::f(); //~ ERROR unresolved +} diff --git a/src/test/run-make/json-errors/Makefile b/src/test/run-make/json-errors/Makefile index 2467e08300c18..e6701224ccb6b 100644 --- a/src/test/run-make/json-errors/Makefile +++ b/src/test/run-make/json-errors/Makefile @@ -4,5 +4,5 @@ all: cp foo.rs $(TMPDIR) cd $(TMPDIR) -$(RUSTC) -Z unstable-options --error-format=json foo.rs 2>foo.log - grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","span":{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19},"children":\[\]}' foo.log - grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","span":{.*},"children":\[{"message":"the .*","code":null,"level":"help","span":{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0},"children":\[\]},{"message":" ","code":null,"level":"help",' foo.log + grep -q '{"message":"unresolved name `y`","code":{"code":"E0425","explanation":"\\nAn unresolved name was used. Example of erroneous codes.*"},"level":"error","spans":\[{"file_name":"foo.rs","byte_start":496,"byte_end":497,"line_start":12,"line_end":12,"column_start":18,"column_end":19}\],"children":\[\]}' foo.log + grep -q '{"message":".*","code":{"code":"E0277","explanation":"\\nYou tried.*"},"level":"error","spans":\[{.*}\],"children":\[{"message":"the .*","code":null,"level":"help","spans":\[{"file_name":"foo.rs","byte_start":504,"byte_end":516,"line_start":14,"line_end":14,"column_start":0,"column_end":0}\],"children":\[\]},{"message":" ","code":null,"level":"help",' foo.log diff --git a/src/test/compile-fail/blind-item-local-shadow.rs b/src/test/run-pass/blind-item-local-shadow.rs similarity index 90% rename from src/test/compile-fail/blind-item-local-shadow.rs rename to src/test/run-pass/blind-item-local-shadow.rs index 5cc087cb66e3c..bb654b1a20b91 100644 --- a/src/test/compile-fail/blind-item-local-shadow.rs +++ b/src/test/run-pass/blind-item-local-shadow.rs @@ -15,6 +15,5 @@ mod bar { fn main() { let foo = || false; use bar::foo; - //~^ ERROR imports are not allowed after non-item statements assert_eq!(foo(), false); } diff --git a/src/test/run-pass/issue-31267-additional.rs b/src/test/run-pass/issue-31267-additional.rs new file mode 100644 index 0000000000000..a6b4252588786 --- /dev/null +++ b/src/test/run-pass/issue-31267-additional.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(associated_consts)] + +#[derive(Clone, Copy, Debug)] +struct Bar; + +const BAZ: Bar = Bar; + +#[derive(Debug)] +struct Foo([Bar; 1]); + +struct Biz; + +impl Biz { + const BAZ: Foo = Foo([BAZ; 1]); +} + +fn main() { + let foo = Biz::BAZ; + println!("{:?}", foo); +} diff --git a/src/test/run-pass/issue-31267.rs b/src/test/run-pass/issue-31267.rs new file mode 100644 index 0000000000000..90eb0f6c841ee --- /dev/null +++ b/src/test/run-pass/issue-31267.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for issue #31267 + +#![feature(associated_consts)] + +struct Foo; + +impl Foo { + const FOO: [i32; 3] = [0; 3]; +} + +pub fn main() { + let foo = Foo::FOO; + assert_eq!(foo, [0i32, 0, 0]); +} diff --git a/src/test/run-pass/multi-panic.rs b/src/test/run-pass/multi-panic.rs index 7bf07314dcc17..6a0d7278b5e16 100644 --- a/src/test/run-pass/multi-panic.rs +++ b/src/test/run-pass/multi-panic.rs @@ -17,7 +17,10 @@ fn main() { panic!(); } else { - let test = std::process::Command::new(&args[0]).arg("run_test").output().unwrap(); + let test = std::process::Command::new(&args[0]).arg("run_test") + .env_remove("RUST_BACKTRACE") + .output() + .unwrap(); assert!(!test.status.success()); let err = String::from_utf8_lossy(&test.stderr); let mut it = err.lines(); diff --git a/src/test/rustdoc/tuples.rs b/src/test/rustdoc/tuples.rs new file mode 100644 index 0000000000000..2269b38780df0 --- /dev/null +++ b/src/test/rustdoc/tuples.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// @has foo/fn.tuple0.html //pre 'pub fn tuple0(x: ())' +pub fn tuple0(x: ()) -> () { x } +// @has foo/fn.tuple1.html //pre 'pub fn tuple1(x: (i32,)) -> (i32,)' +pub fn tuple1(x: (i32,)) -> (i32,) { x } +// @has foo/fn.tuple2.html //pre 'pub fn tuple2(x: (i32, i32)) -> (i32, i32)' +pub fn tuple2(x: (i32, i32)) -> (i32, i32) { x }