diff --git a/README.md b/README.md index acd9cb6afc8d7..5e208a76e0315 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ Read ["Installing Rust"] from [The Book]. # Update package mirrors (may be needed if you have a fresh install of MSYS2) $ pacman -Sy pacman-mirrors - # Choose one based on platform: + # Choose one based on platform: + # *** see the note below *** $ pacman -S mingw-w64-i686-toolchain $ pacman -S mingw-w64-x86_64-toolchain @@ -89,9 +90,12 @@ Read ["Installing Rust"] from [The Book]. ``` > ***Note:*** gcc versions >= 5 currently have issues building LLVM on Windows > resulting in a segmentation fault when building Rust. In order to avoid this -> it may be necessary to obtain an earlier version of gcc such as 4.9.x. -> Installers for earlier Windows builds of gcc are available at the -> [Mingw-Builds] project. For more information on this see issue #28260. +> it may be necessary to obtain an earlier version of gcc such as 4.9.x. +> Msys's `pacman` will install the latest version, so for the time being it is +> recommended to skip gcc toolchain installation step above and use [Mingw-Builds] +> project's installer instead. Be sure to add gcc `bin` directory to the path +> before running `configure`. +> For more information on this see issue #28260. [Mingw-Builds]: http://sourceforge.net/projects/mingw-w64/ diff --git a/RELEASES.md b/RELEASES.md index 9b61ce0e05414..54fe87dc65816 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,182 @@ +Version 1.4.0 (2015-10-29) +============================ + +* ~1200 changes, numerous bugfixes + +Highlights +---------- + +* Windows builds targeting the 64-bit MSVC ABI and linker (instead of + GNU) are now supported and recommended for use. + +Breaking Changes +---------------- + +* [Several changes have been made to fix type soundness and improve + the behavior of associated types][sound]. See [RFC 1214]. Although + we have mostly introduced these changes as warnings this release, to + become errors next release, there are still some scenarios that will + see immediate breakage. +* [The `str::lines` and `BufRead::lines` iterators treat `\r\n` as + line breaks in addition to `\n`][crlf]. +* [Loans of `'static` lifetime extend to the end of a function][stat]. +* [`str::parse` no longer introduces avoidable rounding error when + parsing floating point numbers. Together with earlier changes to + float formatting/output, "round trips" like f.to_string().parse() + now preserve the value of f exactly. Additionally, leading plus + signs are now accepted][fp3]. + + +Language +-------- + +* `use` statements that import multiple items [can now rename + them][i], as in `use foo::{bar as kitten, baz as puppy}`. +* [Binops work correctly on fat pointers][binfat]. +* `pub extern crate`, which does not behave as expected, [issues a + warning][pec] until a better solution is found. + +Libraries +--------- + +* [Many APIs were stabilized][stab]: `>::into_string`, + [`Arc::downgrade`], [`Arc::get_mut`], [`Arc::make_mut`], + [`Arc::try_unwrap`], [`Box::from_raw`], [`Box::into_raw`], [`CStr::to_str`], + [`CStr::to_string_lossy`], [`CString::from_raw`], [`CString::into_raw`], + [`IntoRawFd::into_raw_fd`], [`IntoRawFd`], + `IntoRawHandle::into_raw_handle`, `IntoRawHandle`, + `IntoRawSocket::into_raw_socket`, `IntoRawSocket`, [`Rc::downgrade`], + [`Rc::get_mut`], [`Rc::make_mut`], [`Rc::try_unwrap`], [`Result::expect`], + [`String::into_boxed_str`], [`TcpStream::read_timeout`], + [`TcpStream::set_read_timeout`], [`TcpStream::set_write_timeout`], + [`TcpStream::write_timeout`], [`UdpSocket::read_timeout`], + [`UdpSocket::set_read_timeout`], [`UdpSocket::set_write_timeout`], + [`UdpSocket::write_timeout`], `Vec::append`, `Vec::split_off`, + [`VecDeque::append`], [`VecDeque::retain`], [`VecDeque::split_off`], + [`rc::Weak::upgrade`], [`rc::Weak`], [`slice::Iter::as_slice`], + [`slice::IterMut::into_slice`], [`str::CharIndices::as_str`], + [`str::Chars::as_str`], [`str::split_at_mut`], [`str::split_at`], + [`sync::Weak::upgrade`], [`sync::Weak`], [`thread::park_timeout`], + [`thread::sleep`]. +* [Some APIs were deprecated][dep]: `BTreeMap::with_b`, + `BTreeSet::with_b`, `Option::as_mut_slice`, `Option::as_slice`, + `Result::as_mut_slice`, `Result::as_slice`, `f32::from_str_radix`, + `f64::from_str_radix`. +* [Reverse-searching strings is faster with the 'two-way' + algorithm][s]. +* [`std::io::copy` allows `?Sized` arguments][cc]. +* The `Windows`, `Chunks`, and `ChunksMut` iterators over slices all + [override `count`, `nth` and `last` with an O(1) + implementation][it]. +* [`Default` is implemented for arrays up to `[T; 32]`][d]. +* [`IntoRawFd` has been added to the Unix-specific prelude, + `IntoRawSocket` and `IntoRawHandle` to the Windows-specific + prelude][pr]. +* [`Extend` and `FromIterator` where `T: + Copy`][ext] as part of [RFC 839]. This will cause type inferance + breakage in rare situations. +* [`BinaryHeap` implements `Debug`][bh2]. +* [`Borrow` and `BorrowMut` are implemented for fixed-size + arrays][bm]. +* [`extern fn`s with the "Rust" and "C" ABIs implement common + traits including `Eq`, `Ord`, `Debug`, `Hash`][fp]. +* [String comparison is faster][faststr]. +* `&mut T` where `T: std::fmt::Write` [also implements + `std::fmt::Write`][mutw]. +* [A stable regression in `VecDeque::push_back` and other + capicity-altering methods that caused panics for zero-sized types + was fixed][vd]. +* [Function pointers implement traits for up to 12 parameters][fp2]. + +Miscellaneous +------------- + +* The compiler [no longer uses the 'morestack' feature to prevent + stack overflow][mm]. Instead it uses guard pages and stack + probes (though stack probes are not yet implemented on any platform + but Windows). +* [The compiler matches traits faster when projections are involved][p]. +* The 'improper_ctypes' lint [no longer warns about use of `isize` and + `usize`][ffi]. +* [Cargo now displays useful information about what its doing during + `cargo update`][cu]. + +[`Arc::downgrade`]: http://doc.rust-lang.org/nightly/alloc/arc/struct.Arc.html#method.downgrade +[`Arc::make_mut`]: http://doc.rust-lang.org/nightly/alloc/arc/struct.Arc.html#method.make_mut +[`Arc::get_mut`]: http://doc.rust-lang.org/nightly/alloc/arc/struct.Arc.html#method.get_mut +[`Arc::try_unwrap`]: http://doc.rust-lang.org/nightly/alloc/arc/struct.Arc.html#method.try_unwrap +[`Box::from_raw`]: http://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html#method.from_raw +[`Box::into_raw`]: http://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html#method.into_raw +[`CStr::to_str`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.to_str +[`CStr::to_string_lossy`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CStr.html#method.to_string_lossy +[`CString::from_raw`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html#method.from_raw +[`CString::into_raw`]: http://doc.rust-lang.org/nightly/std/ffi/struct.CString.html#method.into_raw +[`IntoRawFd::into_raw_fd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.IntoRawFd.html#tymethod.into_raw_fd +[`IntoRawFd`]: http://doc.rust-lang.org/nightly/std/os/unix/io/trait.IntoRawFd.html +[`Rc::downgrade`]: http://doc.rust-lang.org/nightly/alloc/rc/struct.Rc.html#method.downgrade +[`Rc::get_mut`]: http://doc.rust-lang.org/nightly/alloc/rc/struct.Rc.html#method.get_mut +[`Rc::make_mut`]: http://doc.rust-lang.org/nightly/alloc/rc/struct.Rc.html#method.make_mut +[`Rc::try_unwrap`]: http://doc.rust-lang.org/nightly/alloc/rc/struct.Rc.html#method.try_unwrap +[`Result::expect`]: http://doc.rust-lang.org/nightly/core/result/enum.Result.html#method.expect +[`String::into_boxed_str`]: http://doc.rust-lang.org/nightly/collections/string/struct.String.html#method.into_boxed_str +[`TcpStream::read_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.read_timeout +[`TcpStream::set_read_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.set_read_timeout +[`TcpStream::write_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.write_timeout +[`TcpStream::set_write_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.set_write_timeout +[`UdpSocket::read_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.read_timeout +[`UdpSocket::set_read_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.set_read_timeout +[`UdpSocket::write_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.write_timeout +[`UdpSocket::set_write_timeout`]: http://doc.rust-lang.org/nightly/std/net/struct.TcpStream.html#method.set_write_timeout +[`VecDeque::append`]: http://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.append +[`VecDeque::retain`]: http://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.retain +[`VecDeque::split_off`]: http://doc.rust-lang.org/nightly/std/collections/struct.VecDeque.html#method.split_off +[`rc::Weak::upgrade`]: http://doc.rust-lang.org/nightly/std/rc/struct.Weak.html#method.upgrade +[`rc::Weak`]: http://doc.rust-lang.org/nightly/std/rc/struct.Weak.html +[`slice::Iter::as_slice`]: http://doc.rust-lang.org/nightly/std/slice/struct.Iter.html#method.as_slice +[`slice::IterMut::into_slice`]: http://doc.rust-lang.org/nightly/std/slice/struct.IterMut.html#method.into_slice +[`str::CharIndices::as_str`]: http://doc.rust-lang.org/nightly/std/str/struct.CharIndices.html#method.as_str +[`str::Chars::as_str`]: http://doc.rust-lang.org/nightly/std/str/struct.Chars.html#method.as_str +[`str::split_at_mut`]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_at_mut +[`str::split_at`]: http://doc.rust-lang.org/nightly/std/primitive.str.html#method.split_at +[`sync::Weak::upgrade`]: http://doc.rust-lang.org/nightly/std/sync/struct.Weak.html#method.upgrade +[`sync::Weak`]: http://doc.rust-lang.org/nightly/std/sync/struct.Weak.html +[`thread::park_timeout`]: http://doc.rust-lang.org/nightly/std/thread/fn.park_timeout.html +[`thread::sleep`]: http://doc.rust-lang.org/nightly/std/thread/fn.sleep.html +[bh2]: https://github.com/rust-lang/rust/pull/28156 +[binfat]: https://github.com/rust-lang/rust/pull/28270 +[bm]: https://github.com/rust-lang/rust/pull/28197 +[cc]: https://github.com/rust-lang/rust/pull/27531 +[crlf]: https://github.com/rust-lang/rust/pull/28034 +[cu]: https://github.com/rust-lang/cargo/pull/1931 +[d]: https://github.com/rust-lang/rust/pull/27825 +[dep]: https://github.com/rust-lang/rust/pull/28339 +[es]: https://github.com/rust-lang/rust/pull/27956 +[ext]: https://github.com/rust-lang/rust/pull/28094 +[faststr]: https://github.com/rust-lang/rust/pull/28338 +[ffi]: https://github.com/rust-lang/rust/pull/28779 +[fp]: https://github.com/rust-lang/rust/pull/28268 +[fp2]: https://github.com/rust-lang/rust/pull/28560 +[fp3]: https://github.com/rust-lang/rust/pull/27307 +[i]: https://github.com/rust-lang/rust/pull/27451 +[into2]: https://github.com/rust-lang/rust/pull/28039 +[it]: https://github.com/rust-lang/rust/pull/27652 +[mm]: https://github.com/rust-lang/rust/pull/27338 +[mutw]: https://github.com/rust-lang/rust/pull/28368 +[sound]: https://github.com/rust-lang/rust/pull/27641 +[p]: https://github.com/rust-lang/rust/pull/27866 +[pec]: https://github.com/rust-lang/rust/pull/28486 +[pr]: https://github.com/rust-lang/rust/pull/27896 +[RFC 839]: https://github.com/rust-lang/rfcs/blob/master/text/0839-embrace-extend-extinguish.md +[RFC 1214]: https://github.com/rust-lang/rfcs/blob/master/text/1214-projections-lifetimes-and-wf.md +[s]: https://github.com/rust-lang/rust/pull/27474 +[stab]: https://github.com/rust-lang/rust/pull/28339 +[stat]: https://github.com/rust-lang/rust/pull/28321 +[vd]: https://github.com/rust-lang/rust/pull/28494 + Version 1.3.0 (2015-09-17) ============================== diff --git a/configure b/configure index 76bee86182282..4fb6cb53bd855 100755 --- a/configure +++ b/configure @@ -1484,25 +1484,9 @@ do done fi - if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -ne 0 ] + # We need the generator later on for compiler-rt even if LLVM's not built + if [ ${is_msvc} -ne 0 ] then - msg "configuring LLVM for $t with cmake" - - CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF" - if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then - CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug" - else - CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release" - fi - if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] - then - CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF" - else - CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON" - fi - - msg "configuring LLVM with:" - msg "$CMAKE_ARGS" case "$CFG_MSVC_ROOT" in *14.0*) generator="Visual Studio 14 2015" @@ -1524,8 +1508,32 @@ do err "can only build LLVM for x86 platforms" ;; esac + CFG_CMAKE_GENERATOR=$generator + putvar CFG_CMAKE_GENERATOR + fi + + if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -ne 0 ] + then + msg "configuring LLVM for $t with cmake" + + CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF" + if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug" + else + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release" + fi + if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] + then + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF" + else + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON" + fi + + msg "configuring LLVM with:" + msg "$CMAKE_ARGS" + (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \ - -G "$generator" \ + -G "$CFG_CMAKE_GENERATOR" \ $CMAKE_ARGS) need_ok "LLVM cmake configure failed" fi diff --git a/mk/crates.mk b/mk/crates.mk index 6673b9b28e716..2a627a6da2bd6 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -103,7 +103,7 @@ DEPS_rustc_mir := rustc rustc_front syntax DEPS_rustc_resolve := rustc rustc_front log syntax DEPS_rustc_platform_intrinsics := rustc rustc_llvm DEPS_rustc_privacy := rustc rustc_front log syntax -DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \ +DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \ log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics diff --git a/mk/dist.mk b/mk/dist.mk index 4cdee8bda9d4c..7ef95d1e78f80 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -59,6 +59,7 @@ PKG_FILES := \ libcoretest \ libbacktrace \ rt \ + rtstartup \ rustllvm \ snapshots.txt \ rust-installer \ diff --git a/mk/rt.mk b/mk/rt.mk index 1f60aaed4730b..d8b5aeccdcd44 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -216,22 +216,31 @@ COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1)) COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt -# Note that on MSVC-targeting builds we hardwire CC/AR to gcc/ar even though -# we're targeting MSVC. This is because although compiler-rt has a CMake build -# config I can't actually figure out how to use it, so I'm not sure how to use -# cl.exe to build the objects. Additionally, the compiler-rt library when built -# with gcc has the same ABI as cl.exe, so they're largely compatible -COMPRT_CC_$(1) := $$(CC_$(1)) -COMPRT_AR_$(1) := $$(AR_$(1)) -COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) ifeq ($$(findstring msvc,$(1)),msvc) -COMPRT_CC_$(1) := gcc -COMPRT_AR_$(1) := ar -ifeq ($$(findstring i686,$(1)),i686) -COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m32 +$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$(1)) + @$$(call E, cmake: compiler-rt) + $$(Q)cd "$$(COMPRT_BUILD_DIR_$(1))"; $$(CFG_CMAKE) "$(S)src/compiler-rt" \ + -DCMAKE_BUILD_TYPE=$$(LLVM_BUILD_CONFIG_MODE) \ + -DLLVM_CONFIG_PATH=$$(LLVM_CONFIG_$(1)) \ + -G"$$(CFG_CMAKE_GENERATOR)" + $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \ + --target lib/builtins/builtins \ + --config $$(LLVM_BUILD_CONFIG_MODE) \ + -- //v:m //nologo + $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/lib/windows/$$(LLVM_BUILD_CONFIG_MODE)/clang_rt.builtins-$$(HOST_$(1)).lib $$@ else -COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64 -endif +COMPRT_CC_$(1) := $$(CC_$(1)) +COMPRT_AR_$(1) := $$(AR_$(1)) +# We chomp -Werror here because GCC warns about the type signature of +# builtins not matching its own and the build fails. It's a bit hacky, +# but what can we do, we're building libclang-rt using GCC ...... +COMPRT_CFLAGS_$(1) := $$(subst -Werror,,$$(CFG_GCCISH_CFLAGS_$(1))) -std=c99 + +# FreeBSD Clang's packaging is problematic; it doesn't copy unwind.h to +# the standard include directory. This should really be in our changes to +# compiler-rt, but we override the CFLAGS here so there isn't much choice +ifeq ($$(findstring freebsd,$(1)),freebsd) + COMPRT_CFLAGS_$(1) += -I/usr/include/c++/v1 endif $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) @@ -246,7 +255,7 @@ $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) TargetTriple=$(1) \ triple-builtins $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$@ - +endif ################################################################################ # libbacktrace # diff --git a/src/compiler-rt b/src/compiler-rt index 58ab642c30d9f..93d0384373750 160000 --- a/src/compiler-rt +++ b/src/compiler-rt @@ -1 +1 @@ -Subproject commit 58ab642c30d9f97735d5745b5d01781ee199c6ae +Subproject commit 93d0384373750b52996fd7f8249adaae39f562d8 diff --git a/src/doc/trpl/error-handling.md b/src/doc/trpl/error-handling.md index 56dfa17b4e3f2..c693cceeac482 100644 --- a/src/doc/trpl/error-handling.md +++ b/src/doc/trpl/error-handling.md @@ -1605,14 +1605,11 @@ arguments. ## Writing the logic -We're all different in how we write code, but error handling is -usually the last thing we want to think about. This isn't very good -practice for good design, but it can be useful for rapidly -prototyping. In our case, because Rust forces us to be explicit about -error handling, it will also make it obvious what parts of our program -can cause errors. Why? Because Rust will make us call `unwrap`! This -can give us a nice bird's eye view of how we need to approach error -handling. +We all write code differently, but error handling is usually the last thing we +want to think about. This isn't great for the overall design of a program, but +it can be useful for rapid prototyping. Because Rust forces us to be explicit +about error handling (by making us call `unwrap`), it is easy to see which +parts of our program can cause errors. In this case study, the logic is really simple. All we need to do is parse the CSV data given to us and print out a field in matching rows. Let's do it. (Make @@ -2074,7 +2071,7 @@ tweak the case analysis in `main`: ```rust,ignore match search(&args.arg_data_path, &args.arg_city) { Err(CliError::NotFound) if args.flag_quiet => process::exit(1), - Err(err) => fatal!("{}", err), + Err(err) => panic!("{}", err), Ok(pops) => for pop in pops { println!("{}, {}: {:?}", pop.city, pop.country, pop.count); } diff --git a/src/doc/trpl/method-syntax.md b/src/doc/trpl/method-syntax.md index d31d823247082..41c134b29f3d1 100644 --- a/src/doc/trpl/method-syntax.md +++ b/src/doc/trpl/method-syntax.md @@ -43,8 +43,6 @@ fn main() { This will print `12.566371`. - - We’ve made a `struct` that represents a circle. We then write an `impl` block, and inside it, define a method, `area`. @@ -83,6 +81,35 @@ impl Circle { } ``` +You can use as many `impl` blocks as you’d like. The previous example could +have also been written like this: + +```rust +struct Circle { + x: f64, + y: f64, + radius: f64, +} + +impl Circle { + fn reference(&self) { + println!("taking self by reference!"); + } +} + +impl Circle { + fn mutable_reference(&mut self) { + println!("taking self by mutable reference!"); + } +} + +impl Circle { + fn takes_ownership(self) { + println!("taking ownership of self!"); + } +} +``` + # Chaining method calls So, now we know how to call a method, such as `foo.bar()`. But what about our diff --git a/src/doc/trpl/rust-inside-other-languages.md b/src/doc/trpl/rust-inside-other-languages.md index 5c0bde02f9605..61627c0b5a2fe 100644 --- a/src/doc/trpl/rust-inside-other-languages.md +++ b/src/doc/trpl/rust-inside-other-languages.md @@ -177,7 +177,8 @@ build deps examples libembed.so native That `libembed.so` is our ‘shared object’ library. We can use this file just like any shared object library written in C! As an aside, this may be -`embed.dll` or `libembed.dylib`, depending on the platform. +`embed.dll` (Microsoft Windows) or `libembed.dylib` (Mac OS X), depending on +your operating system. Now that we’ve got our Rust library built, let’s use it from our Ruby. diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index ea4830fc3e6ce..869e7a0b38393 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -732,6 +732,8 @@ impl [T] { /// /// This is equivalent to `self.sort_by(|a, b| a.cmp(b))`. /// + /// This is a stable sort. + /// /// # Examples /// /// ```rust diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 96a28d3ee3ba3..8d07e2b2018ad 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -963,7 +963,7 @@ impl PartialEq for String { macro_rules! impl_eq { ($lhs:ty, $rhs: ty) => { #[stable(feature = "rust1", since = "1.0.0")] - impl<'a> PartialEq<$rhs> for $lhs { + impl<'a, 'b> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&self[..], &other[..]) } #[inline] @@ -971,7 +971,7 @@ macro_rules! impl_eq { } #[stable(feature = "rust1", since = "1.0.0")] - impl<'a> PartialEq<$lhs> for $rhs { + impl<'a, 'b> PartialEq<$lhs> for $rhs { #[inline] fn eq(&self, other: &$lhs) -> bool { PartialEq::eq(&self[..], &other[..]) } #[inline] @@ -984,24 +984,9 @@ macro_rules! impl_eq { impl_eq! { String, str } impl_eq! { String, &'a str } impl_eq! { Cow<'a, str>, str } +impl_eq! { Cow<'a, str>, &'b str } impl_eq! { Cow<'a, str>, String } -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b> PartialEq<&'b str> for Cow<'a, str> { - #[inline] - fn eq(&self, other: &&'b str) -> bool { PartialEq::eq(&self[..], &other[..]) } - #[inline] - fn ne(&self, other: &&'b str) -> bool { PartialEq::ne(&self[..], &other[..]) } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b> PartialEq> for &'b str { - #[inline] - fn eq(&self, other: &Cow<'a, str>) -> bool { PartialEq::eq(&self[..], &other[..]) } - #[inline] - fn ne(&self, other: &Cow<'a, str>) -> bool { PartialEq::ne(&self[..], &other[..]) } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Default for String { #[inline] diff --git a/src/libcollections/vec_deque.rs b/src/libcollections/vec_deque.rs index 937ace00fdcca..67db0227cbee9 100644 --- a/src/libcollections/vec_deque.rs +++ b/src/libcollections/vec_deque.rs @@ -148,9 +148,9 @@ impl VecDeque { /// Copies a contiguous block of memory len long from src to dst #[inline] unsafe fn copy(&self, dst: usize, src: usize, len: usize) { - debug_assert!(dst + len <= self.cap(), "dst={} src={} len={} cap={}", dst, src, len, + debug_assert!(dst + len <= self.cap(), "cpy dst={} src={} len={} cap={}", dst, src, len, self.cap()); - debug_assert!(src + len <= self.cap(), "dst={} src={} len={} cap={}", dst, src, len, + debug_assert!(src + len <= self.cap(), "cpy dst={} src={} len={} cap={}", dst, src, len, self.cap()); ptr::copy( self.ptr().offset(src as isize), @@ -161,9 +161,9 @@ impl VecDeque { /// Copies a contiguous block of memory len long from src to dst #[inline] unsafe fn copy_nonoverlapping(&self, dst: usize, src: usize, len: usize) { - debug_assert!(dst + len <= self.cap(), "dst={} src={} len={} cap={}", dst, src, len, + debug_assert!(dst + len <= self.cap(), "cno dst={} src={} len={} cap={}", dst, src, len, self.cap()); - debug_assert!(src + len <= self.cap(), "dst={} src={} len={} cap={}", dst, src, len, + debug_assert!(src + len <= self.cap(), "cno dst={} src={} len={} cap={}", dst, src, len, self.cap()); ptr::copy_nonoverlapping( self.ptr().offset(src as isize), @@ -175,9 +175,11 @@ impl VecDeque { /// (abs(dst - src) + len) must be no larger than cap() (There must be at /// most one continuous overlapping region between src and dest). unsafe fn wrap_copy(&self, dst: usize, src: usize, len: usize) { - debug_assert!( - (if src <= dst { dst - src } else { src - dst }) + len <= self.cap(), - "dst={} src={} len={} cap={}", dst, src, len, self.cap()); + #[allow(dead_code)] + fn diff(a: usize, b: usize) -> usize {if a <= b {b - a} else {a - b}} + debug_assert!(cmp::min(diff(dst, src), + self.cap() - diff(dst, src)) + len <= self.cap(), + "wrc dst={} src={} len={} cap={}", dst, src, len, self.cap()); if src == dst || len == 0 { return } diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 262bfd5de992a..9b260b57099bb 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -1565,6 +1565,8 @@ pub trait Iterator { /// as soon as it finds a `false`, given that no matter what else happens, /// the result will also be `false`. /// + /// An empty iterator returns `true`. + /// /// # Examples /// /// Basic usage: @@ -1613,6 +1615,8 @@ pub trait Iterator { /// as soon as it finds a `true`, given that no matter what else happens, /// the result will also be `true`. /// + /// An empty iterator returns `false`. + /// /// # Examples /// /// Basic usage: @@ -2071,6 +2075,8 @@ pub trait Iterator { /// /// Takes each element, adds them together, and returns the result. /// + /// An empty iterator returns the zero value of the type. + /// /// # Examples /// /// Basic usage: @@ -2094,6 +2100,8 @@ pub trait Iterator { /// Iterates over the entire iterator, multiplying all the elements /// + /// An empty iterator returns the one value of the type. + /// /// # Examples /// /// ``` @@ -2557,11 +2565,11 @@ pub trait Extend { /// /// ``` /// // You can extend a String with some chars: - /// let mut message = String::from("The first three letters are: "); + /// let mut message = String::from("abc"); /// - /// message.extend(['a', 'b', 'c'].iter()); + /// message.extend(['d', 'e', 'f'].iter()); /// - /// assert_eq!("abc", &message[29..32]); + /// assert_eq!("abcdef", &message); /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn extend>(&mut self, iterable: T); diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 9f83cb9fddea4..5b931857decca 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -472,7 +472,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { let guard_exit = self.expr(&**guard, guard_start); let this_has_bindings = pat_util::pat_contains_bindings_or_wild( - &self.tcx.def_map, &**pat); + &self.tcx.def_map.borrow(), &**pat); // If both this pattern and the previous pattern // were free of bindings, they must consist only diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index fce96457d174b..f46e55e244140 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -702,7 +702,7 @@ fn is_useful(cx: &MatchCheckCtxt, Some(constructor) => { let matrix = rows.iter().filter_map(|r| { - if pat_is_binding_or_wild(&cx.tcx.def_map, raw_pat(r[0])) { + if pat_is_binding_or_wild(&cx.tcx.def_map.borrow(), raw_pat(r[0])) { Some(r[1..].to_vec()) } else { None @@ -1073,7 +1073,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, // check legality of moving out of the enum // x @ Foo(..) is legal, but x @ Foo(y) isn't. - if sub.map_or(false, |p| pat_contains_bindings(def_map, &*p)) { + if sub.map_or(false, |p| pat_contains_bindings(&def_map.borrow(), &*p)) { span_err!(cx.tcx.sess, p.span, E0007, "cannot bind by-move with sub-bindings"); } else if has_guard { span_err!(cx.tcx.sess, p.span, E0008, "cannot bind by-move into a pattern guard"); @@ -1086,7 +1086,7 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt, for pat in pats { front_util::walk_pat(&**pat, |p| { - if pat_is_binding(def_map, &*p) { + if pat_is_binding(&def_map.borrow(), &*p) { match p.node { hir::PatIdent(hir::BindByValue(_), _, ref sub) => { let pat_ty = tcx.node_id_to_type(p.id); @@ -1181,7 +1181,7 @@ struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> { impl<'a, 'b, 'tcx, 'v> Visitor<'v> for AtBindingPatternVisitor<'a, 'b, 'tcx> { fn visit_pat(&mut self, pat: &Pat) { - if !self.bindings_allowed && pat_is_binding(&self.cx.tcx.def_map, pat) { + if !self.bindings_allowed && pat_is_binding(&self.cx.tcx.def_map.borrow(), pat) { span_err!(self.cx.tcx.sess, pat.span, E0303, "pattern bindings are not allowed \ after an `@`"); diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs index 45671367a5c3c..dd49010c43672 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc/middle/check_static_recursion.rs @@ -237,7 +237,7 @@ impl<'a, 'ast: 'a> Visitor<'ast> for CheckItemRecursionVisitor<'a, 'ast> { fn visit_expr(&mut self, e: &'ast hir::Expr) { match e.node { hir::ExprPath(..) => { - match self.def_map.borrow().get(&e.id).map(|d| d.base_def) { + match self.def_map.get(&e.id).map(|d| d.base_def) { Some(DefStatic(def_id, _)) | Some(DefAssociatedConst(def_id)) | Some(DefConst(def_id)) => { diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1a1a9d4b1b48f..b4280f86c7d58 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -250,7 +250,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> { fn visit_arm(&mut self, arm: &hir::Arm) { if arm.pats.len() == 1 { let pat = &*arm.pats[0]; - let variants = pat_util::necessary_variants(&self.tcx.def_map, pat); + let variants = pat_util::necessary_variants(&self.tcx.def_map.borrow(), pat); // Inside the body, ignore constructions of variants // necessary for the pattern to match. Those construction sites @@ -270,7 +270,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> { hir::PatStruct(_, ref fields, _) => { self.handle_field_pattern_match(pat, fields); } - _ if pat_util::pat_is_const(def_map, pat) => { + _ if pat_util::pat_is_const(&def_map.borrow(), pat) => { // it might be the only use of a const self.lookup_and_handle_definition(&pat.id) } diff --git a/src/librustc/middle/def.rs b/src/librustc/middle/def.rs index ef2b918a9f5d7..b1d2418795753 100644 --- a/src/librustc/middle/def.rs +++ b/src/librustc/middle/def.rs @@ -17,8 +17,6 @@ use util::nodemap::NodeMap; use syntax::ast; use rustc_front::hir; -use std::cell::RefCell; - #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Def { DefFn(DefId, bool /* is_ctor */), @@ -103,7 +101,7 @@ impl PathResolution { } // Definition mapping -pub type DefMap = RefCell>; +pub type DefMap = NodeMap; // This is the replacement export map. It maps a module to all of the exports // within. pub type ExportMap = NodeMap>; diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6e469da33f98e..ce8d74bf191c7 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -934,7 +934,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { return_if_err!(self.mc.cat_pattern(cmt_discr, pat, |_mc, cmt_pat, pat| { let tcx = self.tcx(); let def_map = &self.tcx().def_map; - if pat_util::pat_is_binding(def_map, pat) { + if pat_util::pat_is_binding(&def_map.borrow(), pat) { match pat.node { hir::PatIdent(hir::BindByRef(_), _, _) => mode.lub(BorrowingMatch), @@ -969,7 +969,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { let def_map = &self.tcx().def_map; let delegate = &mut self.delegate; return_if_err!(mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| { - if pat_util::pat_is_binding(def_map, pat) { + if pat_util::pat_is_binding(&def_map.borrow(), pat) { let tcx = typer.tcx; debug!("binding cmt_pat={:?} pat={:?} match_mode={:?}", diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 3b56597d353a3..b25ad66fd1fa5 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1555,7 +1555,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Ignore unused self. let name = path1.node; if name != special_idents::self_.name { - self.warn_about_unused(sp, p_id, entry_ln, var); + if !self.warn_about_unused(sp, p_id, entry_ln, var) { + if self.live_on_entry(entry_ln, var).is_none() { + self.report_dead_assign(p_id, sp, var, true); + } + } } }) } @@ -1609,11 +1613,19 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { ln: LiveNode, var: Variable) { if self.live_on_exit(ln, var).is_none() { - let r = self.should_warn(var); - if let Some(name) = r { + self.report_dead_assign(id, sp, var, false); + } + } + + fn report_dead_assign(&self, id: NodeId, sp: Span, var: Variable, is_argument: bool) { + if let Some(name) = self.should_warn(var) { + if is_argument { + self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_ASSIGNMENTS, id, sp, + format!("value passed to `{}` is never read", name)); + } else { self.ir.tcx.sess.add_lint(lint::builtin::UNUSED_ASSIGNMENTS, id, sp, format!("value assigned to `{}` is never read", name)); } } } - } +} diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 00bd850ea6217..d8031e3134d5c 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1195,7 +1195,18 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { (*op)(self, cmt.clone(), pat); - let opt_def = self.tcx().def_map.borrow().get(&pat.id).map(|d| d.full_def()); + let opt_def = if let Some(path_res) = self.tcx().def_map.borrow().get(&pat.id) { + if path_res.depth != 0 { + // Since patterns can be associated constants + // which are resolved during typeck, we might have + // some unresolved patterns reaching this stage + // without aborting + return Err(()); + } + Some(path_res.full_def()) + } else { + None + }; // Note: This goes up here (rather than within the PatEnum arm // alone) because struct patterns can refer to struct types or diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs index c3555273850bc..09132396054ff 100644 --- a/src/librustc/middle/pat_util.rs +++ b/src/librustc/middle/pat_util.rs @@ -18,11 +18,13 @@ use rustc_front::hir; use rustc_front::util::walk_pat; use syntax::codemap::{respan, Span, Spanned, DUMMY_SP}; +use std::cell::RefCell; + pub type PatIdMap = FnvHashMap; // This is used because same-named variables in alternative patterns need to // use the NodeId of their namesake in the first pattern. -pub fn pat_id_map(dm: &DefMap, pat: &hir::Pat) -> PatIdMap { +pub fn pat_id_map(dm: &RefCell, pat: &hir::Pat) -> PatIdMap { let mut map = FnvHashMap(); pat_bindings(dm, pat, |_bm, p_id, _s, path1| { map.insert(path1.node, p_id); @@ -36,7 +38,7 @@ pub fn pat_is_refutable(dm: &DefMap, pat: &hir::Pat) -> bool { hir::PatEnum(_, _) | hir::PatIdent(_, _, None) | hir::PatStruct(..) => { - match dm.borrow().get(&pat.id).map(|d| d.full_def()) { + match dm.get(&pat.id).map(|d| d.full_def()) { Some(DefVariant(..)) => true, _ => false } @@ -51,7 +53,7 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &hir::Pat) -> bool { hir::PatEnum(_, _) | hir::PatIdent(_, _, None) | hir::PatStruct(..) => { - match dm.borrow().get(&pat.id).map(|d| d.full_def()) { + match dm.get(&pat.id).map(|d| d.full_def()) { Some(DefVariant(..)) | Some(DefStruct(..)) => true, _ => false } @@ -63,7 +65,7 @@ pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &hir::Pat) -> bool { pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool { match pat.node { hir::PatIdent(_, _, None) | hir::PatEnum(..) | hir::PatQPath(..) => { - match dm.borrow().get(&pat.id).map(|d| d.full_def()) { + match dm.get(&pat.id).map(|d| d.full_def()) { Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true, _ => false } @@ -77,7 +79,7 @@ pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool { pub fn pat_is_resolved_const(dm: &DefMap, pat: &hir::Pat) -> bool { match pat.node { hir::PatIdent(_, _, None) | hir::PatEnum(..) | hir::PatQPath(..) => { - match dm.borrow().get(&pat.id) + match dm.get(&pat.id) .and_then(|d| if d.depth == 0 { Some(d.base_def) } else { None } ) { Some(DefConst(..)) | Some(DefAssociatedConst(..)) => true, @@ -108,12 +110,12 @@ pub fn pat_is_binding_or_wild(dm: &DefMap, pat: &hir::Pat) -> bool { /// Call `it` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` -pub fn pat_bindings(dm: &DefMap, pat: &hir::Pat, mut it: I) where +pub fn pat_bindings(dm: &RefCell, pat: &hir::Pat, mut it: I) where I: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned), { walk_pat(pat, |p| { match p.node { - hir::PatIdent(binding_mode, ref pth, _) if pat_is_binding(dm, p) => { + hir::PatIdent(binding_mode, ref pth, _) if pat_is_binding(&dm.borrow(), p) => { it(binding_mode, p.id, p.span, &respan(pth.span, pth.node.name)); } _ => {} @@ -122,12 +124,12 @@ pub fn pat_bindings(dm: &DefMap, pat: &hir::Pat, mut it: I) where }); } -pub fn pat_bindings_hygienic(dm: &DefMap, pat: &hir::Pat, mut it: I) where +pub fn pat_bindings_hygienic(dm: &RefCell, pat: &hir::Pat, mut it: I) where I: FnMut(hir::BindingMode, ast::NodeId, Span, &Spanned), { walk_pat(pat, |p| { match p.node { - hir::PatIdent(binding_mode, ref pth, _) if pat_is_binding(dm, p) => { + hir::PatIdent(binding_mode, ref pth, _) if pat_is_binding(&dm.borrow(), p) => { it(binding_mode, p.id, p.span, &respan(pth.span, pth.node)); } _ => {} @@ -153,7 +155,7 @@ pub fn pat_contains_bindings(dm: &DefMap, pat: &hir::Pat) -> bool { /// Checks if the pattern contains any `ref` or `ref mut` bindings, /// and if yes wether its containing mutable ones or just immutables ones. -pub fn pat_contains_ref_binding(dm: &DefMap, pat: &hir::Pat) -> Option { +pub fn pat_contains_ref_binding(dm: &RefCell, pat: &hir::Pat) -> Option { let mut result = None; pat_bindings(dm, pat, |mode, _, _, _| { match mode { @@ -172,7 +174,7 @@ pub fn pat_contains_ref_binding(dm: &DefMap, pat: &hir::Pat) -> Option Option { +pub fn arm_contains_ref_binding(dm: &RefCell, arm: &hir::Arm) -> Option { arm.pats.iter() .filter_map(|pat| pat_contains_ref_binding(dm, pat)) .max_by(|m| match *m { @@ -226,7 +228,7 @@ pub fn necessary_variants(dm: &DefMap, pat: &hir::Pat) -> Vec { hir::PatEnum(_, _) | hir::PatIdent(_, _, None) | hir::PatStruct(..) => { - match dm.borrow().get(&p.id) { + match dm.get(&p.id) { Some(&PathResolution { base_def: DefVariant(_, id, _), .. }) => { variants.push(id); } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index d9398a1c58cd9..fa0c6c41ce5cc 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -205,7 +205,7 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { hir::TyPath(None, ref path) => { // if this path references a trait, then this will resolve to // a trait ref, which introduces a binding scope. - match self.def_map.borrow().get(&ty.id).map(|d| (d.base_def, d.depth)) { + match self.def_map.get(&ty.id).map(|d| (d.base_def, d.depth)) { Some((def::DefTrait(..), 0)) => { self.with(LateScope(&Vec::new(), self.scope), |_, this| { this.visit_path(path, ty.id); diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 830232cf373b8..d91cac4cc75b9 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -121,6 +121,13 @@ pub struct Tables<'tcx> { /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. pub closure_kinds: DefIdMap, + + /// For each fn, records the "liberated" types of its arguments + /// and return type. Liberated means that all bound regions + /// (including late-bound regions) are replaced with free + /// equivalents. This table is not used in trans (since regions + /// are erased there) and hence is not serialized to metadata. + pub liberated_fn_sigs: NodeMap>, } impl<'tcx> Tables<'tcx> { @@ -133,6 +140,7 @@ impl<'tcx> Tables<'tcx> { upvar_capture_map: FnvHashMap(), closure_tys: DefIdMap(), closure_kinds: DefIdMap(), + liberated_fn_sigs: NodeMap(), } } @@ -220,7 +228,7 @@ pub struct ctxt<'tcx> { pub types: CommonTypes<'tcx>, pub sess: &'tcx Session, - pub def_map: DefMap, + pub def_map: RefCell, pub named_region_map: resolve_lifetime::NamedRegionMap, @@ -445,10 +453,10 @@ impl<'tcx> ctxt<'tcx> { /// reference to the context, to allow formatting values that need it. pub fn create_and_enter(s: &'tcx Session, arenas: &'tcx CtxtArenas<'tcx>, - def_map: DefMap, + def_map: RefCell, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map<'tcx>, - freevars: RefCell, + freevars: FreevarMap, region_maps: RegionMaps, lang_items: middle::lang_items::LanguageItems, stability: stability::Index<'tcx>, @@ -481,7 +489,7 @@ impl<'tcx> ctxt<'tcx> { super_predicates: RefCell::new(DefIdMap()), fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()), map: map, - freevars: freevars, + freevars: RefCell::new(freevars), tcache: RefCell::new(DefIdMap()), rcache: RefCell::new(FnvHashMap()), tc_cache: RefCell::new(FnvHashMap()), diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 573634414932b..aa18974470178 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -1731,6 +1731,13 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> { self.fields.iter().find(|f| f.name == name) } + #[inline] + pub fn index_of_field_named(&self, + name: ast::Name) + -> Option { + self.fields.iter().position(|f| f.name == name) + } + #[inline] pub fn field_named(&self, name: ast::Name) -> &FieldDefData<'tcx, 'container> { self.find_field_named(name).unwrap() diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 69c4dcb0a197c..2237e19c8e7da 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -11,6 +11,7 @@ use rustc::front; use rustc::front::map as hir_map; use rustc_mir as mir; +use rustc_mir::mir_map::MirMap; use rustc::session::Session; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; @@ -22,6 +23,7 @@ use rustc::middle::dependency_format; use rustc::middle; use rustc::plugin::registry::Registry; use rustc::plugin; +use rustc::util::nodemap::NodeMap; use rustc::util::common::time; use rustc_borrowck as borrowck; use rustc_resolve as resolve; @@ -146,7 +148,7 @@ pub fn compile_input(sess: Session, &arenas, &id, control.make_glob_map, - |tcx, analysis| { + |tcx, mir_map, analysis| { { let state = CompileState::state_after_analysis(input, @@ -170,7 +172,7 @@ pub fn compile_input(sess: Session, println!("Pre-trans"); tcx.print_debug_stats(); } - let trans = phase_4_translate_to_llvm(tcx, analysis); + let trans = phase_4_translate_to_llvm(tcx, &mir_map, analysis); if log_enabled!(::log::INFO) { println!("Post-trans"); @@ -670,6 +672,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, f: F) -> R where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, + MirMap<'tcx>, ty::CrateAnalysis) -> R { let time_passes = sess.time_passes(); @@ -697,8 +700,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, syntax::ext::mtwt::clear_tables(); } - let named_region_map = time(time_passes, "lifetime resolution", - || middle::resolve_lifetime::krate(sess, krate, &def_map)); + let named_region_map = time(time_passes, "lifetime resolution", || + middle::resolve_lifetime::krate(sess, krate, &def_map.borrow())); time(time_passes, "looking for entry point", || middle::entry::find_entry_point(sess, &ast_map)); @@ -715,7 +718,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, middle::check_loop::check_crate(sess, krate)); time(time_passes, "static item recursion checking", || - middle::check_static_recursion::check_crate(sess, krate, &def_map, &ast_map)); + middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &ast_map)); ty::ctxt::create_and_enter(sess, arenas, @@ -751,18 +754,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "match checking", || middle::check_match::check_crate(tcx)); - match tcx.sess.opts.unstable_features { + let mir_map = match tcx.sess.opts.unstable_features { UnstableFeatures::Disallow => { // use this as a shorthand for beta/stable, and skip // MIR construction there until known regressions are // addressed + NodeMap() } UnstableFeatures::Allow | UnstableFeatures::Cheat => { - let _mir_map = - time(time_passes, "MIR dump", || - mir::mir_map::build_mir_for_crate(tcx)); + time(time_passes, "MIR dump", || + mir::mir_map::build_mir_for_crate(tcx)) } - } + }; time(time_passes, "liveness checking", || middle::liveness::check_crate(tcx)); @@ -804,7 +807,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // The above three passes generate errors w/o aborting tcx.sess.abort_if_errors(); - f(tcx, ty::CrateAnalysis { + f(tcx, mir_map, ty::CrateAnalysis { export_map: export_map, exported_items: exported_items, public_items: public_items, @@ -817,8 +820,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) - -> trans::CrateTranslation { +pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>, + mir_map: &MirMap<'tcx>, + analysis: ty::CrateAnalysis) + -> trans::CrateTranslation { let time_passes = tcx.sess.time_passes(); time(time_passes, "resolving dependency formats", || @@ -826,7 +831,7 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) // Option dance to work around the lack of stack once closures. time(time_passes, "translation", move || - trans::trans_crate(tcx, analysis)) + trans::trans_crate(tcx, mir_map, analysis)) } /// Run LLVM itself, producing a bitcode file, assembly file or object file diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index a30c437197c3b..f53822d2400b4 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -182,7 +182,7 @@ impl PpSourceMode { arenas, id, resolve::MakeGlobMap::No, - |tcx, _| { + |tcx, _, _| { let annotation = TypedAnnotation { tcx: tcx }; f(&annotation, payload, &ast_map.forest.krate) }) @@ -782,7 +782,7 @@ pub fn pretty_print_input(sess: Session, &arenas, &id, resolve::MakeGlobMap::No, - |tcx, _| { + |tcx, _, _| { print_flowgraph(variants, tcx, code, mode, out) }) } diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 4bbc22ef1a273..5cd6bfa879033 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -134,7 +134,7 @@ fn test_env(source_string: &str, let lang_items = lang_items::collect_language_items(&sess, &ast_map); let resolve::CrateMap { def_map, freevars, .. } = resolve::resolve_crate(&sess, &ast_map, resolve::MakeGlobMap::No); - let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map); + let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map.borrow()); let region_map = region::resolve_crate(&sess, krate); ty::ctxt::create_and_enter(&sess, &arenas, diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 3cfc51ec3c98a..23ca22129fdc0 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // schedule a shallow free of that memory, lest we unwind: let extent = this.extent_of_innermost_scope().unwrap(); - this.schedule_drop(expr_span, extent, DropKind::Shallow, &result, value_ty); + this.schedule_drop(expr_span, extent, DropKind::Free, &result, value_ty); // initialize the box contents: let contents = result.clone().deref(); @@ -149,16 +149,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) } ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above - // first process the set of fields + // first process the set of fields that were provided + // (evaluating them in order given by user) let fields_map: FnvHashMap<_, _> = fields.into_iter() .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) .collect(); - let field_names = this.hir.fields(adt_def, variant_index); - + // if base expression is given, evaluate it now let base = base.map(|base| unpack!(block = this.as_lvalue(block, base))); + // get list of all fields that we will need + let field_names = this.hir.all_fields(adt_def, variant_index); + // for the actual values we use, take either the // expr the user specified or, if they didn't // specify something for this field name, create a diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a7d68b09b5459..57c6db79c5271 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -211,10 +211,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.cfg.start_new_block().unit() } ExprKind::Call { fun, args } => { - let fun = unpack!(block = this.as_lvalue(block, fun)); + let fun = unpack!(block = this.as_operand(block, fun)); let args: Vec<_> = args.into_iter() - .map(|arg| unpack!(block = this.as_lvalue(block, arg))) + .map(|arg| unpack!(block = this.as_operand(block, arg))) .collect(); let success = this.cfg.start_new_block(); let panic = this.diverge_cleanup(); diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index d5745eb28c7a8..e035f53dacf41 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -92,6 +92,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .collect(); self.cfg.terminate(block, Terminator::Switch { discr: lvalue.clone(), + adt_def: adt_def, targets: target_blocks.clone() }); target_blocks @@ -99,27 +100,28 @@ impl<'a,'tcx> Builder<'a,'tcx> { TestKind::Eq { value, ty } => { // call PartialEq::eq(discrim, constant) - let constant = self.push_literal(block, test.span, ty.clone(), value); + let constant = self.literal_operand(test.span, ty.clone(), value); let item_ref = self.hir.partial_eq(ty); - self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant) + self.call_comparison_fn(block, test.span, item_ref, + Operand::Consume(lvalue.clone()), constant) } TestKind::Range { lo, hi, ty } => { // Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`. - let lo = self.push_literal(block, test.span, ty.clone(), lo); - let hi = self.push_literal(block, test.span, ty.clone(), hi); + let lo = self.literal_operand(test.span, ty.clone(), lo); + let hi = self.literal_operand(test.span, ty.clone(), hi); let item_ref = self.hir.partial_le(ty); let lo_blocks = self.call_comparison_fn(block, test.span, item_ref.clone(), lo, - lvalue.clone()); + Operand::Consume(lvalue.clone())); let hi_blocks = self.call_comparison_fn(lo_blocks[0], test.span, item_ref, - lvalue.clone(), + Operand::Consume(lvalue.clone()), hi); let failure = self.cfg.start_new_block(); @@ -164,14 +166,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { block: BasicBlock, span: Span, item_ref: ItemRef<'tcx>, - lvalue1: Lvalue<'tcx>, - lvalue2: Lvalue<'tcx>) + lvalue1: Operand<'tcx>, + lvalue2: Operand<'tcx>) -> Vec { let target_blocks = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty); - let func = self.push_item_ref(block, span, item_ref); + let func = self.item_ref_operand(span, item_ref); let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()]; self.cfg.terminate(block, Terminator::Call { diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 86b6df19b77e7..41274f3f3736e 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -34,20 +34,17 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue } - pub fn push_literal(&mut self, - block: BasicBlock, - span: Span, - ty: Ty<'tcx>, - literal: Literal<'tcx>) - -> Lvalue<'tcx> { - let temp = self.temp(ty.clone()); + pub fn literal_operand(&mut self, + span: Span, + ty: Ty<'tcx>, + literal: Literal<'tcx>) + -> Operand<'tcx> { let constant = Constant { span: span, ty: ty, literal: literal, }; - self.cfg.push_assign_constant(block, span, &temp, constant); - temp + Operand::Constant(constant) } pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: usize) -> Lvalue<'tcx> { @@ -63,15 +60,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { temp } - pub fn push_item_ref(&mut self, - block: BasicBlock, - span: Span, - item_ref: ItemRef<'tcx>) - -> Lvalue<'tcx> { + pub fn item_ref_operand(&mut self, + span: Span, + item_ref: ItemRef<'tcx>) + -> Operand<'tcx> { let literal = Literal::Item { def_id: item_ref.def_id, substs: item_ref.substs, }; - self.push_literal(block, span, item_ref.ty, literal) + self.literal_operand(span, item_ref.ty, literal) } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 857540e2109c0..56a20167b7942 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -9,14 +9,14 @@ // except according to those terms. use hair; +use hair::cx::{Cx, PatNode}; use rustc::middle::region::CodeExtent; -use rustc::middle::ty::Ty; +use rustc::middle::ty::{FnOutput, Ty}; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir; use repr::*; use syntax::ast; use syntax::codemap::Span; -use tcx::{Cx, PatNode}; struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, @@ -75,13 +75,14 @@ macro_rules! unpack { /////////////////////////////////////////////////////////////////////////// // construct() -- the main entry point for building MIR for a function -pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>, - _span: Span, - implicit_arguments: Vec>, - explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>, - argument_extent: CodeExtent, - ast_block: &'tcx hir::Block) - -> Mir<'tcx> { +pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>, + _span: Span, + implicit_arguments: Vec>, + explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>, + argument_extent: CodeExtent, + return_ty: FnOutput<'tcx>, + ast_block: &'tcx hir::Block) + -> Mir<'tcx> { let cfg = CFG { basic_blocks: vec![] }; // it's handy to have a temporary of type `()` sometimes, so make @@ -121,6 +122,7 @@ pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>, var_decls: builder.var_decls, arg_decls: arg_decls, temp_decls: builder.temp_decls, + return_ty: return_ty, } } diff --git a/src/librustc_mir/tcx/block.rs b/src/librustc_mir/hair/cx/block.rs similarity index 82% rename from src/librustc_mir/tcx/block.rs rename to src/librustc_mir/hair/cx/block.rs index dc168bc7c2b6c..a407c42372a81 100644 --- a/src/librustc_mir/tcx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -9,10 +9,9 @@ // except according to those terms. use hair::*; - -use tcx::Cx; -use tcx::pattern::PatNode; -use tcx::to_ref::ToRef; +use hair::cx::Cx; +use hair::cx::pattern::PatNode; +use hair::cx::to_ref::ToRef; use rustc::middle::region::{BlockRemainder, CodeExtentData}; use rustc_front::hir; use syntax::ast; @@ -34,22 +33,11 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { } } -impl<'tcx> Mirror<'tcx> for &'tcx hir::Stmt { - type Output = Stmt<'tcx>; - - fn make_mirror<'a>(self, _cx: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> { - // In order to get the scoping correct, we eagerly mirror - // statements when we translate the enclosing block, so we - // should in fact never get to this point. - panic!("statements are eagerly mirrored"); - } -} - -fn mirror_stmts<'a, 'tcx: 'a, STMTS>(cx: &mut Cx<'a, 'tcx>, - block_id: ast::NodeId, - mut stmts: STMTS) - -> Vec> - where STMTS: Iterator)> +fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>, + block_id: ast::NodeId, + mut stmts: STMTS) + -> Vec> + where STMTS: Iterator)> { let mut result = vec![]; while let Some((index, stmt)) = stmts.next() { diff --git a/src/librustc_mir/tcx/expr.rs b/src/librustc_mir/hair/cx/expr.rs similarity index 94% rename from src/librustc_mir/tcx/expr.rs rename to src/librustc_mir/hair/cx/expr.rs index 9f7ecf522876f..847d76f7d17a9 100644 --- a/src/librustc_mir/tcx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -12,16 +12,16 @@ use hair::*; use repr::*; use rustc_data_structures::fnv::FnvHashMap; use std::rc::Rc; -use tcx::Cx; -use tcx::block; -use tcx::pattern::PatNode; -use tcx::to_ref::ToRef; +use hair::cx::Cx; +use hair::cx::block; +use hair::cx::pattern::PatNode; +use hair::cx::to_ref::ToRef; use rustc::front::map; use rustc::middle::const_eval; use rustc::middle::def; use rustc::middle::region::CodeExtent; use rustc::middle::pat_util; -use rustc::middle::ty::{self, Ty}; +use rustc::middle::ty::{self, VariantDef, Ty}; use rustc_front::hir; use rustc_front::util as hir_util; use syntax::parse::token; @@ -170,11 +170,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { hir::ExprStruct(_, ref fields, ref base) => { match expr_ty.sty { ty::TyStruct(adt, substs) => { + let field_refs = field_refs(&adt.variants[0], fields); ExprKind::Adt { adt_def: adt, variant_index: 0, substs: substs, - fields: fields.to_ref(), + fields: field_refs, base: base.to_ref(), } } @@ -183,11 +184,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { def::DefVariant(enum_id, variant_id, true) => { debug_assert!(adt.did == enum_id); let index = adt.variant_index_with_id(variant_id); + let field_refs = field_refs(&adt.variants[index], fields); ExprKind::Adt { adt_def: adt, variant_index: index, substs: substs, - fields: fields.to_ref(), + fields: field_refs, base: base.to_ref(), } } @@ -238,11 +240,10 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } }; - let field_expr_ref = |s: &'tcx P, nm: &str| { - FieldExprRef { - name: Field::Named(token::intern(nm)), - expr: s.to_ref(), - } + let field_expr_ref = |s: &'tcx P, name: &str| { + let name = token::intern(name); + let index = adt_def.variants[0].index_of_field_named(name).unwrap(); + FieldExprRef { name: Field::new(index), expr: s.to_ref() } }; let start_field = start.as_ref() @@ -293,12 +294,25 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { hir::ExprLoop(ref body, _) => ExprKind::Loop { condition: None, body: block::to_expr_ref(cx, body) }, - hir::ExprField(ref source, name) => - ExprKind::Field { lhs: source.to_ref(), - name: Field::Named(name.node) }, + hir::ExprField(ref source, name) => { + let index = match cx.tcx.expr_ty_adjusted(source).sty { + ty::TyStruct(adt_def, _) => + adt_def.variants[0].index_of_field_named(name.node), + ref ty => + cx.tcx.sess.span_bug( + self.span, + &format!("field of non-struct: {:?}", ty)), + }; + let index = index.unwrap_or_else(|| { + cx.tcx.sess.span_bug( + self.span, + &format!("no index found for field `{}`", name.node)); + }); + ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) } + } hir::ExprTupField(ref source, index) => ExprKind::Field { lhs: source.to_ref(), - name: Field::Indexed(index.node) }, + name: Field::new(index.node as usize) }, hir::ExprCast(ref source, _) => ExprKind::Cast { source: source.to_ref() }, hir::ExprBox(ref value) => @@ -616,7 +630,7 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, // at this point we have `self.n`, which loads up the upvar let field_kind = ExprKind::Field { lhs: self_expr.to_ref(), - name: Field::Indexed(index), + name: Field::new(index), }; // ...but the upvar might be an `&T` or `&mut T` capture, at which @@ -814,3 +828,15 @@ fn loop_label<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Cod } } } + +fn field_refs<'tcx>(variant: VariantDef<'tcx>, + fields: &'tcx [hir::Field]) + -> Vec> +{ + fields.iter() + .map(|field| FieldExprRef { + name: Field::new(variant.index_of_field_named(field.name.node).unwrap()), + expr: field.expr.to_ref(), + }) + .collect() +} diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs new file mode 100644 index 0000000000000..8d4b05afcb6e6 --- /dev/null +++ b/src/librustc_mir/hair/cx/mod.rs @@ -0,0 +1,149 @@ +// Copyright 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. + +/*! + * This module contains the code to convert from the wacky tcx data + * structures into the hair. The `builder` is generally ignorant of + * the tcx etc, and instead goes through the `Cx` for most of its + * work. + */ + +use hair::*; +use repr::*; + +use rustc::middle::const_eval::ConstVal; +use rustc::middle::def_id::DefId; +use rustc::middle::infer::InferCtxt; +use rustc::middle::subst::{Subst, Substs}; +use rustc::middle::ty::{self, Ty}; +use syntax::codemap::Span; +use syntax::parse::token; + +#[derive(Copy, Clone)] +pub struct Cx<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a,'tcx> Cx<'a,'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> { + Cx { + tcx: infcx.tcx, + infcx: infcx, + } + } +} + +pub use self::pattern::PatNode; + +impl<'a,'tcx:'a> Cx<'a, 'tcx> { + /// Normalizes `ast` into the appropriate `mirror` type. + pub fn mirror>(&mut self, ast: M) -> M::Output { + ast.make_mirror(self) + } + + pub fn unit_ty(&mut self) -> Ty<'tcx> { + self.tcx.mk_nil() + } + + pub fn usize_ty(&mut self) -> Ty<'tcx> { + self.tcx.types.usize + } + + pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> { + Literal::Value { value: ConstVal::Uint(value as u64) } + } + + pub fn bool_ty(&mut self) -> Ty<'tcx> { + self.tcx.types.bool + } + + pub fn true_literal(&mut self) -> Literal<'tcx> { + Literal::Value { value: ConstVal::Bool(true) } + } + + pub fn false_literal(&mut self) -> Literal<'tcx> { + Literal::Value { value: ConstVal::Bool(false) } + } + + pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { + let eq_def_id = self.tcx.lang_items.eq_trait().unwrap(); + self.cmp_method_ref(eq_def_id, "eq", ty) + } + + pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { + let ord_def_id = self.tcx.lang_items.ord_trait().unwrap(); + self.cmp_method_ref(ord_def_id, "le", ty) + } + + pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize { + adt_def.variants.len() + } + + pub fn all_fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec { + (0..adt_def.variants[variant_index].fields.len()) + .map(Field::new) + .collect() + } + + pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool { + if self.infcx.type_moves_by_default(ty, span) { + // FIXME(#21859) we should do an add'l check here to determine if + // any dtor will execute, but the relevant fn + // (`type_needs_drop`) is currently factored into + // `librustc_trans`, so we can't easily do so. + true + } else { + // if type implements Copy, cannot require drop + false + } + } + + pub fn span_bug(&mut self, span: Span, message: &str) -> ! { + self.tcx.sess.span_bug(span, message) + } + + pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { + self.tcx + } + + fn cmp_method_ref(&mut self, + trait_def_id: DefId, + method_name: &str, + arg_ty: Ty<'tcx>) + -> ItemRef<'tcx> { + let method_name = token::intern(method_name); + let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty); + for trait_item in self.tcx.trait_items(trait_def_id).iter() { + match *trait_item { + ty::ImplOrTraitItem::MethodTraitItem(ref method) => { + if method.name == method_name { + let method_ty = self.tcx.lookup_item_type(method.def_id); + let method_ty = method_ty.ty.subst(self.tcx, &substs); + return ItemRef { + ty: method_ty, + def_id: method.def_id, + substs: self.tcx.mk_substs(substs), + }; + } + } + ty::ImplOrTraitItem::ConstTraitItem(..) | + ty::ImplOrTraitItem::TypeTraitItem(..) => {} + } + } + + self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id)); + } +} + +mod block; +mod expr; +mod pattern; +mod to_ref; diff --git a/src/librustc_mir/tcx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs similarity index 88% rename from src/librustc_mir/tcx/pattern.rs rename to src/librustc_mir/hair/cx/pattern.rs index db4346364968c..fa96780417905 100644 --- a/src/librustc_mir/tcx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -9,12 +9,11 @@ // except according to those terms. use hair::*; +use hair::cx::Cx; +use hair::cx::to_ref::ToRef; use repr::*; - use rustc_data_structures::fnv::FnvHashMap; use std::rc::Rc; -use tcx::Cx; -use tcx::to_ref::ToRef; use rustc::middle::const_eval; use rustc::middle::def; use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding}; @@ -156,7 +155,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { }, hir::PatEnum(..) | hir::PatIdent(..) | hir::PatQPath(..) - if pat_is_resolved_const(&cx.tcx.def_map, self.pat) => + if pat_is_resolved_const(&cx.tcx.def_map.borrow(), self.pat) => { let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def(); match def { @@ -223,7 +222,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { subpatterns.iter() .enumerate() .map(|(i, subpattern)| FieldPatternRef { - field: Field::Indexed(i), + field: Field::new(i), pattern: self.pat_ref(subpattern), }) .collect(); @@ -232,7 +231,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { } hir::PatIdent(bm, ref ident, ref sub) - if pat_is_binding(&cx.tcx.def_map, self.pat) => + if pat_is_binding(&cx.tcx.def_map.borrow(), self.pat) => { let id = match self.binding_map { None => self.pat.id, @@ -273,7 +272,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { .flat_map(|v| v.iter()) .enumerate() .map(|(i, field)| FieldPatternRef { - field: Field::Indexed(i), + field: Field::new(i), pattern: self.pat_ref(field), }) .collect(); @@ -281,13 +280,35 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { } hir::PatStruct(_, ref fields, _) => { + let pat_ty = cx.tcx.node_id_to_type(self.pat.id); + let adt_def = match pat_ty.sty { + ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def, + _ => { + cx.tcx.sess.span_bug( + self.pat.span, + "struct pattern not applied to struct or enum"); + } + }; + + let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def(); + let variant_def = adt_def.variant_of_def(def); + let subpatterns = fields.iter() - .map(|field| FieldPatternRef { - field: Field::Named(field.node.name), - pattern: self.pat_ref(&field.node.pat), + .map(|field| { + let index = variant_def.index_of_field_named(field.node.name); + let index = index.unwrap_or_else(|| { + cx.tcx.sess.span_bug( + self.pat.span, + &format!("no field with name {:?}", field.node.name)); + }); + FieldPatternRef { + field: Field::new(index), + pattern: self.pat_ref(&field.node.pat), + } }) .collect(); + self.variant_or_leaf(cx, subpatterns) } diff --git a/src/librustc_mir/tcx/to_ref.rs b/src/librustc_mir/hair/cx/to_ref.rs similarity index 85% rename from src/librustc_mir/tcx/to_ref.rs rename to src/librustc_mir/hair/cx/to_ref.rs index 13ca82e3e4c71..e0b8abfbd9ce3 100644 --- a/src/librustc_mir/tcx/to_ref.rs +++ b/src/librustc_mir/hair/cx/to_ref.rs @@ -9,9 +9,8 @@ // except according to those terms. use hair::*; -use repr::*; -use tcx::pattern::PatNode; +use hair::cx::pattern::PatNode; use rustc_front::hir; use syntax::ptr::P; @@ -79,14 +78,3 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec self.iter().map(|expr| expr.to_ref()).collect() } } - -impl<'a,'tcx:'a> ToRef for &'tcx hir::Field { - type Output = FieldExprRef<'tcx>; - - fn to_ref(self) -> FieldExprRef<'tcx> { - FieldExprRef { - name: Field::Named(self.name.node), - expr: self.expr.to_ref(), - } - } -} diff --git a/src/librustc_mir/hair.rs b/src/librustc_mir/hair/mod.rs similarity index 98% rename from src/librustc_mir/hair.rs rename to src/librustc_mir/hair/mod.rs index 641cbae4be271..becaa19974d8e 100644 --- a/src/librustc_mir/hair.rs +++ b/src/librustc_mir/hair/mod.rs @@ -22,7 +22,9 @@ use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; use rustc_front::hir; use syntax::ast; use syntax::codemap::Span; -use tcx::{Cx, PatNode}; +use self::cx::{Cx, PatNode}; + +pub mod cx; #[derive(Clone, Debug)] pub struct ItemRef<'tcx> { @@ -41,7 +43,6 @@ pub struct Block<'tcx> { #[derive(Clone, Debug)] pub enum StmtRef<'tcx> { - Hair(&'tcx hir::Stmt), Mirror(Box>), } @@ -392,9 +393,8 @@ impl<'tcx> Mirror<'tcx> for Stmt<'tcx> { impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> { type Output = Stmt<'tcx>; - fn make_mirror<'a>(self, hir: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> { + fn make_mirror<'a>(self, _: &mut Cx<'a,'tcx>) -> Stmt<'tcx> { match self { - StmtRef::Hair(h) => h.make_mirror(hir), StmtRef::Mirror(m) => *m, } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 934dd660177b8..ed5df21d91193 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -31,7 +31,9 @@ extern crate syntax; pub mod build; pub mod mir_map; -pub mod hair; +mod hair; pub mod repr; mod graphviz; -mod tcx; +pub mod tcx; +pub mod visit; + diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 1acbc8d733d5c..ebcb1db1151ee 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -23,8 +23,8 @@ extern crate rustc_front; use build; use dot; use repr::Mir; +use hair::cx::{PatNode, Cx}; use std::fs::File; -use tcx::{PatNode, Cx}; use self::rustc::middle::infer; use self::rustc::middle::region::CodeExtentData; @@ -189,26 +189,42 @@ impl<'a, 'm, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'m,'tcx> { } } -fn build_mir<'a, 'tcx: 'a>(cx: Cx<'a, 'tcx>, - implicit_arg_tys: Vec>, - fn_id: ast::NodeId, - span: Span, - decl: &'tcx hir::FnDecl, - body: &'tcx hir::Block) - -> Result, ErrorReported> { - let arguments = decl.inputs - .iter() - .map(|arg| { - let ty = cx.tcx().node_id_to_type(arg.id); - (ty, PatNode::irrefutable(&arg.pat)) - }) - .collect(); - - let parameter_scope = cx.tcx().region_maps.lookup_code_extent(CodeExtentData::ParameterScope { - fn_id: fn_id, - body_id: body.id, - }); - Ok(build::construct(cx, span, implicit_arg_tys, arguments, parameter_scope, body)) +fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, + implicit_arg_tys: Vec>, + fn_id: ast::NodeId, + span: Span, + decl: &'tcx hir::FnDecl, + body: &'tcx hir::Block) + -> Result, ErrorReported> { + // fetch the fully liberated fn signature (that is, all bound + // types/lifetimes replaced) + let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) { + Some(f) => f.clone(), + None => { + cx.tcx().sess.span_bug(span, + &format!("no liberated fn sig for {:?}", fn_id)); + } + }; + + let arguments = + decl.inputs + .iter() + .enumerate() + .map(|(index, arg)| { + (fn_sig.inputs[index], PatNode::irrefutable(&arg.pat)) + }) + .collect(); + + let parameter_scope = + cx.tcx().region_maps.lookup_code_extent( + CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); + Ok(build::construct(cx, + span, + implicit_arg_tys, + arguments, + parameter_scope, + fn_sig.output, + body)) } fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>, diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index 5059955c5dc33..89b1afa872381 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -12,7 +12,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::middle::subst::Substs; -use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; +use rustc::middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty}; use rustc_back::slice; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir::InlineAsm; @@ -25,6 +25,8 @@ use std::u32; pub struct Mir<'tcx> { pub basic_blocks: Vec>, + pub return_ty: FnOutput<'tcx>, + // for every node id pub extents: FnvHashMap>, @@ -245,6 +247,7 @@ pub enum Terminator<'tcx> { /// lvalue evaluates to some enum; jump depending on the branch Switch { discr: Lvalue<'tcx>, + adt_def: AdtDef<'tcx>, targets: Vec, }, @@ -277,7 +280,7 @@ impl<'tcx> Terminator<'tcx> { Goto { target: ref b } => slice::ref_slice(b), Panic { target: ref b } => slice::ref_slice(b), If { cond: _, targets: ref b } => b, - Switch { discr: _, targets: ref b } => b, + Switch { discr: _, adt_def: _, targets: ref b } => b, Diverge => &[], Return => &[], Call { data: _, targets: ref b } => b, @@ -291,10 +294,10 @@ pub struct CallData<'tcx> { pub destination: Lvalue<'tcx>, /// the fn being called - pub func: Lvalue<'tcx>, + pub func: Operand<'tcx>, /// the arguments - pub args: Vec>, + pub args: Vec>, } impl<'tcx> BasicBlockData<'tcx> { @@ -316,7 +319,7 @@ impl<'tcx> Debug for Terminator<'tcx> { write!(fmt, "panic -> {:?}", target), If { cond: ref lv, ref targets } => write!(fmt, "if({:?}) -> {:?}", lv, targets), - Switch { discr: ref lv, ref targets } => + Switch { discr: ref lv, adt_def: _, ref targets } => write!(fmt, "switch({:?}) -> {:?}", lv, targets), Diverge => write!(fmt, "diverge"), @@ -353,8 +356,8 @@ pub enum StatementKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DropKind { - Shallow, - Deep, + Free, // free a partially constructed box, should go away eventually + Deep } impl<'tcx> Debug for Statement<'tcx> { @@ -362,7 +365,7 @@ impl<'tcx> Debug for Statement<'tcx> { use self::StatementKind::*; match self.kind { Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), - Drop(DropKind::Shallow, ref lv) => write!(fmt, "shallow_drop {:?}", lv), + Drop(DropKind::Free, ref lv) => write!(fmt, "free {:?}", lv), Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv), } } @@ -441,10 +444,19 @@ pub type LvalueProjection<'tcx> = pub type LvalueElem<'tcx> = ProjectionElem<'tcx,Operand<'tcx>>; +/// Index into the list of fields found in a `VariantDef` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Field { - Named(Name), - Indexed(usize), +pub struct Field(u32); + +impl Field { + pub fn new(value: usize) -> Field { + assert!(value < (u32::MAX) as usize); + Field(value as u32) + } + + pub fn index(self) -> usize { + self.0 as usize + } } impl<'tcx> Lvalue<'tcx> { @@ -489,10 +501,8 @@ impl<'tcx> Debug for Lvalue<'tcx> { write!(fmt,"({:?} as {:?})", data.base, variant_index), ProjectionElem::Deref => write!(fmt,"(*{:?})", data.base), - ProjectionElem::Field(Field::Named(name)) => - write!(fmt,"{:?}.{:?}", data.base, name), - ProjectionElem::Field(Field::Indexed(index)) => - write!(fmt,"{:?}.{:?}", data.base, index), + ProjectionElem::Field(field) => + write!(fmt,"{:?}.{:?}", data.base, field.index()), ProjectionElem::Index(ref index) => write!(fmt,"{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs index 92b026e5035b4..3b9d9228a1680 100644 --- a/src/librustc_mir/tcx/mod.rs +++ b/src/librustc_mir/tcx/mod.rs @@ -9,150 +9,117 @@ // except according to those terms. /*! - * This module contains the code to convert from the wacky tcx data - * structures into the hair. The `builder` is generally ignorant of - * the tcx etc, and instead goes through the `Cx` for most of its - * work. + * Methods for the various MIR types. These are intended for use after + * building is complete. */ -use hair::*; use repr::*; - -use rustc::middle::const_eval::ConstVal; -use rustc::middle::def_id::DefId; -use rustc::middle::infer::InferCtxt; -use rustc::middle::subst::{Subst, Substs}; -use rustc::middle::ty::{self, Ty}; -use syntax::codemap::Span; -use syntax::parse::token::{self, special_idents}; - -#[derive(Copy, Clone)] -pub struct Cx<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, -} - -impl<'a,'tcx> Cx<'a,'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> { - Cx { - tcx: infcx.tcx, - infcx: infcx, - } - } +use rustc::middle::subst::Substs; +use rustc::middle::ty::{self, AdtDef, Ty}; + +#[derive(Copy, Clone, Debug)] +pub enum LvalueTy<'tcx> { + /// Normal type. + Ty { ty: Ty<'tcx> }, + + /// Downcast to a particular variant of an enum. + Downcast { adt_def: AdtDef<'tcx>, + substs: &'tcx Substs<'tcx>, + variant_index: usize }, } -pub use self::pattern::PatNode; - -impl<'a,'tcx:'a> Cx<'a, 'tcx> { - /// Normalizes `ast` into the appropriate `mirror` type. - pub fn mirror>(&mut self, ast: M) -> M::Output { - ast.make_mirror(self) - } - - pub fn unit_ty(&mut self) -> Ty<'tcx> { - self.tcx.mk_nil() - } - - pub fn usize_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.usize - } - - pub fn usize_literal(&mut self, value: usize) -> Literal<'tcx> { - Literal::Value { value: ConstVal::Uint(value as u64) } - } - - pub fn bool_ty(&mut self) -> Ty<'tcx> { - self.tcx.types.bool - } - - pub fn true_literal(&mut self) -> Literal<'tcx> { - Literal::Value { value: ConstVal::Bool(true) } - } - - pub fn false_literal(&mut self) -> Literal<'tcx> { - Literal::Value { value: ConstVal::Bool(false) } +impl<'tcx> LvalueTy<'tcx> { + pub fn from_ty(ty: Ty<'tcx>) -> LvalueTy<'tcx> { + LvalueTy::Ty { ty: ty } } - pub fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { - let eq_def_id = self.tcx.lang_items.eq_trait().unwrap(); - self.cmp_method_ref(eq_def_id, "eq", ty) - } - - pub fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<'tcx> { - let ord_def_id = self.tcx.lang_items.ord_trait().unwrap(); - self.cmp_method_ref(ord_def_id, "le", ty) - } - - pub fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize { - adt_def.variants.len() - } - - pub fn fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec { - adt_def.variants[variant_index] - .fields - .iter() - .enumerate() - .map(|(index, field)| { - if field.name == special_idents::unnamed_field.name { - Field::Indexed(index) - } else { - Field::Named(field.name) - } - }) - .collect() - } - - pub fn needs_drop(&mut self, ty: Ty<'tcx>, span: Span) -> bool { - if self.infcx.type_moves_by_default(ty, span) { - // FIXME(#21859) we should do an add'l check here to determine if - // any dtor will execute, but the relevant fn - // (`type_needs_drop`) is currently factored into - // `librustc_trans`, so we can't easily do so. - true - } else { - // if type implements Copy, cannot require drop - false + pub fn to_ty(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx> { + match *self { + LvalueTy::Ty { ty } => + ty, + LvalueTy::Downcast { adt_def, substs, variant_index: _ } => + tcx.mk_enum(adt_def, substs), } } - pub fn span_bug(&mut self, span: Span, message: &str) -> ! { - self.tcx.sess.span_bug(span, message) + pub fn projection_ty(self, + tcx: &ty::ctxt<'tcx>, + elem: &LvalueElem<'tcx>) + -> LvalueTy<'tcx> + { + match *elem { + ProjectionElem::Deref => + LvalueTy::Ty { + ty: self.to_ty(tcx).builtin_deref(true, ty::LvaluePreference::NoPreference) + .unwrap() + .ty + }, + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => + LvalueTy::Ty { + ty: self.to_ty(tcx).builtin_index().unwrap() + }, + ProjectionElem::Downcast(adt_def1, index) => + match self.to_ty(tcx).sty { + ty::TyEnum(adt_def, substs) => { + assert!(index < adt_def.variants.len()); + assert_eq!(adt_def, adt_def1); + LvalueTy::Downcast { adt_def: adt_def, + substs: substs, + variant_index: index } + } + _ => { + tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self)) + } + }, + ProjectionElem::Field(field) => { + let field_ty = match self { + LvalueTy::Ty { ty } => match ty.sty { + ty::TyStruct(adt_def, substs) => + adt_def.struct_variant().fields[field.index()].ty(tcx, substs), + ty::TyTuple(ref tys) => + tys[field.index()], + _ => + tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)), + }, + LvalueTy::Downcast { adt_def, substs, variant_index } => + adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs), + }; + LvalueTy::Ty { ty: field_ty } + } + } } +} - pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.tcx +impl<'tcx> Mir<'tcx> { + pub fn operand_ty(&self, + tcx: &ty::ctxt<'tcx>, + operand: &Operand<'tcx>) + -> Ty<'tcx> + { + match *operand { + Operand::Consume(ref l) => self.lvalue_ty(tcx, l).to_ty(tcx), + Operand::Constant(ref c) => c.ty, + } } - fn cmp_method_ref(&mut self, - trait_def_id: DefId, - method_name: &str, - arg_ty: Ty<'tcx>) - -> ItemRef<'tcx> { - let method_name = token::intern(method_name); - let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty); - for trait_item in self.tcx.trait_items(trait_def_id).iter() { - match *trait_item { - ty::ImplOrTraitItem::MethodTraitItem(ref method) => { - if method.name == method_name { - let method_ty = self.tcx.lookup_item_type(method.def_id); - let method_ty = method_ty.ty.subst(self.tcx, &substs); - return ItemRef { - ty: method_ty, - def_id: method.def_id, - substs: self.tcx.mk_substs(substs), - }; - } - } - ty::ImplOrTraitItem::ConstTraitItem(..) | - ty::ImplOrTraitItem::TypeTraitItem(..) => {} - } + pub fn lvalue_ty(&self, + tcx: &ty::ctxt<'tcx>, + lvalue: &Lvalue<'tcx>) + -> LvalueTy<'tcx> + { + match *lvalue { + Lvalue::Var(index) => + LvalueTy::Ty { ty: self.var_decls[index as usize].ty }, + Lvalue::Temp(index) => + LvalueTy::Ty { ty: self.temp_decls[index as usize].ty }, + Lvalue::Arg(index) => + LvalueTy::Ty { ty: self.arg_decls[index as usize].ty }, + Lvalue::Static(def_id) => + LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty }, + Lvalue::ReturnPointer => + LvalueTy::Ty { ty: self.return_ty.unwrap() }, + Lvalue::Projection(ref proj) => + self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem) } - - self.tcx.sess.bug(&format!("found no method `{}` in `{:?}`", method_name, trait_def_id)); } } - -mod block; -mod expr; -mod pattern; -mod to_ref; diff --git a/src/librustc_mir/visit.rs b/src/librustc_mir/visit.rs new file mode 100644 index 0000000000000..b4d6075d0adb7 --- /dev/null +++ b/src/librustc_mir/visit.rs @@ -0,0 +1,239 @@ +// Copyright 2012-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. + +use rustc::middle::ty::Region; +use repr::*; + +pub trait Visitor<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. + + fn visit_mir(&mut self, mir: &Mir<'tcx>) { + self.super_mir(mir); + } + + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); + } + + fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { + self.super_statement(block, statement); + } + + fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { + self.super_assign(block, lvalue, rvalue); + } + + fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { + self.super_terminator(block, terminator); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + self.super_rvalue(rvalue); + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>) { + self.super_operand(operand); + } + + fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) { + self.super_lvalue(lvalue, context); + } + + fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { + self.super_branch(source, target); + } + + fn visit_constant(&mut self, constant: &Constant<'tcx>) { + self.super_constant(constant); + } + + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overidden. + + fn super_mir(&mut self, mir: &Mir<'tcx>) { + for block in mir.all_basic_blocks() { + let data = mir.basic_block_data(block); + self.visit_basic_block_data(block, data); + } + } + + fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { + for statement in &data.statements { + self.visit_statement(block, statement); + } + self.visit_terminator(block, &data.terminator); + } + + fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { + match statement.kind { + StatementKind::Assign(ref lvalue, ref rvalue) => { + self.visit_assign(block, lvalue, rvalue); + } + StatementKind::Drop(_, ref lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } + } + + fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { + self.visit_lvalue(lvalue, LvalueContext::Store); + self.visit_rvalue(rvalue); + } + + fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { + match *terminator { + Terminator::Goto { target } | + Terminator::Panic { target } => { + self.visit_branch(block, target); + } + + Terminator::If { ref cond, ref targets } => { + self.visit_operand(cond); + for &target in &targets[..] { + self.visit_branch(block, target); + } + } + + Terminator::Switch { ref discr, adt_def: _, ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } + + Terminator::Diverge | + Terminator::Return => { + } + + Terminator::Call { ref data, ref targets } => { + self.visit_lvalue(&data.destination, LvalueContext::Store); + self.visit_operand(&data.func); + for arg in &data.args { + self.visit_operand(arg); + } + for &target in &targets[..] { + self.visit_branch(block, target); + } + } + } + } + + fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + match *rvalue { + Rvalue::Use(ref operand) => { + self.visit_operand(operand); + } + + Rvalue::Repeat(ref value, ref len) => { + self.visit_operand(value); + self.visit_operand(len); + } + + Rvalue::Ref(r, bk, ref path) => { + self.visit_lvalue(path, LvalueContext::Borrow { + region: r, + kind: bk + }); + } + + Rvalue::Len(ref path) => { + self.visit_lvalue(path, LvalueContext::Inspect); + } + + Rvalue::Cast(_, ref operand, _) => { + self.visit_operand(operand); + } + + Rvalue::BinaryOp(_, ref lhs, ref rhs) => { + self.visit_operand(lhs); + self.visit_operand(rhs); + } + + Rvalue::UnaryOp(_, ref op) => { + self.visit_operand(op); + } + + Rvalue::Box(_) => { + } + + Rvalue::Aggregate(_, ref operands) => { + for operand in operands { + self.visit_operand(operand); + } + } + + Rvalue::Slice { ref input, from_start, from_end } => { + self.visit_lvalue(input, LvalueContext::Slice { + from_start: from_start, + from_end: from_end, + }); + } + + Rvalue::InlineAsm(_) => { + } + } + } + + fn super_operand(&mut self, operand: &Operand<'tcx>) { + match *operand { + Operand::Consume(ref lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Consume); + } + Operand::Constant(ref constant) => { + self.visit_constant(constant); + } + } + } + + fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) { + match *lvalue { + Lvalue::Var(_) | + Lvalue::Temp(_) | + Lvalue::Arg(_) | + Lvalue::Static(_) | + Lvalue::ReturnPointer => { + } + Lvalue::Projection(ref proj) => { + self.visit_lvalue(&proj.base, LvalueContext::Projection); + } + } + } + + fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { + } + + fn super_constant(&mut self, _constant: &Constant<'tcx>) { + } +} + +#[derive(Copy, Clone, Debug)] +pub enum LvalueContext { + // Appears as LHS of an assignment or as dest of a call + Store, + + // Being dropped + Drop, + + // Being inspected in some way, like loading a len + Inspect, + + // Being borrowed + Borrow { region: Region, kind: BorrowKind }, + + // Being sliced -- this should be same as being borrowed, probably + Slice { from_start: usize, from_end: usize }, + + // Used as base for another lvalue, e.g. `x` in `x.y` + Projection, + + // Consumed as part of an operand + Consume, +} diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 106591724a798..e9a0efe76cb42 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -53,7 +53,7 @@ use rustc::front::map as hir_map; use rustc::session::Session; use rustc::lint; use rustc::metadata::csearch; -use rustc::metadata::decoder::{DefLike, DlDef, DlField, DlImpl}; +use rustc::metadata::decoder::{DefLike, DlDef}; use rustc::middle::def::*; use rustc::middle::def_id::DefId; use rustc::middle::pat_util::pat_bindings_hygienic; @@ -652,6 +652,21 @@ impl Rib { } } +/// A definition along with the index of the rib it was found on +struct LocalDef { + ribs: Option<(Namespace, usize)>, + def: Def +} + +impl LocalDef { + fn from_def(def: Def) -> Self { + LocalDef { + ribs: None, + def: def + } + } +} + /// The link from a module up to its nearest parent node. #[derive(Clone,Debug)] enum ParentLink { @@ -1135,9 +1150,9 @@ pub struct Resolver<'a, 'tcx:'a> { // The idents for the primitive types. primitive_type_table: PrimitiveTypeTable, - def_map: DefMap, - freevars: RefCell, - freevars_seen: RefCell>>, + def_map: RefCell, + freevars: FreevarMap, + freevars_seen: NodeMap>, export_map: ExportMap, trait_map: TraitMap, external_exports: ExternalExports, @@ -1212,8 +1227,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { primitive_type_table: PrimitiveTypeTable::new(), def_map: RefCell::new(NodeMap()), - freevars: RefCell::new(NodeMap()), - freevars_seen: RefCell::new(NodeMap()), + freevars: NodeMap(), + freevars_seen: NodeMap(), export_map: NodeMap(), trait_map: NodeMap(), used_imports: HashSet::new(), @@ -1954,116 +1969,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.current_module = orig_module; } - /// Wraps the given definition in the appropriate number of `DefUpvar` - /// wrappers. - fn upvarify(&self, - ribs: &[Rib], - def_like: DefLike, - span: Span) - -> Option { - let mut def = match def_like { - DlDef(def) => def, - _ => return Some(def_like) - }; - match def { - DefUpvar(..) => { - self.session.span_bug(span, - &format!("unexpected {:?} in bindings", def)) - } - DefLocal(_, node_id) => { - for rib in ribs { - match rib.kind { - NormalRibKind => { - // Nothing to do. Continue. - } - ClosureRibKind(function_id) => { - let prev_def = def; - let node_def_id = self.ast_map.local_def_id(node_id); - - let mut seen = self.freevars_seen.borrow_mut(); - let seen = seen.entry(function_id).or_insert_with(|| NodeMap()); - if let Some(&index) = seen.get(&node_id) { - def = DefUpvar(node_def_id, node_id, index, function_id); - continue; - } - let mut freevars = self.freevars.borrow_mut(); - let vec = freevars.entry(function_id) - .or_insert_with(|| vec![]); - let depth = vec.len(); - vec.push(Freevar { def: prev_def, span: span }); - - def = DefUpvar(node_def_id, node_id, depth, function_id); - seen.insert(node_id, depth); - } - ItemRibKind | MethodRibKind => { - // This was an attempt to access an upvar inside a - // named function item. This is not allowed, so we - // report an error. - resolve_error( - self, - span, - ResolutionError::CannotCaptureDynamicEnvironmentInFnItem - ); - return None; - } - ConstantItemRibKind => { - // Still doesn't deal with upvars - resolve_error( - self, - span, - ResolutionError::AttemptToUseNonConstantValueInConstant - ); - return None; - } - } - } - } - DefTyParam(..) | DefSelfTy(..) => { - for rib in ribs { - match rib.kind { - NormalRibKind | MethodRibKind | ClosureRibKind(..) => { - // Nothing to do. Continue. - } - ItemRibKind => { - // This was an attempt to use a type parameter outside - // its scope. - - resolve_error(self, - span, - ResolutionError::TypeParametersFromOuterFunction); - return None; - } - ConstantItemRibKind => { - // see #9186 - resolve_error(self, span, ResolutionError::OuterTypeParameterContext); - return None; - } - } - } - } - _ => {} - } - Some(DlDef(def)) - } - - /// Searches the current set of local scopes and - /// applies translations for closures. - fn search_ribs(&self, - ribs: &[Rib], - name: Name, - span: Span) - -> Option { - // FIXME #4950: Try caching? - - for (i, rib) in ribs.iter().enumerate().rev() { - if let Some(def_like) = rib.bindings.get(&name).cloned() { - return self.upvarify(&ribs[i + 1..], def_like, span); - } - } - - None - } - /// Searches the current set of local scopes for labels. /// Stops after meeting a closure. fn search_label(&self, name: Name) -> Option { @@ -3123,19 +3028,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // Try to find a path to an item in a module. - let unqualified_def = - self.resolve_identifier(segments.last().unwrap().identifier, - namespace, - check_ribs, - span); + let unqualified_def = self.resolve_identifier( + segments.last().unwrap().identifier, + namespace, check_ribs); if segments.len() <= 1 { - return unqualified_def.map(mk_res); + return unqualified_def + .and_then(|def| self.adjust_local_def(def, span)) + .map(|def| { + PathResolution::new(def, LastMod(AllPublic), path_depth) + }); } let def = self.resolve_module_relative_path(span, segments, namespace); match (def, unqualified_def) { - (Some((ref d, _)), Some((ref ud, _))) if *d == *ud => { + (Some((ref d, _)), Some(ref ud)) if *d == ud.def => { self.session .add_lint(lint::builtin::UNUSED_QUALIFICATIONS, id, span, @@ -3147,31 +3054,118 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { def.map(mk_res) } - // Resolve a single identifier. + // Resolve a single identifier fn resolve_identifier(&mut self, identifier: Ident, namespace: Namespace, - check_ribs: bool, - span: Span) - -> Option<(Def, LastPrivate)> { + check_ribs: bool) + -> Option { // First, check to see whether the name is a primitive type. if namespace == TypeNS { if let Some(&prim_ty) = self.primitive_type_table .primitive_types .get(&identifier.name) { - return Some((DefPrimTy(prim_ty), LastMod(AllPublic))); + return Some(LocalDef::from_def(DefPrimTy(prim_ty))); } } if check_ribs { if let Some(def) = self.resolve_identifier_in_local_ribs(identifier, - namespace, - span) { - return Some((def, LastMod(AllPublic))); + namespace) { + return Some(def); } } self.resolve_item_by_name_in_lexical_scope(identifier.name, namespace) + .map(LocalDef::from_def) + } + + // Resolve a local definition, potentially adjusting for closures. + fn adjust_local_def(&mut self, local_def: LocalDef, span: Span) -> Option { + let ribs = match local_def.ribs { + Some((TypeNS, i)) => &self.type_ribs[i+1..], + Some((ValueNS, i)) => &self.value_ribs[i+1..], + _ => &[] as &[_] + }; + let mut def = local_def.def; + match def { + DefUpvar(..) => { + self.session.span_bug(span, + &format!("unexpected {:?} in bindings", def)) + } + DefLocal(_, node_id) => { + for rib in ribs { + match rib.kind { + NormalRibKind => { + // Nothing to do. Continue. + } + ClosureRibKind(function_id) => { + let prev_def = def; + let node_def_id = self.ast_map.local_def_id(node_id); + + let seen = self.freevars_seen.entry(function_id) + .or_insert_with(|| NodeMap()); + if let Some(&index) = seen.get(&node_id) { + def = DefUpvar(node_def_id, node_id, index, function_id); + continue; + } + let vec = self.freevars.entry(function_id) + .or_insert_with(|| vec![]); + let depth = vec.len(); + vec.push(Freevar { def: prev_def, span: span }); + + def = DefUpvar(node_def_id, node_id, depth, function_id); + seen.insert(node_id, depth); + } + ItemRibKind | MethodRibKind => { + // This was an attempt to access an upvar inside a + // named function item. This is not allowed, so we + // report an error. + resolve_error( + self, + span, + ResolutionError::CannotCaptureDynamicEnvironmentInFnItem + ); + return None; + } + ConstantItemRibKind => { + // Still doesn't deal with upvars + resolve_error( + self, + span, + ResolutionError::AttemptToUseNonConstantValueInConstant + ); + return None; + } + } + } + } + DefTyParam(..) | DefSelfTy(..) => { + for rib in ribs { + match rib.kind { + NormalRibKind | MethodRibKind | ClosureRibKind(..) => { + // Nothing to do. Continue. + } + ItemRibKind => { + // This was an attempt to use a type parameter outside + // its scope. + + resolve_error(self, + span, + ResolutionError::TypeParametersFromOuterFunction); + return None; + } + ConstantItemRibKind => { + // see #9186 + resolve_error(self, span, ResolutionError::OuterTypeParameterContext); + return None; + } + } + } + } + _ => {} + } + return Some(def); } // FIXME #4952: Merge me with resolve_name_in_module? @@ -3364,38 +3358,41 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn resolve_identifier_in_local_ribs(&mut self, ident: Ident, - namespace: Namespace, - span: Span) - -> Option { + namespace: Namespace) + -> Option { // Check the local set of ribs. - let search_result = match namespace { - ValueNS => { - let renamed = mtwt::resolve(ident); - self.search_ribs(&self.value_ribs, renamed, span) - } - TypeNS => { - let name = ident.name; - self.search_ribs(&self.type_ribs, name, span) - } + let (name, ribs) = match namespace { + ValueNS => (mtwt::resolve(ident), &self.value_ribs), + TypeNS => (ident.name, &self.type_ribs) }; - match search_result { - Some(DlDef(def)) => { - debug!("(resolving path in local ribs) resolved `{}` to local: {:?}", - ident, - def); - Some(def) - } - Some(DlField) | Some(DlImpl(_)) | None => { - None + for (i, rib) in ribs.iter().enumerate().rev() { + if let Some(def_like) = rib.bindings.get(&name).cloned() { + match def_like { + DlDef(def) => { + debug!("(resolving path in local ribs) resolved `{}` to {:?} at {}", + name, def, i); + return Some(LocalDef { + ribs: Some((namespace, i)), + def: def + }); + } + def_like => { + debug!("(resolving path in local ribs) resolved `{}` to pseudo-def {:?}", + name, def_like); + return None; + } + } } } + + None } fn resolve_item_by_name_in_lexical_scope(&mut self, name: Name, namespace: Namespace) - -> Option<(Def, LastPrivate)> { + -> Option { // Check the items. let module = self.current_module.clone(); match self.resolve_item_in_lexical_scope(module, @@ -3409,7 +3406,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { debug!("(resolving item path by identifier in lexical \ scope) failed to resolve {} after success...", name); - return None; + None } Some(def) => { debug!("(resolving item path in lexical scope) \ @@ -3418,7 +3415,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // This lookup is "all public" because it only searched // for one identifier in the current module (couldn't // have passed through reexports or anything like that. - return Some((def, LastMod(AllPublic))); + Some(def) } } } @@ -3433,7 +3430,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { resolve_error(self, span, ResolutionError::FailedToResolve(&*msg)) } - return None; + None } } } @@ -4029,8 +4026,8 @@ fn module_to_string(module: &Module) -> String { pub struct CrateMap { - pub def_map: DefMap, - pub freevars: RefCell, + pub def_map: RefCell, + pub freevars: FreevarMap, pub export_map: ExportMap, pub trait_map: TraitMap, pub external_exports: ExternalExports, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e02ce49132a7c..84ce458ed14f7 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -37,6 +37,7 @@ #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] +#![feature(slice_patterns)] #![feature(staged_api)] #![feature(unicode)] #![feature(vec_push_all)] @@ -50,8 +51,10 @@ extern crate graphviz; extern crate libc; extern crate rustc; extern crate rustc_back; +extern crate rustc_data_structures; extern crate rustc_front; extern crate rustc_llvm as llvm; +extern crate rustc_mir; extern crate rustc_platform_intrinsics as intrinsics; extern crate serialize; diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index f6f504e27aefc..3ea631bd7fb67 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -107,8 +107,17 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { } pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { + let source_file = self.tcx.sess.local_crate_source_file.as_ref(); + let crate_root = match source_file { + Some(source_file) => match source_file.file_name() { + Some(_) => source_file.parent().unwrap().display().to_string(), + None => source_file.display().to_string(), + }, + None => "".to_owned(), + }; + // The current crate. - self.fmt.crate_str(krate.span, name); + self.fmt.crate_str(krate.span, name, &crate_root); // Dump info about all the external crates referenced from this crate. for c in &self.save_ctxt.get_external_crates() { diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs index a32d8b1b761c5..34eb1d282636e 100644 --- a/src/librustc_trans/save/recorder.rs +++ b/src/librustc_trans/save/recorder.rs @@ -198,7 +198,7 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { vec!("name", "crate", "file_name"), false, false), - Crate => ("crate", vec!("name"), true, false), + Crate => ("crate", vec!("name", "crate_root"), true, false), FnCall => ("fn_call", vec!("refid", "refidcrate", "qualname", "scopeid"), true, @@ -658,8 +658,8 @@ impl<'a, 'tcx: 'a> FmtStrs<'a, 'tcx> { self.check_and_record(Typedef, span, sub_span, svec!(id, qualname, value)); } - pub fn crate_str(&mut self, span: Span, name: &str) { - self.record_with_span(Crate, span, span, svec!(name)); + pub fn crate_str(&mut self, span: Span, name: &str, crate_root: &str) { + self.record_with_span(Crate, span, span, svec!(name, crate_root)); } pub fn external_crate_str(&mut self, span: Span, name: &str, num: ast::CrateNum) { diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 2654ab3339a10..0fb0407d3ba72 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -222,6 +222,7 @@ use util::nodemap::FnvHashMap; use util::ppaux; use std; +use std::cell::RefCell; use std::cmp::Ordering; use std::fmt; use std::rc::Rc; @@ -303,7 +304,7 @@ impl<'a, 'tcx> Opt<'a, 'tcx> { RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) } Variant(disr_val, ref repr, _, _) => { - adt::trans_case(bcx, &**repr, disr_val) + SingleResult(Result::new(bcx, adt::trans_case(bcx, &**repr, disr_val))) } SliceLengthEqual(length, _) => { SingleResult(Result::new(bcx, C_uint(ccx, length))) @@ -495,7 +496,7 @@ fn expand_nested_bindings<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, - dm: &DefMap, + dm: &RefCell, m: &[Match<'a, 'p, 'blk, 'tcx>], col: usize, val: MatchInput, @@ -516,7 +517,7 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, let mut bound_ptrs = br.bound_ptrs.clone(); match this.node { hir::PatIdent(_, ref path, None) => { - if pat_is_binding(dm, &*this) { + if pat_is_binding(&dm.borrow(), &*this) { bound_ptrs.push((path.node.name, val.val)); } } @@ -541,7 +542,7 @@ fn enter_match<'a, 'b, 'p, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, } fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - dm: &DefMap, + dm: &RefCell, m: &[Match<'a, 'p, 'blk, 'tcx>], col: usize, val: MatchInput) @@ -555,7 +556,7 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Collect all of the matches that can match against anything. enter_match(bcx, dm, m, col, val, |pats| { - if pat_is_binding_or_wild(dm, &*pats[col]) { + if pat_is_binding_or_wild(&dm.borrow(), &*pats[col]) { let mut r = pats[..col].to_vec(); r.push_all(&pats[col + 1..]); Some(r) @@ -596,7 +597,7 @@ fn enter_default<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>, fn enter_opt<'a, 'p, 'blk, 'tcx>( bcx: Block<'blk, 'tcx>, _: ast::NodeId, - dm: &DefMap, + dm: &RefCell, m: &[Match<'a, 'p, 'blk, 'tcx>], opt: &Opt, col: usize, @@ -842,11 +843,11 @@ impl FailureHandler { } } -fn pick_column_to_specialize(def_map: &DefMap, m: &[Match]) -> Option { - fn pat_score(def_map: &DefMap, pat: &hir::Pat) -> usize { +fn pick_column_to_specialize(def_map: &RefCell, m: &[Match]) -> Option { + fn pat_score(def_map: &RefCell, pat: &hir::Pat) -> usize { match pat.node { hir::PatIdent(_, _, Some(ref inner)) => pat_score(def_map, &**inner), - _ if pat_is_refutable(def_map, pat) => 1, + _ if pat_is_refutable(&def_map.borrow(), pat) => 1, _ => 0 } } @@ -1800,7 +1801,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let ccx = bcx.ccx(); match pat.node { hir::PatIdent(pat_binding_mode, ref path1, ref inner) => { - if pat_is_binding(&tcx.def_map, &*pat) { + if pat_is_binding(&tcx.def_map.borrow(), &*pat) { // Allocate the stack slot where the value of this // binding will live and place it into the appropriate // map. diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index de09e33ec1427..a4f66110450df 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -945,15 +945,13 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr) /// /// This should ideally be less tightly tied to `_match`. pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) - -> _match::OptResult<'blk, 'tcx> { + -> ValueRef { match *r { CEnum(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) + C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true) } General(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) + C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true) } Univariant(..) => { bcx.ccx().sess().bug("no cases for univariants or structs") @@ -961,7 +959,7 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { assert!(discr == 0 || discr == 1); - _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) + C_bool(bcx.ccx(), discr != 0) } } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 46a4cb0c92ee7..a536060efbd0f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -44,6 +44,7 @@ use middle::pat_util::simple_name; use middle::subst::Substs; use middle::ty::{self, Ty, HasTypeFlags}; use rustc::front::map as hir_map; +use rustc_mir::mir_map::MirMap; use session::config::{self, NoDebugInfo, FullDebugInfo}; use session::Session; use trans::_match; @@ -74,6 +75,7 @@ use trans::intrinsic; use trans::machine; use trans::machine::{llsize_of, llsize_of_real}; use trans::meth; +use trans::mir; use trans::monomorphize; use trans::tvec; use trans::type_::Type; @@ -497,13 +499,8 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>, &format!("enum-iter-variant-{}", &variant.disr_val.to_string()) ); - match adt::trans_case(cx, &*repr, variant.disr_val) { - _match::SingleResult(r) => { - AddCase(llswitch, r.val, variant_cx.llbb) - } - _ => ccx.sess().unimpl("value from adt::trans_case \ - in iter_structural_ty") - } + let case_val = adt::trans_case(cx, &*repr, variant.disr_val); + AddCase(llswitch, case_val, variant_cx.llbb); let variant_cx = iter_variant(variant_cx, &*repr, @@ -1235,7 +1232,10 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, false }; + let mir = ccx.mir_map().get(&id); + let mut fcx = FunctionContext { + mir: mir, llfn: llfndecl, llenv: None, llretslotptr: Cell::new(None), @@ -1575,7 +1575,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfndecl: ValueRef, param_substs: &'tcx Substs<'tcx>, fn_ast_id: ast::NodeId, - _attributes: &[ast::Attribute], + attributes: &[ast::Attribute], output_type: ty::FnOutput<'tcx>, abi: Abi, closure_env: closure::ClosureEnv<'b>) { @@ -1604,6 +1604,12 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, &arena); let mut bcx = init_function(&fcx, false, output_type); + if attributes.iter().any(|item| item.check_name("rustc_mir")) { + mir::trans_mir(bcx); + fcx.cleanup(); + return; + } + // cleanup scope for the incoming arguments let fn_cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(ccx, fn_ast_id, body.span, true); @@ -2737,7 +2743,10 @@ pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet { }).collect() } -pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation { +pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, + mir_map: &MirMap<'tcx>, + analysis: ty::CrateAnalysis) + -> CrateTranslation { let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; let krate = tcx.map.krate(); @@ -2779,6 +2788,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat let shared_ccx = SharedCrateContext::new(&link_meta.crate_name, codegen_units, tcx, + &mir_map, export_map, Sha256::new(), link_meta.clone(), diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index b39b7818a6350..8d6ba53dd222d 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -16,7 +16,7 @@ pub use self::ExprOrMethodCall::*; use session::Session; use llvm; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind}; use llvm::{True, False, Bool}; use middle::cfg; use middle::def; @@ -40,6 +40,7 @@ use middle::traits; use middle::ty::{self, HasTypeFlags, Ty}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use rustc_front::hir; +use rustc_mir::repr::Mir; use util::nodemap::{FnvHashMap, NodeMap}; use arena::TypedArena; @@ -328,6 +329,11 @@ impl<'tcx> DropFlagHintsMap<'tcx> { // Function context. Every LLVM function we create will have one of // these. pub struct FunctionContext<'a, 'tcx: 'a> { + // The MIR for this function. At present, this is optional because + // we only have MIR available for things that are local to the + // crate. + pub mir: Option<&'a Mir<'tcx>>, + // The ValueRef returned from a call to llvm::LLVMAddFunction; the // address of the first instruction in the sequence of // instructions for this function that will go in the .text @@ -407,6 +413,10 @@ pub struct FunctionContext<'a, 'tcx: 'a> { } impl<'a, 'tcx> FunctionContext<'a, 'tcx> { + pub fn mir(&self) -> &'a Mir<'tcx> { + self.mir.unwrap() + } + pub fn arg_offset(&self) -> usize { self.env_arg_pos() + if self.llenv.is_some() { 1 } else { 0 } } @@ -644,6 +654,10 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> { } pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() } + pub fn mir(&self) -> &'blk Mir<'tcx> { + self.fcx.mir() + } + pub fn name(&self, name: ast::Name) -> String { name.to_string() } @@ -729,6 +743,12 @@ pub fn C_floating(s: &str, t: Type) -> ValueRef { } } +pub fn C_floating_f64(f: f64, t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstReal(t.to_ref(), f) + } +} + pub fn C_nil(ccx: &CrateContext) -> ValueRef { C_struct(ccx, &[], false) } @@ -1132,3 +1152,65 @@ pub fn inlined_variant_def<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.sess().bug(&format!("no variant for {:?}::{}", adt_def, inlined_vid)) }) } + +// To avoid UB from LLVM, these two functions mask RHS with an +// appropriate mask unconditionally (i.e. the fallback behavior for +// all shifts). For 32- and 64-bit types, this matches the semantics +// of Java. (See related discussion on #1877 and #10183.) + +pub fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + binop_debug_loc: DebugLoc) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); + build::Shl(bcx, lhs, rhs, binop_debug_loc) +} + +pub fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lhs_t: Ty<'tcx>, + lhs: ValueRef, + rhs: ValueRef, + binop_debug_loc: DebugLoc) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); + let is_signed = lhs_t.is_signed(); + if is_signed { + build::AShr(bcx, lhs, rhs, binop_debug_loc) + } else { + build::LShr(bcx, lhs, rhs, binop_debug_loc) + } +} + +fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + rhs: ValueRef, + debug_loc: DebugLoc) -> ValueRef { + let rhs_llty = val_ty(rhs); + build::And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) +} + +pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool) -> ValueRef { + let kind = llty.kind(); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = llty.int_width() - 1; + if invert { + C_integral(mask_llty, !val, true) + } else { + C_integral(mask_llty, val, false) + } + }, + TypeKind::Vector => { + let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); + build::VectorSplat(bcx, mask_llty.vector_length(), mask) + }, + _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 01d0c51a08dd9..1f1d43feeb38e 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -14,6 +14,7 @@ use metadata::common::LinkMeta; use middle::def::ExportMap; use middle::def_id::DefId; use middle::traits; +use rustc_mir::mir_map::MirMap; use trans::adt; use trans::base; use trans::builder::Builder; @@ -70,6 +71,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { stats: Stats, check_overflow: bool, check_drop_flag_for_sanity: bool, + mir_map: &'a MirMap<'tcx>, available_drop_glues: RefCell, String>>, use_dll_storage_attrs: bool, @@ -251,6 +253,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { pub fn new(crate_name: &str, local_count: usize, tcx: &'b ty::ctxt<'tcx>, + mir_map: &'b MirMap<'tcx>, export_map: ExportMap, symbol_hasher: Sha256, link_meta: LinkMeta, @@ -317,6 +320,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { link_meta: link_meta, symbol_hasher: RefCell::new(symbol_hasher), tcx: tcx, + mir_map: mir_map, stats: Stats { n_glues_created: Cell::new(0), n_null_glues: Cell::new(0), @@ -803,6 +807,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { pub fn use_dll_storage_attrs(&self) -> bool { self.shared.use_dll_storage_attrs() } + + pub fn mir_map(&self) -> &'b MirMap<'tcx> { + self.shared.mir_map + } } pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs index 01f5a32b1a3d4..0c424de9e10b8 100644 --- a/src/librustc_trans/trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs @@ -167,7 +167,7 @@ fn walk_pattern(cx: &CrateContext, // Check if this is a binding. If so we need to put it on the // scope stack and maybe introduce an artificial scope - if pat_util::pat_is_binding(def_map, &*pat) { + if pat_util::pat_is_binding(&def_map.borrow(), &*pat) { let name = path1.node.name; diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 6144de7109fba..7648587e35268 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -2574,29 +2574,6 @@ impl OverflowOpViaInputCheck { } } -fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llty: Type, - mask_llty: Type, - invert: bool) -> ValueRef { - let kind = llty.kind(); - match kind { - TypeKind::Integer => { - // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. - let val = llty.int_width() - 1; - if invert { - C_integral(mask_llty, !val, true) - } else { - C_integral(mask_llty, val, false) - } - }, - TypeKind::Vector => { - let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); - VectorSplat(bcx, mask_llty.vector_length(), mask) - }, - _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind), - } -} - // Check if an integer or vector contains a nonzero element. fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, value: ValueRef, @@ -2616,44 +2593,6 @@ fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e. the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - Shl(bcx, lhs, rhs, binop_debug_loc) -} - -fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs_t: Ty<'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - let is_signed = lhs_t.is_signed(); - if is_signed { - AShr(bcx, lhs, rhs, binop_debug_loc) - } else { - LShr(bcx, lhs, rhs, binop_debug_loc) - } -} - -fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - rhs: ValueRef, - debug_loc: DebugLoc) -> ValueRef { - let rhs_llty = val_ty(rhs); - And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) -} - fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info: NodeIdAndSpan, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef, diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs new file mode 100644 index 0000000000000..f5fa897bca631 --- /dev/null +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -0,0 +1,115 @@ +// Copyright 2012-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. + +//! An analysis to determine which temporaries require allocas and +//! which do not. + +use rustc_data_structures::fnv::FnvHashSet; +use rustc_mir::repr as mir; +use rustc_mir::visit::{Visitor, LvalueContext}; +use trans::common::{self, Block}; +use super::rvalue; + +pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) + -> FnvHashSet { + let mut analyzer = TempAnalyzer::new(); + + analyzer.visit_mir(mir); + + for (index, temp_decl) in mir.temp_decls.iter().enumerate() { + let ty = bcx.monomorphize(&temp_decl.ty); + debug!("temp {:?} has type {:?}", index, ty); + if + ty.is_scalar() || + ty.is_unique() || + (ty.is_region_ptr() && !common::type_is_fat_ptr(bcx.tcx(), ty)) || + ty.is_simd() + { + // These sorts of types are immediates that we can store + // in an ValueRef without an alloca. + assert!(common::type_is_immediate(bcx.ccx(), ty)); + } else { + // These sorts of types require an alloca. Note that + // type_is_immediate() may *still* be true, particularly + // for newtypes, but we currently force some types + // (e.g. structs) into an alloca unconditionally, just so + // that we don't have to deal with having two pathways + // (gep vs extractvalue etc). + analyzer.mark_as_lvalue(index); + } + } + + analyzer.lvalue_temps +} + +struct TempAnalyzer { + lvalue_temps: FnvHashSet, +} + +impl TempAnalyzer { + fn new() -> TempAnalyzer { + TempAnalyzer { lvalue_temps: FnvHashSet() } + } + + fn mark_as_lvalue(&mut self, temp: usize) { + debug!("marking temp {} as lvalue", temp); + self.lvalue_temps.insert(temp); + } +} + +impl<'tcx> Visitor<'tcx> for TempAnalyzer { + fn visit_assign(&mut self, + block: mir::BasicBlock, + lvalue: &mir::Lvalue<'tcx>, + rvalue: &mir::Rvalue<'tcx>) { + debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); + + match *lvalue { + mir::Lvalue::Temp(index) => { + if !rvalue::rvalue_creates_operand(rvalue) { + self.mark_as_lvalue(index as usize); + } + } + _ => { + self.visit_lvalue(lvalue, LvalueContext::Store); + } + } + + self.visit_rvalue(rvalue); + } + + fn visit_lvalue(&mut self, + lvalue: &mir::Lvalue<'tcx>, + context: LvalueContext) { + debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context); + + match *lvalue { + mir::Lvalue::Temp(index) => { + match context { + LvalueContext::Consume => { + } + LvalueContext::Store | + LvalueContext::Drop | + LvalueContext::Inspect | + LvalueContext::Borrow { .. } | + LvalueContext::Slice { .. } | + LvalueContext::Projection => { + self.mark_as_lvalue(index as usize); + } + } + } + _ => { + } + } + + self.super_lvalue(lvalue, context); + } +} diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs new file mode 100644 index 0000000000000..c9f4123c17127 --- /dev/null +++ b/src/librustc_trans/trans/mir/block.rs @@ -0,0 +1,106 @@ +// Copyright 2012-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. + +use llvm::BasicBlockRef; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::debuginfo::DebugLoc; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_block(&mut self, bb: mir::BasicBlock) { + debug!("trans_block({:?})", bb); + + let mut bcx = self.bcx(bb); + let data = self.mir.basic_block_data(bb); + + for statement in &data.statements { + bcx = self.trans_statement(bcx, statement); + } + + debug!("trans_block: terminator: {:?}", data.terminator); + + match data.terminator { + mir::Terminator::Goto { target } => { + build::Br(bcx, self.llblock(target), DebugLoc::None) + } + + mir::Terminator::Panic { .. } => { + unimplemented!() + } + + mir::Terminator::If { ref cond, targets: [true_bb, false_bb] } => { + let cond = self.trans_operand(bcx, cond); + let lltrue = self.llblock(true_bb); + let llfalse = self.llblock(false_bb); + build::CondBr(bcx, cond.llval, lltrue, llfalse, DebugLoc::None); + } + + mir::Terminator::Switch { .. } => { + unimplemented!() + } + + mir::Terminator::Diverge => { + if let Some(llpersonalityslot) = self.llpersonalityslot { + let lp = build::Load(bcx, llpersonalityslot); + // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); + build::Resume(bcx, lp); + } else { + // This fn never encountered anything fallible, so + // a Diverge cannot actually happen. Note that we + // do a total hack to ensure that we visit the + // DIVERGE block last. + build::Unreachable(bcx); + } + } + + mir::Terminator::Return => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); + } + + mir::Terminator::Call { .. } => { + unimplemented!() + //let llbb = unimplemented!(); // self.make_landing_pad(panic_bb); + // + //let tr_dest = self.trans_lvalue(bcx, &data.destination); + // + //// Create the callee. This will always be a fn + //// ptr and hence a kind of scalar. + //let callee = self.trans_operand(bcx, &data.func); + // + //// Process the arguments. + // + //let args = unimplemented!(); + // + //callee::trans_call_inner(bcx, + // DebugLoc::None, + // |bcx, _| Callee { + // bcx: bcx, + // data: CalleeData::Fn(callee.llval), + // ty: callee.ty, + // }, + // args, + // Some(Dest::SaveIn(tr_dest.llval))); + } + } + } + + fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { + self.blocks[bb.index()] + } + + fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { + self.blocks[bb.index()].llbb + } +} diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs new file mode 100644 index 0000000000000..1b61001834a99 --- /dev/null +++ b/src/librustc_trans/trans/mir/constant.rs @@ -0,0 +1,63 @@ +// Copyright 2012-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. + +use llvm::ValueRef; +use rustc::middle::const_eval::ConstVal; +use rustc_mir::repr as mir; +use trans::consts::{self, TrueConst}; +use trans::common::{self, Block}; +use trans::type_of; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_constant(&mut self, + bcx: Block<'bcx, 'tcx>, + constant: &mir::Constant<'tcx>) + -> ValueRef + { + let ccx = bcx.ccx(); + let constant_ty = bcx.monomorphize(&constant.ty); + let llty = type_of::type_of(ccx, constant_ty); + match constant.literal { + mir::Literal::Item { .. } => { + unimplemented!() + } + mir::Literal::Value { ref value } => { + match *value { + ConstVal::Float(v) => common::C_floating_f64(v, llty), + ConstVal::Bool(v) => common::C_bool(ccx, v), + ConstVal::Int(v) => common::C_integral(llty, v as u64, true), + ConstVal::Uint(v) => common::C_integral(llty, v, false), + ConstVal::Str(ref v) => common::C_str_slice(ccx, v.clone()), + ConstVal::ByteStr(ref v) => consts::addr_of(ccx, + common::C_bytes(ccx, v), + 1, + "byte_str"), + ConstVal::Struct(id) | ConstVal::Tuple(id) => { + let expr = bcx.tcx().map.expect_expr(id); + let (llval, _) = match consts::const_expr(ccx, + expr, + bcx.fcx.param_substs, + None, + TrueConst::Yes) { + Ok(v) => v, + Err(_) => panic!("constant eval failure"), + }; + llval + } + ConstVal::Function(_) => { + unimplemented!() + } + } + } + } + } +} diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs new file mode 100644 index 0000000000000..1ce7b55a9c686 --- /dev/null +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -0,0 +1,154 @@ +// Copyright 2012-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. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use trans::adt; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::machine; +use trans::tvec; + +use super::{MirContext, TempRef}; + +#[derive(Copy, Clone)] +pub struct LvalueRef<'tcx> { + /// Pointer to the contents of the lvalue + pub llval: ValueRef, + + /// Monomorphized type of this lvalue, including variant information + pub ty: LvalueTy<'tcx>, +} + +impl<'tcx> LvalueRef<'tcx> { + pub fn new(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { + LvalueRef { llval: llval, ty: lvalue_ty } + } + + pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>, + ty: Ty<'tcx>, + name: &str) + -> LvalueRef<'tcx> + { + let lltemp = base::alloc_ty(bcx, ty, name); + LvalueRef::new(lltemp, LvalueTy::from_ty(ty)) + } +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_lvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lvalue: &mir::Lvalue<'tcx>) + -> LvalueRef<'tcx> { + debug!("trans_lvalue(lvalue={:?})", lvalue); + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let tcx = bcx.tcx(); + match *lvalue { + mir::Lvalue::Var(index) => self.vars[index as usize], + mir::Lvalue::Temp(index) => match self.temps[index as usize] { + TempRef::Lvalue(lvalue) => + lvalue, + TempRef::Operand(..) => + tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)), + }, + mir::Lvalue::Arg(index) => self.args[index as usize], + mir::Lvalue::Static(_def_id) => unimplemented!(), + mir::Lvalue::ReturnPointer => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + let llval = fcx.get_ret_slot(bcx, return_ty, "return"); + LvalueRef::new(llval, LvalueTy::from_ty(return_ty.unwrap())) + } + mir::Lvalue::Projection(ref projection) => { + let tr_base = self.trans_lvalue(bcx, &projection.base); + let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); + let llprojected = match projection.elem { + mir::ProjectionElem::Deref => { + let base_ty = tr_base.ty.to_ty(tcx); + base::load_ty(bcx, tr_base.llval, base_ty) + } + mir::ProjectionElem::Field(ref field) => { + let base_ty = tr_base.ty.to_ty(tcx); + let base_repr = adt::represent_type(ccx, base_ty); + let discr = match tr_base.ty { + LvalueTy::Ty { .. } => 0, + LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, + }; + let discr = discr as u64; + adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()) + } + mir::ProjectionElem::Index(ref index) => { + let base_ty = tr_base.ty.to_ty(tcx); + let index = self.trans_operand(bcx, index); + let llindex = self.prepare_index(bcx, index.llval); + let (llbase, _) = tvec::get_base_and_len(bcx, tr_base.llval, base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let base_ty = tr_base.ty.to_ty(tcx); + let lloffset = common::C_u32(bcx.ccx(), offset); + let llindex = self.prepare_index(bcx, lloffset); + let (llbase, _) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = common::C_u32(bcx.ccx(), offset); + let base_ty = tr_base.ty.to_ty(tcx); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None); + let llindex = self.prepare_index(bcx, llindex); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::Downcast(..) => { + tr_base.llval + } + }; + LvalueRef { + llval: llprojected, + ty: projected_ty, + } + } + } + } + + /// Adjust the bitwidth of an index since LLVM is less forgiving + /// than we are. + /// + /// nmatsakis: is this still necessary? Not sure. + fn prepare_index(&mut self, + bcx: Block<'bcx, 'tcx>, + llindex: ValueRef) + -> ValueRef + { + let ccx = bcx.ccx(); + let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex)); + let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type()); + if index_size < int_size { + build::ZExt(bcx, llindex, ccx.int_type()) + } else if index_size > int_size { + build::Trunc(bcx, llindex, ccx.int_type()) + } else { + llindex + } + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs new file mode 100644 index 0000000000000..3b018cc132184 --- /dev/null +++ b/src/librustc_trans/trans/mir/mod.rs @@ -0,0 +1,195 @@ +// Copyright 2012-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. + +use libc::c_uint; +use llvm::{self, ValueRef}; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::expr; +use trans::type_of; + +use self::lvalue::LvalueRef; +use self::operand::OperandRef; + +// FIXME DebugLoc is always None right now + +/// Master context for translating MIR. +pub struct MirContext<'bcx, 'tcx:'bcx> { + mir: &'bcx mir::Mir<'tcx>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + llpersonalityslot: Option, + + /// A `Block` for each MIR `BasicBlock` + blocks: Vec>, + + /// An LLVM alloca for each MIR `VarDecl` + vars: Vec>, + + /// The location where each MIR `TempDecl` is stored. This is + /// usually an `LvalueRef` representing an alloca, but not always: + /// sometimes we can skip the alloca and just store the value + /// directly using an `OperandRef`, which makes for tighter LLVM + /// IR. The conditions for using an `OperandRef` are as follows: + /// + /// - the type of the temporary must be judged "immediate" by `type_is_immediate` + /// - the operand must never be referenced indirectly + /// - we should not take its address using the `&` operator + /// - nor should it appear in an lvalue path like `tmp.a` + /// - the operand must be defined by an rvalue that can generate immediate + /// values + /// + /// Avoiding allocs can also be important for certain intrinsics, + /// notably `expect`. + temps: Vec>, + + /// The arguments to the function; as args are lvalues, these are + /// always indirect, though we try to avoid creating an alloca + /// when we can (and just reuse the pointer the caller provided). + args: Vec>, +} + +enum TempRef<'tcx> { + Lvalue(LvalueRef<'tcx>), + Operand(Option>), +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { + let fcx = bcx.fcx; + let mir = bcx.mir(); + + let mir_blocks = bcx.mir().all_basic_blocks(); + + // Analyze the temps to determine which must be lvalues + // FIXME + let lvalue_temps = analyze::lvalue_temps(bcx, mir); + + // Allocate variable and temp allocas + let vars = mir.var_decls.iter() + .map(|decl| (bcx.monomorphize(&decl.ty), decl.name)) + .map(|(mty, name)| LvalueRef::alloca(bcx, mty, &name.as_str())) + .collect(); + let temps = mir.temp_decls.iter() + .map(|decl| bcx.monomorphize(&decl.ty)) + .enumerate() + .map(|(i, mty)| if lvalue_temps.contains(&i) { + TempRef::Lvalue(LvalueRef::alloca(bcx, + mty, + &format!("temp{:?}", i))) + } else { + // If this is an immediate temp, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + TempRef::Operand(None) + }) + .collect(); + let args = arg_value_refs(bcx, mir); + + // Allocate a `Block` for every basic block + let block_bcxs: Vec> = + mir_blocks.iter() + .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None)) + .collect(); + + // Branch to the START block + let start_bcx = block_bcxs[mir::START_BLOCK.index()]; + build::Br(bcx, start_bcx.llbb, DebugLoc::None); + + let mut mircx = MirContext { + mir: mir, + llpersonalityslot: None, + blocks: block_bcxs, + vars: vars, + temps: temps, + args: args, + }; + + // Translate the body of each block + for &bb in &mir_blocks { + if bb != mir::DIVERGE_BLOCK { + mircx.trans_block(bb); + } + } + + // Total hack: translate DIVERGE_BLOCK last. This is so that any + // panics which the fn may do can initialize the + // `llpersonalityslot` cell. We don't do this up front because the + // LLVM type of it is (frankly) annoying to compute. + mircx.trans_block(mir::DIVERGE_BLOCK); +} + +/// Produce, for each argument, a `ValueRef` pointing at the +/// argument's value. As arguments are lvalues, these are always +/// indirect. +fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, + mir: &mir::Mir<'tcx>) + -> Vec> { + // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though + let fcx = bcx.fcx; + let tcx = bcx.tcx(); + let mut idx = fcx.arg_offset() as c_uint; + mir.arg_decls + .iter() + .enumerate() + .map(|(arg_index, arg_decl)| { + let arg_ty = bcx.monomorphize(&arg_decl.ty); + let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + // FIXME: lifetimes, debug info + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + llarg + } else if common::type_is_fat_ptr(tcx, arg_ty) { + // we pass fat pointers as two words, but we want to + // represent them internally as a pointer to two words, + // so make an alloca to store them in. + let lldata = llvm::get_param(fcx.llfn, idx); + let llextra = llvm::get_param(fcx.llfn, idx + 1); + idx += 2; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, lldata, expr::get_dataptr(bcx, lltemp)); + build::Store(bcx, llextra, expr::get_dataptr(bcx, lltemp)); + lltemp + } else { + // otherwise, arg is passed by value, so make a + // temporary and store it there + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, llarg, lltemp); + lltemp + }; + LvalueRef::new(llval, LvalueTy::from_ty(arg_ty)) + }) + .collect() +} + +mod analyze; +mod block; +mod constant; +mod lvalue; +mod rvalue; +mod operand; +mod statement; + diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs new file mode 100644 index 0000000000000..a0308032ac07b --- /dev/null +++ b/src/librustc_trans/trans/mir/operand.rs @@ -0,0 +1,110 @@ +// Copyright 2012-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. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::datum; + +use super::{MirContext, TempRef}; + +#[derive(Copy, Clone)] +pub struct OperandRef<'tcx> { + // This will be "indirect" if `appropriate_rvalue_mode` returns + // ByRef, and otherwise ByValue. + pub llval: ValueRef, + + // The type of value being returned. + pub ty: Ty<'tcx> +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + // watch out for temporaries that do not have an + // alloca; they are handled somewhat differently + if let &mir::Lvalue::Temp(index) = lvalue { + match self.temps[index as usize] { + TempRef::Operand(Some(o)) => { + return o; + } + TempRef::Operand(None) => { + bcx.tcx().sess.bug( + &format!("use of {:?} before def", lvalue)); + } + TempRef::Lvalue(..) => { + // use path below + } + } + } + + // for most lvalues, to consume them we just load them + // out from their home + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + ty); + let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { + datum::ByValue => build::Load(bcx, tr_lvalue.llval), + datum::ByRef => tr_lvalue.llval, + }; + OperandRef { + llval: llval, + ty: ty + } + } + + mir::Operand::Constant(ref constant) => { + let llval = self.trans_constant(bcx, constant); + let ty = bcx.monomorphize(&constant.ty); + OperandRef { + llval: llval, + ty: ty, + } + } + } + } + + pub fn trans_operand_into(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + operand: &mir::Operand<'tcx>) + { + debug!("trans_operand_into(lldest={}, operand={:?})", + bcx.val_to_string(lldest), + operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let lvalue_ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand_into: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + lvalue_ty); + base::memcpy_ty(bcx, lldest, tr_lvalue.llval, lvalue_ty); + } + + mir::Operand::Constant(..) => { + unimplemented!() + } + } + } +} diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs new file mode 100644 index 0000000000000..ad89ee79de40f --- /dev/null +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -0,0 +1,307 @@ +// Copyright 2012-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. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_front::hir; +use rustc_mir::repr as mir; + +use trans::asm; +use trans::base; +use trans::build; +use trans::common::{self, Block, Result}; +use trans::debuginfo::DebugLoc; +use trans::declare; +use trans::expr; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use trans::tvec; + +use super::MirContext; +use super::operand::OperandRef; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_rvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + rvalue: &mir::Rvalue<'tcx>) + -> Block<'bcx, 'tcx> + { + debug!("trans_rvalue(lldest={}, rvalue={:?})", + bcx.val_to_string(lldest), + rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + self.trans_operand_into(bcx, lldest, operand); + bcx + } + + mir::Rvalue::Cast(..) => { + unimplemented!() + } + + mir::Rvalue::Repeat(..) => { + unimplemented!() + } + + mir::Rvalue::Aggregate(_, ref operands) => { + for (i, operand) in operands.iter().enumerate() { + // Note: perhaps this should be StructGep, but + // note that in some cases the values here will + // not be structs but arrays. + let lldest_i = build::GEPi(bcx, lldest, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + bcx + } + + mir::Rvalue::Slice { ref input, from_start, from_end } => { + let ccx = bcx.ccx(); + let input = self.trans_lvalue(bcx, input); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + input.llval, + input.ty.to_ty(bcx.tcx())); + let llbase1 = build::GEPi(bcx, llbase, &[from_start]); + let adj = common::C_uint(ccx, from_start + from_end); + let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); + let lladdrdest = expr::get_dataptr(bcx, lldest); + build::Store(bcx, llbase1, lladdrdest); + let llmetadest = expr::get_meta(bcx, lldest); + build::Store(bcx, lllen1, llmetadest); + bcx + } + + mir::Rvalue::InlineAsm(inline_asm) => { + asm::trans_inline_asm(bcx, inline_asm) + } + + _ => { + assert!(rvalue_creates_operand(rvalue)); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); + build::Store(bcx, temp.llval, lldest); + bcx + } + } + } + + pub fn trans_rvalue_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> (Block<'bcx, 'tcx>, OperandRef<'tcx>) + { + assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + let operand = self.trans_operand(bcx, operand); + (bcx, operand) + } + + mir::Rvalue::Cast(..) => { + unimplemented!() + } + + mir::Rvalue::Ref(_, _, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + + // Note: lvalues are indirect, so storing the `llval` into the + // destination effectively creates a reference. + (bcx, OperandRef { + llval: tr_lvalue.llval, + ty: tr_lvalue.ty.to_ty(bcx.tcx()), + }) + } + + mir::Rvalue::Len(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let (_, lllen) = tvec::get_base_and_len(bcx, + tr_lvalue.llval, + tr_lvalue.ty.to_ty(bcx.tcx())); + (bcx, OperandRef { + llval: lllen, + ty: bcx.tcx().types.usize, + }) + } + + mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.trans_operand(bcx, lhs); + let rhs = self.trans_operand(bcx, rhs); + let is_float = lhs.ty.is_fp(); + let is_signed = lhs.ty.is_signed(); + let binop_debug_loc = DebugLoc::None; + let llval = match op { + mir::BinOp::Add => if is_float { + build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Sub => if is_float { + build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Mul => if is_float { + build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Div => if is_float { + build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else if is_signed { + build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Rem => if is_float { + // LLVM currently always lowers the `frem` instructions appropriate + // library calls typically found in libm. Notably f64 gets wired up + // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for + // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's + // instead just an inline function in a header that goes up to a + // f64, uses `fmod`, and then comes back down to a f32. + // + // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will + // still unconditionally lower frem instructions over 32-bit floats + // to a call to `fmodf`. To work around this we special case MSVC + // 32-bit float rem instructions and instead do the call out to + // `fmod` ourselves. + // + // Note that this is currently duplicated with src/libcore/ops.rs + // which does the same thing, and it would be nice to perhaps unify + // these two implementations one day! Also note that we call `fmod` + // for both 32 and 64-bit floats because if we emit any FRem + // instruction at all then LLVM is capable of optimizing it into a + // 32-bit FRem (which we're trying to avoid). + let tcx = bcx.tcx(); + let use_fmod = tcx.sess.target.target.options.is_like_msvc && + tcx.sess.target.target.arch == "x86"; + if use_fmod { + let f64t = Type::f64(bcx.ccx()); + let fty = Type::func(&[f64t, f64t], &f64t); + let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty, + tcx.types.f64); + if lhs.ty == tcx.types.f32 { + let lllhs = build::FPExt(bcx, lhs.llval, f64t); + let llrhs = build::FPExt(bcx, rhs.llval, f64t); + let llres = build::Call(bcx, llfn, &[lllhs, llrhs], + None, binop_debug_loc); + build::FPTrunc(bcx, llres, Type::f32(bcx.ccx())) + } else { + build::Call(bcx, llfn, &[lhs.llval, rhs.llval], + None, binop_debug_loc) + } + } else { + build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } + } else if is_signed { + build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::Shl => common::build_unchecked_lshift(bcx, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Shr => common::build_unchecked_rshift(bcx, + lhs.ty, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiEq, binop_debug_loc), + mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLt, binop_debug_loc), + mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLe, binop_debug_loc), + mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiNe, binop_debug_loc), + mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGe, binop_debug_loc), + mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGt, binop_debug_loc), + }; + (bcx, OperandRef { + llval: llval, + ty: lhs.ty, + }) + } + + mir::Rvalue::UnaryOp(op, ref operand) => { + let operand = self.trans_operand(bcx, operand); + let is_float = operand.ty.is_fp(); + let debug_loc = DebugLoc::None; + let llval = match op { + mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc), + mir::UnOp::Neg => if is_float { + build::FNeg(bcx, operand.llval, debug_loc) + } else { + build::Neg(bcx, operand.llval, debug_loc) + } + }; + (bcx, OperandRef { + llval: llval, + ty: operand.ty, + }) + } + + mir::Rvalue::Box(content_ty) => { + let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty); + let llty = type_of::type_of(bcx.ccx(), content_ty); + let llsize = machine::llsize_of(bcx.ccx(), llty); + let align = type_of::align_of(bcx.ccx(), content_ty); + let llalign = common::C_uint(bcx.ccx(), align); + let llty_ptr = llty.ptr_to(); + let box_ty = bcx.tcx().mk_box(content_ty); + let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx, + llty_ptr, + box_ty, + llsize, + llalign, + DebugLoc::None); + (bcx, OperandRef { + llval: llval, + ty: box_ty, + }) + } + + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => { + bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue)); + } + } + } +} + +pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Use(..) | // (*) + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Box(..) => + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => + false, + } + + // (*) this is only true if the type is suitable +} diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs new file mode 100644 index 0000000000000..95ff049836eb4 --- /dev/null +++ b/src/librustc_trans/trans/mir/statement.rs @@ -0,0 +1,70 @@ +// Copyright 2012-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. + +use rustc::middle::ty::LvaluePreference; +use rustc_mir::repr as mir; +use trans::common::Block; +use trans::debuginfo::DebugLoc; +use trans::glue; + +use super::MirContext; +use super::TempRef; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_statement(&mut self, + bcx: Block<'bcx, 'tcx>, + statement: &mir::Statement<'tcx>) + -> Block<'bcx, 'tcx> { + debug!("trans_statement(statement={:?})", statement); + + match statement.kind { + mir::StatementKind::Assign(ref lvalue, ref rvalue) => { + match *lvalue { + mir::Lvalue::Temp(index) => { + let index = index as usize; + match self.temps[index as usize] { + TempRef::Lvalue(tr_dest) => { + self.trans_rvalue(bcx, tr_dest.llval, rvalue) + } + TempRef::Operand(None) => { + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); + self.temps[index] = TempRef::Operand(Some(operand)); + bcx + } + TempRef::Operand(Some(_)) => { + bcx.tcx().sess.span_bug( + statement.span, + &format!("operand {:?} already assigned", rvalue)); + } + } + } + _ => { + let tr_dest = self.trans_lvalue(bcx, lvalue); + self.trans_rvalue(bcx, tr_dest.llval, rvalue) + } + } + } + + mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + glue::drop_ty(bcx, tr_lvalue.llval, ty, DebugLoc::None) + } + + mir::StatementKind::Drop(mir::DropKind::Free, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + let content_ty = ty.builtin_deref(true, LvaluePreference::NoPreference); + let content_ty = content_ty.unwrap().ty; + glue::trans_exchange_free_ty(bcx, tr_lvalue.llval, content_ty, DebugLoc::None) + } + } + } +} diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index 04854501312bf..fa37b00553982 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -52,6 +52,7 @@ mod llrepr; mod machine; mod _match; mod meth; +mod mir; mod monomorphize; mod tvec; mod type_; diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 694bb0e15ac79..cd7012cd4ec6d 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -133,7 +133,8 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // subtyping doesn't matter here, as the value is some kind of scalar demand::eqtype(fcx, pat.span, expected, lhs_ty); } - hir::PatEnum(..) | hir::PatIdent(..) if pat_is_resolved_const(&tcx.def_map, pat) => { + hir::PatEnum(..) | hir::PatIdent(..) + if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => { let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id(); let const_scheme = tcx.lookup_item_type(const_did); assert!(const_scheme.generics.is_empty()); @@ -149,7 +150,7 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, // is good enough. demand::suptype(fcx, pat.span, expected, const_ty); } - hir::PatIdent(bm, ref path, ref sub) if pat_is_binding(&tcx.def_map, pat) => { + hir::PatIdent(bm, ref path, ref sub) if pat_is_binding(&tcx.def_map.borrow(), pat) => { let typ = fcx.local_ty(pat.span, pat.id); match bm { hir::BindByRef(mutbl) => { @@ -410,7 +411,7 @@ pub fn check_dereferencable<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, inner: &hir::Pat) -> bool { let fcx = pcx.fcx; let tcx = pcx.fcx.ccx.tcx; - if pat_is_binding(&tcx.def_map, inner) { + if pat_is_binding(&tcx.def_map.borrow(), inner) { let expected = fcx.infcx().shallow_resolve(expected); expected.builtin_deref(true, ty::NoPreference).map_or(true, |mt| match mt.ty.sty { ty::TyTrait(_) => { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f085ce23e3f0c..cfa32bc073a1f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -164,11 +164,6 @@ pub struct Inherited<'a, 'tcx: 'a> { tables: &'a RefCell>, - // A mapping from each fn's id to its signature, with all bound - // regions replaced with free ones. Unlike the other tables, this - // one is never copied into the tcx: it is only used by regionck. - fn_sig_map: RefCell>>>, - // When we process a call like `c()` where `c` is a closure type, // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or // `FnOnce` closure. In that case, we defer full resolution of the @@ -314,7 +309,6 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), true), locals: RefCell::new(NodeMap()), tables: tables, - fn_sig_map: RefCell::new(NodeMap()), deferred_call_resolutions: RefCell::new(DefIdMap()), deferred_cast_checks: RefCell::new(Vec::new()), } @@ -535,7 +529,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add pattern bindings. fn visit_pat(&mut self, p: &'tcx hir::Pat) { if let hir::PatIdent(_, ref path1, _) = p.node { - if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map, p) { + if pat_util::pat_is_binding(&self.fcx.ccx.tcx.def_map.borrow(), p) { let var_ty = self.assign(p.span, p.id, None); self.fcx.require_type_is_sized(var_ty, p.span, @@ -620,22 +614,13 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, ccx: ccx }; - // Remember return type so that regionck can access it later. - let mut fn_sig_tys: Vec = - arg_tys.iter() - .cloned() - .collect(); - if let ty::FnConverging(ret_ty) = ret_ty { fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType); - fn_sig_tys.push(ret_ty); // FIXME(#25759) just take implied bounds from the arguments } - debug!("fn-sig-map: fn_id={} fn_sig_tys={:?}", - fn_id, - fn_sig_tys); + debug!("fn-sig-map: fn_id={} fn_sig={:?}", fn_id, fn_sig); - inherited.fn_sig_map.borrow_mut().insert(fn_id, fn_sig_tys); + inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig.clone()); { let mut visit = GatherLocalsVisitor { fcx: &fcx, }; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 6e60f926b7cc9..3cdc9b559446e 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -284,19 +284,32 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { // When we enter a function, we can derive debug!("visit_fn_body(id={})", id); - let fn_sig_map = self.fcx.inh.fn_sig_map.borrow(); - let fn_sig = match fn_sig_map.get(&id) { - Some(f) => f, - None => { - self.tcx().sess.bug( - &format!("No fn-sig entry for id={}", id)); + let fn_sig = { + let fn_sig_map = &self.infcx().tables.borrow().liberated_fn_sigs; + match fn_sig_map.get(&id) { + Some(f) => f.clone(), + None => { + self.tcx().sess.bug( + &format!("No fn-sig entry for id={}", id)); + } } }; let old_region_bounds_pairs_len = self.region_bound_pairs.len(); + // Collect the types from which we create inferred bounds. + // For the return type, if diverging, substitute `bool` just + // because it will have no effect. + // + // FIXME(#25759) return types should not be implied bounds + let fn_sig_tys: Vec<_> = + fn_sig.inputs.iter() + .cloned() + .chain(Some(fn_sig.output.unwrap_or(self.tcx().types.bool))) + .collect(); + let old_body_id = self.set_body_id(body.id); - self.relate_free_regions(&fn_sig[..], body.id, span); + self.relate_free_regions(&fn_sig_tys[..], body.id, span); link_fn_args(self, self.tcx().region_maps.node_extent(body.id), &fn_decl.inputs[..]); diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 2c18a245159cf..5b1fafe09fac9 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) { wbcx.visit_expr(e); wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); + wbcx.visit_liberated_fn_sigs(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -56,13 +57,14 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, wbcx.visit_pat(&*arg.pat); // Privacy needs the type for the whole pattern, not just each binding - if !pat_util::pat_is_binding(&fcx.tcx().def_map, &*arg.pat) { + if !pat_util::pat_is_binding(&fcx.tcx().def_map.borrow(), &*arg.pat) { wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.pat.id); } } wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); + wbcx.visit_liberated_fn_sigs(); } /////////////////////////////////////////////////////////////////////////// @@ -361,6 +363,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_liberated_fn_sigs(&self) { + for (&node_id, fn_sig) in self.fcx.inh.tables.borrow().liberated_fn_sigs.iter() { + let fn_sig = self.resolve(fn_sig, ResolvingFnSig(node_id)); + self.tcx().tables.borrow_mut().liberated_fn_sigs.insert(node_id, fn_sig.clone()); + } + } + fn resolve>(&self, t: &T, reason: ResolveReason) -> T { t.fold_with(&mut Resolver::new(self.fcx, reason)) } @@ -376,6 +385,7 @@ enum ResolveReason { ResolvingPattern(Span), ResolvingUpvar(ty::UpvarId), ResolvingClosure(DefId), + ResolvingFnSig(ast::NodeId), } impl ResolveReason { @@ -387,6 +397,9 @@ impl ResolveReason { ResolvingUpvar(upvar_id) => { tcx.expr_span(upvar_id.closure_expr_id) } + ResolvingFnSig(id) => { + tcx.map.span(id) + } ResolvingClosure(did) => { if let Some(node_id) = tcx.map.as_local_node_id(did) { tcx.expr_span(node_id) @@ -463,6 +476,16 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span_err!(self.tcx.sess, span, E0196, "cannot determine a type for this closure") } + + ResolvingFnSig(id) => { + // any failures here should also fail when + // resolving the patterns, closure types, or + // something else. + let span = self.reason.span(self.tcx); + self.tcx.sess.delay_span_bug( + span, + &format!("cannot resolve some aspect of fn sig for {:?}", id)); + } } } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4955951d36e84..f9a138f8fd7af 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -105,6 +105,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, search_paths: search_paths, crate_types: vec!(config::CrateTypeRlib), lint_opts: vec!((warning_lint, lint::Allow)), + lint_cap: Some(lint::Allow), externs: externs, target_triple: triple.unwrap_or(config::host_triple().to_string()), cfg: config::parse_cfgspecs(cfgs), @@ -145,7 +146,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, &arenas, &name, resolve::MakeGlobMap::No, - |tcx, analysis| { + |tcx, _, analysis| { let ty::CrateAnalysis { exported_items, public_items, .. } = analysis; // Convert from a NodeId set to a DefId set since we don't always have easy access diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 1957f1efa4774..a09ca95dceaa4 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -331,18 +331,18 @@ pub fn unindent(s: &str) -> String { min_indent } else { saw_first_line = true; - let mut spaces = 0; + let mut whitespace = 0; line.chars().all(|char| { - // Only comparing against space because I wouldn't - // know what to do with mixed whitespace chars - if char == ' ' { - spaces += 1; + // Compare against either space or tab, ignoring whether they + // are mixed or not + if char == ' ' || char == '\t' { + whitespace += 1; true } else { false } }); - cmp::min(min_indent, spaces) + cmp::min(min_indent, whitespace) } }); @@ -407,4 +407,22 @@ mod unindent_tests { let r = unindent(&s); assert_eq!(r, "line1\n\n line2"); } + + #[test] + fn should_unindent_tabs() { + let s = "\tline1\n\tline2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } + + #[test] + fn should_trim_mixed_indentation() { + let s = "\t line1\n\t line2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + + let s = " \tline1\n \tline2".to_string(); + let r = unindent(&s); + assert_eq!(r, "line1\nline2"); + } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 7c6add80337c0..965226f6355c2 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -601,6 +601,9 @@ impl HashMap /// Returns the number of elements the map can hold without reallocating. /// + /// This number is a lower bound; the `HashMap` might be able to hold + /// more, but is guaranteed to be able to hold at least this many. + /// /// # Examples /// /// ``` diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 0559849f6a647..b9a58a117643a 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -48,7 +48,8 @@ //! The path APIs are built around the notion of "components", which roughly //! correspond to the substrings between path separators (`/` and, on Windows, //! `\`). The APIs for path parsing are largely specified in terms of the path's -//! components, so it's important to clearly understand how those are determined. +//! components, so it's important to clearly understand how those are +//! determined. //! //! A path can always be reconstructed into an *equivalent* path by //! putting together its components via `push`. Syntactically, the @@ -191,10 +192,9 @@ mod platform { // \\?\UNC\server\share path = &path[4..]; let (server, share) = match parse_two_comps(path, is_verbatim_sep) { - Some((server, share)) => (u8_slice_as_os_str(server), - u8_slice_as_os_str(share)), - None => (u8_slice_as_os_str(path), - u8_slice_as_os_str(&[])), + Some((server, share)) => + (u8_slice_as_os_str(server), u8_slice_as_os_str(share)), + None => (u8_slice_as_os_str(path), u8_slice_as_os_str(&[])), }; return Some(VerbatimUNC(server, share)); } else { @@ -207,7 +207,7 @@ mod platform { return Some(VerbatimDisk(c.to_ascii_uppercase())); } } - let slice = &path[.. idx.unwrap_or(path.len())]; + let slice = &path[..idx.unwrap_or(path.len())]; return Some(Verbatim(u8_slice_as_os_str(slice))); } } else if path.starts_with(b".\\") { @@ -220,10 +220,9 @@ mod platform { match parse_two_comps(path, is_sep_byte) { Some((server, share)) if !server.is_empty() && !share.is_empty() => { // \\server\share - return Some(UNC(u8_slice_as_os_str(server), - u8_slice_as_os_str(share))); + return Some(UNC(u8_slice_as_os_str(server), u8_slice_as_os_str(share))); } - _ => () + _ => (), } } else if path.len() > 1 && path[1] == b':' { // C: @@ -238,11 +237,11 @@ mod platform { fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { let first = match path.iter().position(|x| f(*x)) { None => return None, - Some(x) => &path[.. x] + Some(x) => &path[..x], }; - path = &path[(first.len()+1)..]; + path = &path[(first.len() + 1)..]; let idx = path.iter().position(|x| f(*x)); - let second = &path[.. idx.unwrap_or(path.len())]; + let second = &path[..idx.unwrap_or(path.len())]; Some((first, second)) } } @@ -299,15 +298,25 @@ impl<'a> Prefix<'a> { } match *self { Verbatim(x) => 4 + os_str_len(x), - VerbatimUNC(x,y) => 8 + os_str_len(x) + - if os_str_len(y) > 0 { 1 + os_str_len(y) } - else { 0 }, + VerbatimUNC(x, y) => { + 8 + os_str_len(x) + + if os_str_len(y) > 0 { + 1 + os_str_len(y) + } else { + 0 + } + }, VerbatimDisk(_) => 6, - UNC(x,y) => 2 + os_str_len(x) + - if os_str_len(y) > 0 { 1 + os_str_len(y) } - else { 0 }, + UNC(x, y) => { + 2 + os_str_len(x) + + if os_str_len(y) > 0 { + 1 + os_str_len(y) + } else { + 0 + } + }, DeviceNS(x) => 4 + os_str_len(x), - Disk(_) => 2 + Disk(_) => 2, } } @@ -368,14 +377,18 @@ pub const MAIN_SEPARATOR: char = platform::MAIN_SEP; // Iterate through `iter` while it matches `prefix`; return `None` if `prefix` // is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving // `iter` after having exhausted `prefix`. -fn iter_after(mut iter: I, mut prefix: J) -> Option where - I: Iterator + Clone, J: Iterator, A: PartialEq +fn iter_after(mut iter: I, mut prefix: J) -> Option + where I: Iterator + Clone, + J: Iterator, + A: PartialEq { loop { let mut iter_next = iter.clone(); match (iter_next.next(), prefix.next()) { (Some(x), Some(y)) => { - if x != y { return None } + if x != y { + return None; + } } (Some(_), None) => return Some(iter), (None, None) => return Some(iter), @@ -399,14 +412,20 @@ unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { /// Says whether the first byte after the prefix is a separator. fn has_physical_root(s: &[u8], prefix: Option) -> bool { - let path = if let Some(p) = prefix { &s[p.len()..] } else { s }; + let path = if let Some(p) = prefix { + &s[p.len()..] + } else { + s + }; !path.is_empty() && is_sep_byte(path[0]) } // basic workhorse for splitting stem and extension fn split_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) { unsafe { - if os_str_as_u8_slice(file) == b".." { return (Some(file), None) } + if os_str_as_u8_slice(file) == b".." { + return (Some(file), None); + } // The unsafety here stems from converting between &OsStr and &[u8] // and back. This is safe to do because (1) we only look at ASCII @@ -589,7 +608,7 @@ pub struct Components<'a> { #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a> { - inner: Components<'a> + inner: Components<'a>, } impl<'a> Components<'a> { @@ -617,8 +636,16 @@ impl<'a> Components<'a> { // Given the iteration so far, how much of the pre-State::Body path is left? #[inline] fn len_before_body(&self) -> usize { - let root = if self.front <= State::StartDir && self.has_physical_root { 1 } else { 0 }; - let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 1 } else { 0 }; + let root = if self.front <= State::StartDir && self.has_physical_root { + 1 + } else { + 0 + }; + let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { + 1 + } else { + 0 + }; self.prefix_remaining() + root + cur_dir } @@ -653,28 +680,38 @@ impl<'a> Components<'a> { #[stable(feature = "rust1", since = "1.0.0")] pub fn as_path(&self) -> &'a Path { let mut comps = self.clone(); - if comps.front == State::Body { comps.trim_left(); } - if comps.back == State::Body { comps.trim_right(); } + if comps.front == State::Body { + comps.trim_left(); + } + if comps.back == State::Body { + comps.trim_right(); + } unsafe { Path::from_u8_slice(comps.path) } } /// Is the *original* path rooted? fn has_root(&self) -> bool { - if self.has_physical_root { return true } + if self.has_physical_root { + return true; + } if let Some(p) = self.prefix { - if p.has_implicit_root() { return true } + if p.has_implicit_root() { + return true; + } } false } /// Should the normalized path include a leading . ? fn include_cur_dir(&self) -> bool { - if self.has_root() { return false } + if self.has_root() { + return false; + } let mut iter = self.path[self.prefix_len()..].iter(); match (iter.next(), iter.next()) { (Some(&b'.'), None) => true, (Some(&b'.'), Some(&b)) => self.is_sep_byte(b), - _ => false + _ => false, } } @@ -687,7 +724,7 @@ impl<'a> Components<'a> { // separately via `include_cur_dir` b".." => Some(Component::ParentDir), b"" => None, - _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })) + _ => Some(Component::Normal(unsafe { u8_slice_as_os_str(comp) })), } } @@ -697,7 +734,7 @@ impl<'a> Components<'a> { debug_assert!(self.front == State::Body); let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) { None => (0, self.path), - Some(i) => (1, &self.path[.. i]), + Some(i) => (1, &self.path[..i]), }; (comp.len() + extra, self.parse_single_component(comp)) } @@ -708,8 +745,8 @@ impl<'a> Components<'a> { debug_assert!(self.back == State::Body); let start = self.len_before_body(); let (extra, comp) = match self.path[start..].iter().rposition(|b| self.is_sep_byte(*b)) { - None => (0, &self.path[start ..]), - Some(i) => (1, &self.path[start + i + 1 ..]), + None => (0, &self.path[start..]), + Some(i) => (1, &self.path[start + i + 1..]), }; (comp.len() + extra, self.parse_single_component(comp)) } @@ -721,7 +758,7 @@ impl<'a> Components<'a> { if comp.is_some() { return; } else { - self.path = &self.path[size ..]; + self.path = &self.path[size..]; } } } @@ -733,7 +770,7 @@ impl<'a> Components<'a> { if comp.is_some() { return; } else { - self.path = &self.path[.. self.path.len() - size]; + self.path = &self.path[..self.path.len() - size]; } } } @@ -807,12 +844,12 @@ impl<'a> Iterator for Components<'a> { State::Prefix if self.prefix_len() > 0 => { self.front = State::StartDir; debug_assert!(self.prefix_len() <= self.path.len()); - let raw = &self.path[.. self.prefix_len()]; - self.path = &self.path[self.prefix_len() .. ]; + let raw = &self.path[..self.prefix_len()]; + self.path = &self.path[self.prefix_len()..]; return Some(Component::Prefix(PrefixComponent { raw: unsafe { u8_slice_as_os_str(raw) }, - parsed: self.prefix.unwrap() - })) + parsed: self.prefix.unwrap(), + })); } State::Prefix => { self.front = State::StartDir; @@ -822,26 +859,28 @@ impl<'a> Iterator for Components<'a> { if self.has_physical_root { debug_assert!(!self.path.is_empty()); self.path = &self.path[1..]; - return Some(Component::RootDir) + return Some(Component::RootDir); } else if let Some(p) = self.prefix { if p.has_implicit_root() && !p.is_verbatim() { - return Some(Component::RootDir) + return Some(Component::RootDir); } } else if self.include_cur_dir() { debug_assert!(!self.path.is_empty()); self.path = &self.path[1..]; - return Some(Component::CurDir) + return Some(Component::CurDir); } } State::Body if !self.path.is_empty() => { let (size, comp) = self.parse_next_component(); - self.path = &self.path[size ..]; - if comp.is_some() { return comp } + self.path = &self.path[size..]; + if comp.is_some() { + return comp; + } } State::Body => { self.front = State::Done; } - State::Done => unreachable!() + State::Done => unreachable!(), } } None @@ -855,8 +894,10 @@ impl<'a> DoubleEndedIterator for Components<'a> { match self.back { State::Body if self.path.len() > self.len_before_body() => { let (size, comp) = self.parse_next_component_back(); - self.path = &self.path[.. self.path.len() - size]; - if comp.is_some() { return comp } + self.path = &self.path[..self.path.len() - size]; + if comp.is_some() { + return comp; + } } State::Body => { self.back = State::StartDir; @@ -864,29 +905,29 @@ impl<'a> DoubleEndedIterator for Components<'a> { State::StartDir => { self.back = State::Prefix; if self.has_physical_root { - self.path = &self.path[.. self.path.len() - 1]; - return Some(Component::RootDir) + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::RootDir); } else if let Some(p) = self.prefix { if p.has_implicit_root() && !p.is_verbatim() { - return Some(Component::RootDir) + return Some(Component::RootDir); } } else if self.include_cur_dir() { - self.path = &self.path[.. self.path.len() - 1]; - return Some(Component::CurDir) + self.path = &self.path[..self.path.len() - 1]; + return Some(Component::CurDir); } } State::Prefix if self.prefix_len() > 0 => { self.back = State::Done; return Some(Component::Prefix(PrefixComponent { raw: unsafe { u8_slice_as_os_str(self.path) }, - parsed: self.prefix.unwrap() - })) + parsed: self.prefix.unwrap(), + })); } State::Prefix => { self.back = State::Done; - return None + return None; } - State::Done => unreachable!() + State::Done => unreachable!(), } } None @@ -943,7 +984,7 @@ impl<'a> cmp::Ord for Components<'a> { #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct PathBuf { - inner: OsString + inner: OsString, } impl PathBuf { @@ -984,10 +1025,8 @@ impl PathBuf { // in the special case of `C:` on Windows, do *not* add a separator { let comps = self.components(); - if comps.prefix_len() > 0 && - comps.prefix_len() == comps.path.len() && - comps.prefix.unwrap().is_drive() - { + if comps.prefix_len() > 0 && comps.prefix_len() == comps.path.len() && + comps.prefix.unwrap().is_drive() { need_sep = false } } @@ -1020,7 +1059,7 @@ impl PathBuf { self.as_mut_vec().truncate(len); true } - None => false + None => false, } } @@ -1067,7 +1106,9 @@ impl PathBuf { } fn _set_extension(&mut self, extension: &OsStr) -> bool { - if self.file_name().is_none() { return false; } + if self.file_name().is_none() { + return false; + } let mut stem = match self.file_stem() { Some(stem) => stem.to_os_string(), @@ -1166,10 +1207,28 @@ impl<'a> IntoCow<'a, Path> for &'a Path { } } +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From<&'a Path> for Cow<'a, Path> { + #[inline] + fn from(s: &'a Path) -> Cow<'a, Path> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_path", since = "1.6.0")] +impl<'a> From for Cow<'a, Path> { + #[inline] + fn from(s: PathBuf) -> Cow<'a, Path> { + Cow::Owned(s) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ToOwned for Path { type Owned = PathBuf; - fn to_owned(&self) -> PathBuf { self.to_path_buf() } + fn to_owned(&self) -> PathBuf { + self.to_path_buf() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1241,7 +1300,7 @@ impl Into for PathBuf { /// #[stable(feature = "rust1", since = "1.0.0")] pub struct Path { - inner: OsStr + inner: OsStr, } impl Path { @@ -1364,8 +1423,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn is_absolute(&self) -> bool { - self.has_root() && - (cfg!(unix) || self.prefix().is_some()) + self.has_root() && (cfg!(unix) || self.prefix().is_some()) } /// A path is *relative* if it is not absolute. @@ -1412,7 +1470,7 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn has_root(&self) -> bool { - self.components().has_root() + self.components().has_root() } /// The path without its final component, if any. @@ -1436,11 +1494,13 @@ impl Path { pub fn parent(&self) -> Option<&Path> { let mut comps = self.components(); let comp = comps.next_back(); - comp.and_then(|p| match p { - Component::Normal(_) | - Component::CurDir | - Component::ParentDir => Some(comps.as_path()), - _ => None + comp.and_then(|p| { + match p { + Component::Normal(_) | + Component::CurDir | + Component::ParentDir => Some(comps.as_path()), + _ => None, + } }) } @@ -1462,9 +1522,11 @@ impl Path { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn file_name(&self) -> Option<&OsStr> { - self.components().next_back().and_then(|p| match p { - Component::Normal(p) => Some(p.as_ref()), - _ => None + self.components().next_back().and_then(|p| { + match p { + Component::Normal(p) => Some(p.as_ref()), + _ => None, + } }) } @@ -1474,8 +1536,7 @@ impl Path { /// returns false), then `relative_from` returns `None`. #[unstable(feature = "path_relative_from", reason = "see #23284", issue = "23284")] - pub fn relative_from<'a, P: ?Sized + AsRef>(&'a self, base: &'a P) -> Option<&Path> - { + pub fn relative_from<'a, P: ?Sized + AsRef>(&'a self, base: &'a P) -> Option<&Path> { self._relative_from(base.as_ref()) } @@ -1799,7 +1860,7 @@ impl fmt::Debug for Path { /// Helper struct for safely printing paths with `format!()` and `{}` #[stable(feature = "rust1", since = "1.0.0")] pub struct Display<'a> { - path: &'a Path + path: &'a Path, } #[stable(feature = "rust1", since = "1.0.0")] @@ -1851,32 +1912,44 @@ impl cmp::Ord for Path { #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for Path { - fn as_ref(&self) -> &Path { self } + fn as_ref(&self) -> &Path { + self + } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for OsStr { - fn as_ref(&self) -> &Path { Path::new(self) } + fn as_ref(&self) -> &Path { + Path::new(self) + } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for OsString { - fn as_ref(&self) -> &Path { Path::new(self) } + fn as_ref(&self) -> &Path { + Path::new(self) + } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for str { - fn as_ref(&self) -> &Path { Path::new(self) } + fn as_ref(&self) -> &Path { + Path::new(self) + } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for String { - fn as_ref(&self) -> &Path { Path::new(self) } + fn as_ref(&self) -> &Path { + Path::new(self) + } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRef for PathBuf { - fn as_ref(&self) -> &Path { self } + fn as_ref(&self) -> &Path { + self + } } #[stable(feature = "path_into_iter", since = "1.6.0")] @@ -1893,6 +1966,29 @@ impl<'a> IntoIterator for &'a Path { fn into_iter(self) -> Iter<'a> { self.iter() } } +macro_rules! impl_eq { + ($lhs:ty, $rhs: ty) => { + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl<'a, 'b> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { ::eq(self, other) } + } + + #[stable(feature = "partialeq_path", since = "1.6.0")] + impl<'a, 'b> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { ::eq(self, other) } + } + + } +} + +impl_eq!(PathBuf, Path); +impl_eq!(PathBuf, &'a Path); +impl_eq!(Cow<'a, Path>, Path); +impl_eq!(Cow<'a, Path>, &'b Path); +impl_eq!(Cow<'a, Path>, PathBuf); + #[cfg(test)] mod tests { use super::*; @@ -2002,6 +2098,26 @@ mod tests { assert_eq!(static_cow_path, owned_cow_path); } + #[test] + fn into() { + use borrow::Cow; + + let static_path = Path::new("/home/foo"); + let static_cow_path: Cow<'static, Path> = static_path.into(); + let pathbuf = PathBuf::from("/home/foo"); + + { + let path: &Path = &pathbuf; + let borrowed_cow_path: Cow = path.into(); + + assert_eq!(static_cow_path, borrowed_cow_path); + } + + let owned_cow_path: Cow<'static, Path> = pathbuf.into(); + + assert_eq!(static_cow_path, owned_cow_path); + } + #[test] #[cfg(unix)] pub fn test_decompositions_unix() { @@ -2931,20 +3047,26 @@ mod tests { tp!("C:a\\b\\c", "C:d", "C:d"); tp!("C:", r"a\b\c", r"C:a\b\c"); tp!("C:", r"..\a", r"C:..\a"); - tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); + tp!("\\\\server\\share\\foo", + "bar", + "\\\\server\\share\\foo\\bar"); tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); - tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); + tp!("\\\\?\\UNC\\server\\share\\foo", + "bar", + "\\\\?\\UNC\\server\\share\\foo\\bar"); tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); // Note: modified from old path API tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); - tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); + tp!("C:\\a", + "\\\\?\\UNC\\server\\share", + "\\\\?\\UNC\\server\\share"); tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); tp!("\\\\.\\foo\\bar", "C:a", "C:a"); // again, not sure about the following, but I'm assuming \\.\ should be verbatim @@ -2997,9 +3119,15 @@ mod tests { tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); - tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); - tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); - tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); + tp!("\\\\?\\UNC\\server\\share\\a\\b", + "\\\\?\\UNC\\server\\share\\a", + true); + tp!("\\\\?\\UNC\\server\\share\\a", + "\\\\?\\UNC\\server\\share\\", + true); + tp!("\\\\?\\UNC\\server\\share", + "\\\\?\\UNC\\server\\share", + false); tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); tp!("\\\\.\\a", "\\\\.\\a", false); @@ -3065,11 +3193,36 @@ mod tests { tfe!(".", "foo", ".", false); tfe!("foo/", "bar", "foo.bar", true); tfe!("foo/.", "bar", "foo.bar", true); - tfe!("..", "foo", "..", false); + tfe!("..", "foo", "..", false); tfe!("foo/..", "bar", "foo/..", false); tfe!("/", "foo", "/", false); } + #[test] + fn test_eq_recievers() { + use borrow::Cow; + + let borrowed: &Path = Path::new("foo/bar"); + let mut owned: PathBuf = PathBuf::new(); + owned.push("foo"); + owned.push("bar"); + let borrowed_cow: Cow = borrowed.into(); + let owned_cow: Cow = owned.clone().into(); + + macro_rules! t { + ($($current:expr),+) => { + $( + assert_eq!($current, borrowed); + assert_eq!($current, owned); + assert_eq!($current, borrowed_cow); + assert_eq!($current, owned_cow); + )+ + } + } + + t!(borrowed, owned, borrowed_cow, owned_cow); + } + #[test] pub fn test_compare() { use hash::{Hash, Hasher, SipHasher}; diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index 984e73f85f610..bdfbb7a49330e 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -139,9 +139,9 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let (constraint, _str_style) = panictry!(p.parse_str()); - if constraint.starts_with("=") { + if constraint.starts_with("=") && !constraint.contains("*") { cx.span_err(p.last_span, "input operand constraint contains '='"); - } else if constraint.starts_with("+") { + } else if constraint.starts_with("+") && !constraint.contains("*") { cx.span_err(p.last_span, "input operand constraint contains '+'"); } diff --git a/src/test/compile-fail/issue-28971.rs b/src/test/compile-fail/issue-28971.rs new file mode 100644 index 0000000000000..1d14b71a40e4f --- /dev/null +++ b/src/test/compile-fail/issue-28971.rs @@ -0,0 +1,27 @@ +// 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. + +// This should not cause an ICE + +enum Foo { + Bar(u8) +} +fn main(){ + foo(|| { + match Foo::Bar(1) { + Foo::Baz(..) => (), //~ ERROR no associated + _ => (), + } + }); +} + +fn foo(f: F) where F: FnMut() { + f(); +} diff --git a/src/test/compile-fail/liveness-dead.rs b/src/test/compile-fail/liveness-dead.rs index dc7b0fc4fd0f2..ddd8fc68c43a3 100644 --- a/src/test/compile-fail/liveness-dead.rs +++ b/src/test/compile-fail/liveness-dead.rs @@ -27,4 +27,14 @@ fn f3() { x = 4; //~ ERROR: value assigned to `x` is never read } +fn f4(mut x: i32) { //~ ERROR: value passed to `x` is never read + x = 4; + x.clone(); +} + +fn f5(mut x: i32) { + x.clone(); + x = 4; //~ ERROR: value assigned to `x` is never read +} + fn main() {} diff --git a/src/test/run-make/compiler-rt-works-on-mingw/Makefile b/src/test/run-make/compiler-rt-works-on-mingw/Makefile new file mode 100644 index 0000000000000..4ec54f73e67a5 --- /dev/null +++ b/src/test/run-make/compiler-rt-works-on-mingw/Makefile @@ -0,0 +1,17 @@ +-include ../tools.mk + +ifneq (,$(findstring MINGW,$(UNAME))) +ifndef IS_MSVC +all: + g++ foo.cpp -c -o $(TMPDIR)/foo.o + ar crus $(TMPDIR)/libfoo.a $(TMPDIR)/foo.o + $(RUSTC) foo.rs -lfoo -lstdc++ + $(call RUN,foo) +else +all: + +endif +else +all: + +endif diff --git a/src/test/run-make/compiler-rt-works-on-mingw/foo.cpp b/src/test/run-make/compiler-rt-works-on-mingw/foo.cpp new file mode 100644 index 0000000000000..aac3ba4220101 --- /dev/null +++ b/src/test/run-make/compiler-rt-works-on-mingw/foo.cpp @@ -0,0 +1,5 @@ +// ignore-license +extern "C" void foo() { + int *a = new int(3); + delete a; +} diff --git a/src/test/run-make/compiler-rt-works-on-mingw/foo.rs b/src/test/run-make/compiler-rt-works-on-mingw/foo.rs new file mode 100644 index 0000000000000..293f9d582945e --- /dev/null +++ b/src/test/run-make/compiler-rt-works-on-mingw/foo.rs @@ -0,0 +1,16 @@ +// Copyright 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. + +extern { fn foo(); } + +pub fn main() { + unsafe { foo(); } + assert_eq!(7f32.powi(3), 343f32); +} diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs index f4031a3aaae1c..f2dd155595ac4 100644 --- a/src/test/run-make/execution-engine/test.rs +++ b/src/test/run-make/execution-engine/test.rs @@ -229,9 +229,9 @@ fn compile_program(input: &str, sysroot: PathBuf) let ast_map = driver::make_map(&sess, &mut hir_forest); driver::phase_3_run_analysis_passes( - &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, analysis| { + &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, mir_map, analysis| { - let trans = driver::phase_4_translate_to_llvm(tcx, analysis); + let trans = driver::phase_4_translate_to_llvm(tcx, &mir_map, analysis); let crates = tcx.sess.cstore.get_used_crates(RequireDynamic); diff --git a/src/test/run-pass/asm-indirect-memory.rs b/src/test/run-pass/asm-indirect-memory.rs new file mode 100644 index 0000000000000..80fd548dfe354 --- /dev/null +++ b/src/test/run-pass/asm-indirect-memory.rs @@ -0,0 +1,39 @@ +// Copyright 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. + +#![feature(asm)] + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn read(ptr: &u32) -> u32 { + let out: u32; + unsafe { + asm!("mov $1, $0" : "=r" (out) : "*m" (ptr)); + } + out +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn write(ptr: &mut u32, val: u32) { + unsafe { + asm!("mov $1, $0" :: "=*m" (ptr), "r" (val)); + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn main() { + let a = 1; + let mut b = 2; + assert_eq!(read(&a), 1); + write(&mut b, 3); + assert_eq!(b, 3); +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +pub fn main() {} diff --git a/src/test/run-pass/issue-24954.rs b/src/test/run-pass/issue-24954.rs new file mode 100644 index 0000000000000..f525274a1dfca --- /dev/null +++ b/src/test/run-pass/issue-24954.rs @@ -0,0 +1,22 @@ +// Copyright 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. + +macro_rules! foo { + ($y:expr) => ({ + $y = 2; + }) +} + +#[allow(unused_variables)] +#[allow(unused_assignments)] +fn main() { + let mut x = 1; + foo!(x); +} diff --git a/src/test/run-pass/issue-29522.rs b/src/test/run-pass/issue-29522.rs new file mode 100644 index 0000000000000..de7c7aee05530 --- /dev/null +++ b/src/test/run-pass/issue-29522.rs @@ -0,0 +1,25 @@ +// Copyright 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. + +// check that we don't accidentally capture upvars just because their name +// occurs in a path + +fn assert_static(_t: T) {} + +mod foo { + pub fn scope() {} +} + +fn main() { + let scope = &mut 0; + assert_static(|| { + foo::scope(); + }); +} diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs new file mode 100644 index 0000000000000..9a06ab78e73b4 --- /dev/null +++ b/src/test/run-pass/mir_trans_spike1.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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. + +// A simple spike test for MIR version of trans. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn sum(x: i32, y: i32) -> i32 { + x + y +} + +fn main() { + let x = sum(22, 44); + assert_eq!(x, 66); + println!("sum()={:?}", x); +} diff --git a/src/test/run-pass/resolve-pseudo-shadowing.rs b/src/test/run-pass/resolve-pseudo-shadowing.rs new file mode 100644 index 0000000000000..071279ae7d818 --- /dev/null +++ b/src/test/run-pass/resolve-pseudo-shadowing.rs @@ -0,0 +1,20 @@ +// Copyright 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. + +// check that type parameters can't "shadow" qualified paths. + +fn check(_c: Clone) { + fn check2() { + <() as std::clone::Clone>::clone(&()); + } + check2(); +} + +fn main() { check(()); } diff --git a/src/test/rustdoc/cap-lints.rs b/src/test/rustdoc/cap-lints.rs new file mode 100644 index 0000000000000..e7f308a6f0b69 --- /dev/null +++ b/src/test/rustdoc/cap-lints.rs @@ -0,0 +1,20 @@ +// Copyright 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. + +// This should fail a normal compile due to non_camel_case_types, +// It should pass a doc-compile as it only needs to type-check and +// therefore should not concern itself with the lints. +#[deny(warnings)] + +// @has cap_lints/struct.foo.html //pre '#[must_use]' +#[must_use] +pub struct foo { + field: i32, +}