diff options
105 files changed, 2408 insertions, 1053 deletions
diff --git a/.travis.yml b/.travis.yml index 9e3225a103a..139f06ec570 100644 --- a/.travis.yml +++ b/.travis.yml @@ -115,6 +115,8 @@ matrix: if: branch = auto - env: IMAGE=cross DEPLOY=1 if: branch = auto + - env: IMAGE=cross2 DEPLOY=1 + if: branch = auto - env: IMAGE=dist-aarch64-linux DEPLOY=1 if: branch = auto - env: IMAGE=dist-android DEPLOY=1 @@ -125,8 +127,6 @@ matrix: if: branch = auto - env: IMAGE=dist-armv7-linux DEPLOY=1 if: branch = auto - - env: IMAGE=dist-fuchsia DEPLOY=1 - if: branch = auto - env: IMAGE=dist-i586-gnu-i686-musl DEPLOY=1 if: branch = auto - env: IMAGE=dist-i686-freebsd DEPLOY=1 diff --git a/src/ci/docker/cross2/Dockerfile b/src/ci/docker/cross2/Dockerfile new file mode 100644 index 00000000000..f5fc06767ce --- /dev/null +++ b/src/ci/docker/cross2/Dockerfile @@ -0,0 +1,52 @@ +FROM ubuntu:16.04 + +COPY scripts/cross-apt-packages.sh /scripts/ +RUN sh /scripts/cross-apt-packages.sh + +RUN apt-get build-dep -y clang llvm && apt-get install -y --no-install-recommends \ + build-essential \ + libedit-dev \ + libgmp-dev \ + libisl-dev \ + libmpc-dev \ + libmpfr-dev \ + ninja-build \ + nodejs \ + python2.7-dev \ + software-properties-common \ + unzip + +RUN apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys 74DA7924C5513486 +RUN add-apt-repository -y 'deb http://apt.dilos.org/dilos dilos2-testing main' + +WORKDIR /tmp +COPY cross2/shared.sh cross2/build-fuchsia-toolchain.sh /tmp/ +COPY cross2/build-solaris-toolchain.sh /tmp/ +RUN /tmp/build-fuchsia-toolchain.sh +RUN /tmp/build-solaris-toolchain.sh x86_64 amd64 solaris-i386 +RUN /tmp/build-solaris-toolchain.sh sparcv9 sparcv9 solaris-sparc + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV \ + AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \ + CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \ + CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \ + AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \ + CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \ + CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++ \ + AR_sparcv9_sun_solaris=sparcv9-sun-solaris2.11-ar \ + CC_sparcv9_sun_solaris=sparcv9-sun-solaris2.11-gcc \ + CXX_sparcv9_sun_solaris=sparcv9-sun-solaris2.11-g++ \ + AR_x86_64_sun_solaris=x86_64-sun-solaris2.11-ar \ + CC_x86_64_sun_solaris=x86_64-sun-solaris2.11-gcc \ + CXX_x86_64_sun_solaris=x86_64-sun-solaris2.11-g++ + +ENV TARGETS=x86_64-unknown-fuchsia +ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia +ENV TARGETS=$TARGETS,sparcv9-sun-solaris +ENV TARGETS=$TARGETS,x86_64-sun-solaris + +ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended +ENV SCRIPT python2.7 ../x.py dist --target $TARGETS diff --git a/src/ci/docker/dist-fuchsia/build-toolchain.sh b/src/ci/docker/cross2/build-fuchsia-toolchain.sh index 756013a235c..756013a235c 100755 --- a/src/ci/docker/dist-fuchsia/build-toolchain.sh +++ b/src/ci/docker/cross2/build-fuchsia-toolchain.sh diff --git a/src/ci/docker/cross2/build-solaris-toolchain.sh b/src/ci/docker/cross2/build-solaris-toolchain.sh new file mode 100755 index 00000000000..ae84cc62b60 --- /dev/null +++ b/src/ci/docker/cross2/build-solaris-toolchain.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Copyright 2016 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +set -ex +source shared.sh + +ARCH=$1 +LIB_ARCH=$2 +APT_ARCH=$3 +BINUTILS=2.28.1 +GCC=6.4.0 + +# First up, build binutils +mkdir binutils +cd binutils + +curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.xz | tar xJf - +mkdir binutils-build +cd binutils-build +hide_output ../binutils-$BINUTILS/configure --target=$ARCH-sun-solaris2.11 +hide_output make -j10 +hide_output make install + +cd ../.. +rm -rf binutils + +# Next, download and install the relevant solaris packages +mkdir solaris +cd solaris + +dpkg --add-architecture $APT_ARCH +apt-get update +apt-get download \ + libc:$APT_ARCH \ + libc-dev:$APT_ARCH \ + libm:$APT_ARCH \ + libm-dev:$APT_ARCH \ + libpthread:$APT_ARCH \ + libpthread-dev:$APT_ARCH \ + libresolv:$APT_ARCH \ + libresolv-dev:$APT_ARCH \ + librt:$APT_ARCH \ + librt-dev:$APT_ARCH \ + libsocket:$APT_ARCH \ + libsocket-dev:$APT_ARCH \ + system-crt:$APT_ARCH \ + system-header:$APT_ARCH + +for deb in *$APT_ARCH.deb; do + dpkg -x $deb . +done + +mkdir /usr/local/$ARCH-sun-solaris2.11/usr +mv usr/include /usr/local/$ARCH-sun-solaris2.11/usr/include +mv usr/lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.11/lib +mv lib/$LIB_ARCH/* /usr/local/$ARCH-sun-solaris2.11/lib + +ln -s /usr/local/$ARCH-sun-solaris2.11/usr/include /usr/local/$ARCH-sun-solaris2.11/sys-include +ln -s /usr/local/$ARCH-sun-solaris2.11/usr/include /usr/local/$ARCH-sun-solaris2.11/include + +cd .. +rm -rf solaris + +# Finally, download and build gcc to target solaris +mkdir gcc +cd gcc + +curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | tar xJf - +cd gcc-$GCC + +mkdir ../gcc-build +cd ../gcc-build +hide_output ../gcc-$GCC/configure \ + --enable-languages=c,c++ \ + --target=$ARCH-sun-solaris2.11 \ + --with-gnu-as \ + --with-gnu-ld \ + --disable-multilib \ + --disable-nls \ + --disable-libgomp \ + --disable-libquadmath \ + --disable-libssp \ + --disable-libvtv \ + --disable-libcilkrts \ + --disable-libada \ + --disable-libsanitizer \ + --disable-libquadmath-support \ + --disable-lto \ + --with-sysroot=/usr/local/$ARCH-sun-solaris2.11 + +hide_output make -j10 +hide_output make install + +cd ../.. +rm -rf gcc diff --git a/src/ci/docker/dist-fuchsia/shared.sh b/src/ci/docker/cross2/shared.sh index e26c6eb6645..e26c6eb6645 100644 --- a/src/ci/docker/dist-fuchsia/shared.sh +++ b/src/ci/docker/cross2/shared.sh diff --git a/src/ci/docker/dist-fuchsia/Dockerfile b/src/ci/docker/dist-fuchsia/Dockerfile deleted file mode 100644 index bcd95924b42..00000000000 --- a/src/ci/docker/dist-fuchsia/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM ubuntu:16.04 - -RUN apt-get update && apt-get build-dep -y clang llvm && apt-get install -y \ - build-essential \ - bzip2 \ - ca-certificates \ - cmake \ - curl \ - file \ - g++ \ - gdb \ - git \ - libedit-dev \ - make \ - ninja-build \ - nodejs \ - python2.7-dev \ - sudo \ - xz-utils \ - unzip - -WORKDIR /tmp -COPY dist-fuchsia/shared.sh dist-fuchsia/build-toolchain.sh /tmp/ -RUN /tmp/build-toolchain.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV \ - AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \ - CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \ - CXX_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang++ \ - AR_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-ar \ - CC_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang \ - CXX_aarch64_unknown_fuchsia=aarch64-unknown-fuchsia-clang++ - -ENV TARGETS=x86_64-unknown-fuchsia -ENV TARGETS=$TARGETS,aarch64-unknown-fuchsia - -ENV RUST_CONFIGURE_ARGS --target=$TARGETS --enable-extended -ENV SCRIPT python2.7 ../x.py dist --target $TARGETS \ No newline at end of file diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 3b7dbd813cf..9481cd4e1a4 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -52,8 +52,10 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize; /// also destroyed. /// /// Shared references in Rust disallow mutation by default, and `Arc` is no -/// exception. If you need to mutate through an `Arc`, use [`Mutex`][mutex], -/// [`RwLock`][rwlock], or one of the [`Atomic`][atomic] types. +/// exception: you cannot generally obtain a mutable reference to something +/// inside an `Arc`. If you need to mutate through an `Arc`, use +/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] +/// types. /// /// ## Thread Safety /// diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 553980d463f..2f8620cc750 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -19,7 +19,7 @@ //! given value is destroyed, the pointed-to value is also destroyed. //! //! Shared references in Rust disallow mutation by default, and [`Rc`] -//! is no exception: you cannot obtain a mutable reference to +//! is no exception: you cannot generally obtain a mutable reference to //! something inside an [`Rc`]. If you need mutability, put a [`Cell`] //! or [`RefCell`] inside the [`Rc`]; see [an example of mutability //! inside an Rc][mutability]. diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs index b594c886b64..60b9eeb1283 100644 --- a/src/libcore/fmt/builders.rs +++ b/src/libcore/fmt/builders.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use fmt::{self, FlagV1}; +use fmt; struct PadAdapter<'a, 'b: 'a> { fmt: &'a mut fmt::Formatter<'b>, @@ -140,7 +140,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -233,7 +233,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -277,7 +277,7 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } @@ -519,6 +519,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { } fn is_pretty(&self) -> bool { - self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + self.fmt.alternate() } } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 6c251b9eb09..1e45af5b105 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -322,7 +322,6 @@ impl<'a> ArgumentV1<'a> { // flags available in the v1 format of format_args #[derive(Copy, Clone)] -#[allow(dead_code)] // SignMinus isn't currently used enum FlagV1 { SignPlus, SignMinus, Alternate, SignAwareZeroPad, } impl<'a> Arguments<'a> { @@ -427,7 +426,7 @@ impl<'a> Display for Arguments<'a> { } } -/// Format trait for the `?` character. +/// `?` formatting. /// /// `Debug` should format the output in a programmer-facing, debugging context. /// @@ -593,7 +592,7 @@ pub trait Display { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `o` character. +/// `o` formatting. /// /// The `Octal` trait should format its output as a number in base-8. /// @@ -640,7 +639,7 @@ pub trait Octal { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `b` character. +/// `b` formatting. /// /// The `Binary` trait should format its output as a number in binary. /// @@ -687,7 +686,7 @@ pub trait Binary { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `x` character. +/// `x` formatting. /// /// The `LowerHex` trait should format its output as a number in hexadecimal, with `a` through `f` /// in lower case. @@ -735,7 +734,7 @@ pub trait LowerHex { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `X` character. +/// `X` formatting. /// /// The `UpperHex` trait should format its output as a number in hexadecimal, with `A` through `F` /// in upper case. @@ -783,7 +782,7 @@ pub trait UpperHex { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `p` character. +/// `p` formatting. /// /// The `Pointer` trait should format its output as a memory location. This is commonly presented /// as hexadecimal. @@ -828,7 +827,7 @@ pub trait Pointer { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `e` character. +/// `e` formatting. /// /// The `LowerExp` trait should format its output in scientific notation with a lower-case `e`. /// @@ -871,7 +870,7 @@ pub trait LowerExp { fn fmt(&self, f: &mut Formatter) -> Result; } -/// Format trait for the `E` character. +/// `E` formatting. /// /// The `UpperExp` trait should format its output in scientific notation with an upper-case `E`. /// @@ -1276,7 +1275,7 @@ impl<'a> Formatter<'a> { write(self.buf, fmt) } - /// Flags for formatting (packed version of rt::Flag) + /// Flags for formatting #[stable(feature = "rust1", since = "1.0.0")] pub fn flags(&self) -> u32 { self.flags } diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 465d31b5f49..524f4508c9b 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -119,7 +119,9 @@ pub fn hint_core_should_pause() /// A boolean type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a `bool`. +/// This type has the same in-memory representation as a [`bool`]. +/// +/// [`bool`]: ../../../std/primitive.bool.html #[cfg(target_has_atomic = "8")] #[stable(feature = "rust1", since = "1.0.0")] pub struct AtomicBool { @@ -246,11 +248,13 @@ impl AtomicBool { AtomicBool { v: UnsafeCell::new(v as u8) } } - /// Returns a mutable reference to the underlying `bool`. + /// Returns a mutable reference to the underlying [`bool`]. /// /// This is safe because the mutable reference guarantees that no other threads are /// concurrently accessing the atomic data. /// + /// [`bool`]: ../../../std/primitive.bool.html + /// /// # Examples /// /// ``` @@ -369,7 +373,7 @@ impl AtomicBool { unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// The return value is always the previous value. If it is equal to `current`, then the value /// was updated. @@ -378,6 +382,7 @@ impl AtomicBool { /// ordering of this operation. /// /// [`Ordering`]: enum.Ordering.html + /// [`bool`]: ../../../std/primitive.bool.html /// /// # Examples /// @@ -401,7 +406,7 @@ impl AtomicBool { } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// The return value is a result indicating whether the new value was written and containing /// the previous value. On success this value is guaranteed to be equal to `current`. @@ -412,6 +417,7 @@ impl AtomicBool { /// operation fails. The failure ordering can't be [`Release`] or [`AcqRel`] and must /// be equivalent or weaker than the success ordering. /// + /// [`bool`]: ../../../std/primitive.bool.html /// [`Ordering`]: enum.Ordering.html /// [`Release`]: enum.Ordering.html#variant.Release /// [`AcqRel`]: enum.Ordering.html#variant.Release @@ -452,7 +458,7 @@ impl AtomicBool { } } - /// Stores a value into the `bool` if the current value is the same as the `current` value. + /// Stores a value into the [`bool`] if the current value is the same as the `current` value. /// /// Unlike [`compare_exchange`], this function is allowed to spuriously fail even when the /// comparison succeeds, which can result in more efficient code on some platforms. The @@ -465,6 +471,7 @@ impl AtomicBool { /// failure ordering can't be [`Release`] or [`AcqRel`] and must be equivalent or /// weaker than the success ordering. /// + /// [`bool`]: ../../../std/primitive.bool.html /// [`compare_exchange`]: #method.compare_exchange /// [`Ordering`]: enum.Ordering.html /// [`Release`]: enum.Ordering.html#variant.Release diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 36236ff25b0..1ecbc62225e 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -339,6 +339,25 @@ macro_rules! define_dep_nodes { Ok(DepNode::new_no_params(kind)) } } + + /// Used in testing + pub fn has_label_string(label: &str) -> bool { + match label { + $( + stringify!($variant) => true, + )* + _ => false, + } + } + } + + /// Contains variant => str representations for constructing + /// DepNode groups for tests. + #[allow(dead_code, non_upper_case_globals)] + pub mod label_strs { + $( + pub const $variant: &'static str = stringify!($variant); + )* } ); } @@ -356,7 +375,7 @@ impl fmt::Debug for DepNode { ::ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { if let Some(def_id) = self.extract_def_id(tcx) { - write!(f, "{}", tcx.def_path(def_id).to_string(tcx))?; + write!(f, "{}", tcx.def_path_debug_str(def_id))?; } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*self) { write!(f, "{}", s)?; } else { @@ -700,8 +719,8 @@ impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId, De let (def_id_0, def_id_1) = *self; format!("({}, {})", - tcx.def_path(def_id_0).to_string(tcx), - tcx.def_path(def_id_1).to_string(tcx)) + tcx.def_path_debug_str(def_id_0), + tcx.def_path_debug_str(def_id_1)) } } diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index cb4126245af..8aff042955c 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -577,7 +577,7 @@ impl DepGraph { "DepGraph::try_mark_green() - Duplicate DepNodeColor \ insertion for {:?}", dep_node); - debug!("try_mark_green({:?}) - END - successfully marked as green", dep_node.kind); + debug!("try_mark_green({:?}) - END - successfully marked as green", dep_node); Some(dep_node_index) } diff --git a/src/librustc/hir/def_id.rs b/src/librustc/hir/def_id.rs index 8e48352007b..69d23504cda 100644 --- a/src/librustc/hir/def_id.rs +++ b/src/librustc/hir/def_id.rs @@ -197,12 +197,12 @@ pub struct DefId { impl fmt::Debug for DefId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DefId {{ krate: {:?}, node: {:?}", + write!(f, "DefId {{ krate: {:?}, index: {:?}", self.krate, self.index)?; ty::tls::with_opt(|opt_tcx| { if let Some(tcx) = opt_tcx { - write!(f, " => {}", tcx.def_path(*self).to_string(tcx))?; + write!(f, " => {}", tcx.def_path_debug_str(*self))?; } Ok(()) })?; diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 1fdfbe20328..64a2ba1fa6f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -673,6 +673,7 @@ impl<'a> LoweringContext<'a> { unsafety: self.lower_unsafety(f.unsafety), abi: f.abi, decl: self.lower_fn_decl(&f.decl), + arg_names: self.lower_fn_args_to_names(&f.decl), })) } TyKind::Never => hir::TyNever, diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index bd80b613e77..8bc7cf2faba 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -27,7 +27,6 @@ use std::hash::Hash; use syntax::ast; use syntax::ext::hygiene::Mark; use syntax::symbol::{Symbol, InternedString}; -use ty::TyCtxt; use util::nodemap::NodeMap; /// The DefPathTable maps DefIndexes to DefKeys and vice versa. @@ -296,26 +295,6 @@ impl DefPath { DefPath { data: data, krate: krate } } - pub fn to_string(&self, tcx: TyCtxt) -> String { - let mut s = String::with_capacity(self.data.len() * 16); - - s.push_str(&tcx.original_crate_name(self.krate).as_str()); - s.push_str("/"); - // Don't print the whole crate disambiguator. That's just annoying in - // debug output. - s.push_str(&tcx.crate_disambiguator(self.krate).as_str()[..7]); - - for component in &self.data { - write!(s, - "::{}[{}]", - component.data.as_interned_str(), - component.disambiguator) - .unwrap(); - } - - s - } - /// Returns a string representation of the DefPath without /// the crate-prefix. This method is useful if you don't have /// a TyCtxt available. diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index dcff66dc23b..5ad0ff04c1b 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1418,6 +1418,7 @@ pub struct BareFnTy { pub abi: Abi, pub lifetimes: HirVec<LifetimeDef>, pub decl: P<FnDecl>, + pub arg_names: HirVec<Spanned<Name>>, } #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 5daffe667fd..7287e599b29 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -399,7 +399,8 @@ impl<'a> State<'a> { }, span: syntax_pos::DUMMY_SP, }; - self.print_ty_fn(f.abi, f.unsafety, &f.decl, None, &generics)?; + self.print_ty_fn(f.abi, f.unsafety, &f.decl, None, &generics, + &f.arg_names[..])?; } hir::TyPath(ref qpath) => { self.print_qpath(qpath, false)? @@ -2140,7 +2141,8 @@ impl<'a> State<'a> { unsafety: hir::Unsafety, decl: &hir::FnDecl, name: Option<ast::Name>, - generics: &hir::Generics) + generics: &hir::Generics, + arg_names: &[Spanned<ast::Name>]) -> io::Result<()> { self.ibox(indent_unit)?; if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { @@ -2163,7 +2165,7 @@ impl<'a> State<'a> { name, &generics, &hir::Inherited, - &[], + arg_names, None)?; self.end() } diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 776f85cf5da..c0fae8bf8bd 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -274,7 +274,8 @@ impl_stable_hash_for!(struct hir::BareFnTy { unsafety, abi, lifetimes, - decl + decl, + arg_names }); impl_stable_hash_for!(enum hir::Ty_ { diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 89049958309..4e4fc8b3118 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -429,7 +429,7 @@ impl<'a, 'tcx> Index<'tcx> { // while maintaining the invariant that all sysroot crates are unstable // by default and are unable to be used. if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { - let reason = "this crate is being loaded from the sysroot, and \ + let reason = "this crate is being loaded from the sysroot, an \ unstable location; did you mean to load this crate \ from crates.io via `Cargo.toml` instead?"; let stability = tcx.intern_stability(Stability { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index b909269e153..075a629de04 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -260,6 +260,19 @@ impl<'tcx> Mir<'tcx> { debug_assert!(location.statement_index < block.statements.len()); block.statements[location.statement_index].make_nop() } + + /// Returns the source info associated with `location`. + pub fn source_info(&self, location: Location) -> &SourceInfo { + let block = &self[location.block]; + let stmts = &block.statements; + let idx = location.statement_index; + if location.statement_index < stmts.len() { + &stmts[idx].source_info + } else { + assert!(location.statement_index == stmts.len()); + &block.terminator().source_info + } + } } #[derive(Clone, Debug)] diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 7c1d457a6ee..8b35b064eb3 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -409,9 +409,7 @@ impl_stable_hash_for!(struct self::OutputFilenames { outputs }); -/// Codegen unit names generated by the numbered naming scheme will contain this -/// marker right before the index of the codegen unit. -pub const NUMBERED_CODEGEN_UNIT_MARKER: &'static str = ".cgu-"; +pub const RUST_CGU_EXT: &str = "rust-cgu"; impl OutputFilenames { pub fn path(&self, flavor: OutputType) -> PathBuf { @@ -442,22 +440,14 @@ impl OutputFilenames { let mut extension = String::new(); if let Some(codegen_unit_name) = codegen_unit_name { - if codegen_unit_name.contains(NUMBERED_CODEGEN_UNIT_MARKER) { - // If we use the numbered naming scheme for modules, we don't want - // the files to look like <crate-name><extra>.<crate-name>.<index>.<ext> - // but simply <crate-name><extra>.<index>.<ext> - let marker_offset = codegen_unit_name.rfind(NUMBERED_CODEGEN_UNIT_MARKER) - .unwrap(); - let index_offset = marker_offset + NUMBERED_CODEGEN_UNIT_MARKER.len(); - extension.push_str(&codegen_unit_name[index_offset .. ]); - } else { - extension.push_str(codegen_unit_name); - }; + extension.push_str(codegen_unit_name); } if !ext.is_empty() { if !extension.is_empty() { extension.push_str("."); + extension.push_str(RUST_CGU_EXT); + extension.push_str("."); } extension.push_str(ext); @@ -874,7 +864,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, build_codegen_options, "C", "codegen", CG_OPTIONS, cg_type_desc, cgsetters, ar: Option<String> = (None, parse_opt_string, [UNTRACKED], - "tool to assemble archives with"), + "tool to assemble archives with (has no effect currently, \ + rustc doesn't use an external archiver)"), linker: Option<String> = (None, parse_opt_string, [UNTRACKED], "system linker to link outputs with"), link_arg: Vec<String> = (vec![], parse_string_push, [UNTRACKED], @@ -1060,6 +1051,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "print the result of the translation item collection pass"), mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), + mutable_noalias: bool = (false, parse_bool, [UNTRACKED], + "emit noalias metadata for mutable references"), dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED], "dump MIR state at various points in translation"), dump_mir_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], @@ -1105,6 +1098,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "run the non-lexical lifetimes MIR pass"), trans_time_graph: bool = (false, parse_bool, [UNTRACKED], "generate a graphical HTML report of time spent in trans and LLVM"), + thinlto: bool = (false, parse_bool, [TRACKED], + "enable ThinLTO when possible"), } pub fn default_lib_output() -> CrateType { @@ -1716,7 +1711,7 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) let codegen_units = codegen_units.unwrap_or_else(|| { match opt_level { - // If we're compiling at `-O0` then default to 32 codegen units. + // If we're compiling at `-O0` then default to 16 codegen units. // The number here shouldn't matter too too much as debug mode // builds don't rely on performance at all, meaning that lost // opportunities for inlining through multiple codegen units is @@ -1734,7 +1729,21 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) // unit takes *too* long to build we'll be guaranteed that all // cpus will finish pretty closely to one another and we should // make relatively optimal use of system resources - OptLevel::No => 32, + // + // Another note worth mentioning here, however, is that this number + // isn't *too* high. When codegen units are increased that means we + // currently have to codegen `#[inline]` functions into each codegen + // unit, which means the more codegen units we're using the more we + // may be generating. In other words, increasing codegen units may + // increase the overall work the compiler does. If we don't have + // enough cores to make up for this loss then increasing the number + // of codegen units could become an overall loss! + // + // As a result we choose a hopefully conservative value 16, which + // should be more than the number of cpus of most hardware compiling + // Rust but also not too much for 2-4 core machines to have too much + // loss of compile time. + OptLevel::No => 16, // All other optimization levels default use one codegen unit, // the historical default in Rust for a Long Time. diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 15e10f8c5e8..740299b91f1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1252,6 +1252,27 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } + pub fn def_path_debug_str(self, def_id: DefId) -> String { + // We are explicitly not going through queries here in order to get + // crate name and disambiguator since this code is called from debug!() + // statements within the query system and we'd run into endless + // recursion otherwise. + let (crate_name, crate_disambiguator) = if def_id.is_local() { + (self.crate_name.clone(), + self.sess.local_crate_disambiguator()) + } else { + (self.cstore.crate_name_untracked(def_id.krate), + self.cstore.crate_disambiguator_untracked(def_id.krate)) + }; + + format!("{}[{}]{}", + crate_name, + // Don't print the whole crate disambiguator. That's just + // annoying in debug output. + &(crate_disambiguator.as_str())[..4], + self.def_path(def_id).to_string_no_crate()) + } + pub fn metadata_encoding_version(self) -> Vec<u8> { self.cstore.metadata_encoding_version().to_vec() } diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs index e93a9a788a4..ba21b1df032 100644 --- a/src/librustc_back/target/arm_linux_androideabi.rs +++ b/src/librustc_back/target/arm_linux_androideabi.rs @@ -14,7 +14,7 @@ use target::{Target, TargetOptions, TargetResult}; pub fn target() -> TargetResult { let mut base = super::android_base::opts(); // https://developer.android.com/ndk/guides/abis.html#armeabi - base.features = "+v5te".to_string(); + base.features = "+strict-align,+v5te".to_string(); base.max_atomic_width = Some(64); Ok(Target { diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs index b9f40accaeb..e630376a67d 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabi.rs @@ -27,7 +27,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+v6".to_string(), + features: "+strict-align,+v6".to_string(), abi_blacklist: super::arm_base::abi_blacklist(), .. base }, diff --git a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs index a3ea69caec6..178a948b2b9 100644 --- a/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_gnueabihf.rs @@ -27,7 +27,7 @@ pub fn target() -> TargetResult { linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { - features: "+v6,+vfp2".to_string(), + features: "+strict-align,+v6,+vfp2".to_string(), abi_blacklist: super::arm_base::abi_blacklist(), .. base } diff --git a/src/librustc_back/target/arm_unknown_linux_musleabi.rs b/src/librustc_back/target/arm_unknown_linux_musleabi.rs index 598f722d9af..29720ec5efc 100644 --- a/src/librustc_back/target/arm_unknown_linux_musleabi.rs +++ b/src/librustc_back/target/arm_unknown_linux_musleabi.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { // Most of these settings are copied from the arm_unknown_linux_gnueabi // target. - base.features = "+v6".to_string(); + base.features = "+strict-align,+v6".to_string(); base.max_atomic_width = Some(64); Ok(Target { // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it diff --git a/src/librustc_back/target/arm_unknown_linux_musleabihf.rs b/src/librustc_back/target/arm_unknown_linux_musleabihf.rs index ea0bf75b103..fc8313877f6 100644 --- a/src/librustc_back/target/arm_unknown_linux_musleabihf.rs +++ b/src/librustc_back/target/arm_unknown_linux_musleabihf.rs @@ -16,7 +16,7 @@ pub fn target() -> TargetResult { // Most of these settings are copied from the arm_unknown_linux_gnueabihf // target. - base.features = "+v6,+vfp2".to_string(); + base.features = "+strict-align,+v6,+vfp2".to_string(); base.max_atomic_width = Some(64); Ok(Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM diff --git a/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs b/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs index b9573721678..97397ca4962 100644 --- a/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs +++ b/src/librustc_back/target/armv5te_unknown_linux_gnueabi.rs @@ -27,8 +27,12 @@ pub fn target() -> TargetResult { options: TargetOptions { features: "+soft-float,+strict-align".to_string(), - // No atomic instructions on ARMv5 - max_atomic_width: Some(0), + + // Atomic operations provided when linked with libgcc. + // FIXME: If the following PR is merged, the atomic operations would be + // provided by compiler-builtins instead with no change of behavior: + // https://github.com/rust-lang-nursery/compiler-builtins/pull/115/files + max_atomic_width: Some(32), abi_blacklist: super::arm_base::abi_blacklist(), .. base } diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs deleted file mode 100644 index 9af4606f1f1..00000000000 --- a/src/librustc_back/target/le32_unknown_nacl.rs +++ /dev/null @@ -1,51 +0,0 @@ -// 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use LinkerFlavor; -use super::{LinkArgs, Target, TargetOptions, TargetResult}; - -pub fn target() -> TargetResult { - let mut pre_link_args = LinkArgs::new(); - pre_link_args.insert(LinkerFlavor::Gcc, - vec!["--pnacl-exceptions=sjlj".to_string(), - "--target=le32-unknown-nacl".to_string(), - "-Wl,--start-group".to_string()]); - let mut post_link_args = LinkArgs::new(); - post_link_args.insert(LinkerFlavor::Gcc, - vec!["-Wl,--end-group".to_string()]); - - let opts = TargetOptions { - linker: "pnacl-clang".to_string(), - ar: "pnacl-ar".to_string(), - - pre_link_args, - post_link_args, - dynamic_linking: false, - executables: true, - exe_suffix: ".pexe".to_string(), - linker_is_gnu: true, - allow_asm: false, - max_atomic_width: Some(32), - .. Default::default() - }; - Ok(Target { - llvm_target: "le32-unknown-nacl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "nacl".to_string(), - target_env: "newlib".to_string(), - target_vendor: "unknown".to_string(), - data_layout: "e-i64:64:64-p:32:32:32-v128:32:32".to_string(), - arch: "le32".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: opts, - }) -} diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index b1b208d2de4..039e0153656 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -215,7 +215,6 @@ supported_targets! { ("i686-pc-windows-msvc", i686_pc_windows_msvc), ("i586-pc-windows-msvc", i586_pc_windows_msvc), - ("le32-unknown-nacl", le32_unknown_nacl), ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-experimental-emscripten", wasm32_experimental_emscripten), diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index de3f6f08325..1f2b917bdb9 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -14,6 +14,7 @@ use rustc::middle::mem_categorization::Categorization; use rustc::middle::mem_categorization::NoteClosureEnv; use rustc::middle::mem_categorization::InteriorOffsetKind as Kind; use rustc::ty; +use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin}; use syntax::ast; use syntax_pos; use errors::DiagnosticBuilder; @@ -134,7 +135,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>) } // (keep in sync with gather_moves::check_and_get_illegal_move_origin ) -fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, +fn report_cannot_move_out_of<'a, 'tcx>(bccx: &'a BorrowckCtxt<'a, 'tcx>, move_from: mc::cmt<'tcx>) -> DiagnosticBuilder<'a> { match move_from.cat { @@ -142,43 +143,21 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, Categorization::Deref(_, mc::Implicit(..)) | Categorization::Deref(_, mc::UnsafePtr(..)) | Categorization::StaticItem => { - let mut err = struct_span_err!(bccx, move_from.span, E0507, - "cannot move out of {}", - move_from.descriptive_string(bccx.tcx)); - err.span_label( - move_from.span, - format!("cannot move out of {}", move_from.descriptive_string(bccx.tcx)) - ); - err + bccx.cannot_move_out_of( + move_from.span, &move_from.descriptive_string(bccx.tcx), Origin::Ast) } - Categorization::Interior(ref b, mc::InteriorElement(ik)) => { - let type_name = match (&b.ty.sty, ik) { - (&ty::TyArray(_, _), Kind::Index) => "array", - (&ty::TySlice(_), _) => "slice", - _ => { - span_bug!(move_from.span, "this path should not cause illegal move"); - }, - }; - let mut err = struct_span_err!(bccx, move_from.span, E0508, - "cannot move out of type `{}`, \ - a non-copy {}", - b.ty, type_name); - err.span_label(move_from.span, "cannot move out of here"); - err + bccx.cannot_move_out_of_interior_noncopy( + move_from.span, b.ty, ik == Kind::Index, Origin::Ast) } Categorization::Downcast(ref b, _) | Categorization::Interior(ref b, mc::InteriorField(_)) => { match b.ty.sty { ty::TyAdt(def, _) if def.has_dtor(bccx.tcx) => { - let mut err = struct_span_err!(bccx, move_from.span, E0509, - "cannot move out of type `{}`, \ - which implements the `Drop` trait", - b.ty); - err.span_label(move_from.span, "cannot move out of here"); - err - }, + bccx.cannot_move_out_of_interior_of_drop( + move_from.span, b.ty, Origin::Ast) + } _ => { span_bug!(move_from.span, "this path should not cause illegal move"); } diff --git a/src/librustc_borrowck/diagnostics.rs b/src/librustc_borrowck/diagnostics.rs index 29c35e23d4e..031dbcb1ebb 100644 --- a/src/librustc_borrowck/diagnostics.rs +++ b/src/librustc_borrowck/diagnostics.rs @@ -317,272 +317,6 @@ fn main() { ``` "##, -E0507: r##" -You tried to move out of a value which was borrowed. Erroneous code example: - -```compile_fail,E0507 -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // error: cannot move out of borrowed content -} -``` - -Here, the `nothing_is_true` method takes the ownership of `self`. However, -`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`, -which is a borrow of the content owned by the `RefCell`. To fix this error, -you have three choices: - -* Try to avoid moving the variable. -* Somehow reclaim the ownership. -* Implement the `Copy` trait on the type. - -Examples: - -``` -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(&self) {} // First case, we don't take ownership -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // ok! -} -``` - -Or: - -``` -use std::cell::RefCell; - -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - let x = x.into_inner(); // we get back ownership - - x.nothing_is_true(); // ok! -} -``` - -Or: - -``` -use std::cell::RefCell; - -#[derive(Clone, Copy)] // we implement the Copy trait -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -fn main() { - let x = RefCell::new(TheDarkKnight); - - x.borrow().nothing_is_true(); // ok! -} -``` - -Moving a member out of a mutably borrowed struct will also cause E0507 error: - -```compile_fail,E0507 -struct TheDarkKnight; - -impl TheDarkKnight { - fn nothing_is_true(self) {} -} - -struct Batcave { - knight: TheDarkKnight -} - -fn main() { - let mut cave = Batcave { - knight: TheDarkKnight - }; - let borrowed = &mut cave; - - borrowed.knight.nothing_is_true(); // E0507 -} -``` - -It is fine only if you put something back. `mem::replace` can be used for that: - -``` -# struct TheDarkKnight; -# impl TheDarkKnight { fn nothing_is_true(self) {} } -# struct Batcave { knight: TheDarkKnight } -use std::mem; - -let mut cave = Batcave { - knight: TheDarkKnight -}; -let borrowed = &mut cave; - -mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! -``` - -You can find more information about borrowing in the rust-book: -http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html -"##, - -E0508: r##" -A value was moved out of a non-copy fixed-size array. - -Example of erroneous code: - -```compile_fail,E0508 -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`, - // a non-copy fixed-size array -} -``` - -The first element was moved out of the array, but this is not -possible because `NonCopy` does not implement the `Copy` trait. - -Consider borrowing the element instead of moving it: - -``` -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - let _value = &array[0]; // Borrowing is allowed, unlike moving. -} -``` - -Alternatively, if your type implements `Clone` and you need to own the value, -consider borrowing and then cloning: - -``` -#[derive(Clone)] -struct NonCopy; - -fn main() { - let array = [NonCopy; 1]; - // Now you can clone the array element. - let _value = array[0].clone(); -} -``` -"##, - -E0509: r##" -This error occurs when an attempt is made to move out of a value whose type -implements the `Drop` trait. - -Example of erroneous code: - -```compile_fail,E0509 -struct FancyNum { - num: usize -} - -struct DropStruct { - fancy: FancyNum -} - -impl Drop for DropStruct { - fn drop(&mut self) { - // Destruct DropStruct, possibly using FancyNum - } -} - -fn main() { - let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; - let fancy_field = drop_struct.fancy; // Error E0509 - println!("Fancy: {}", fancy_field.num); - // implicit call to `drop_struct.drop()` as drop_struct goes out of scope -} -``` - -Here, we tried to move a field out of a struct of type `DropStruct` which -implements the `Drop` trait. However, a struct cannot be dropped if one or -more of its fields have been moved. - -Structs implementing the `Drop` trait have an implicit destructor that gets -called when they go out of scope. This destructor may use the fields of the -struct, so moving out of the struct could make it impossible to run the -destructor. Therefore, we must think of all values whose type implements the -`Drop` trait as single units whose fields cannot be moved. - -This error can be fixed by creating a reference to the fields of a struct, -enum, or tuple using the `ref` keyword: - -``` -struct FancyNum { - num: usize -} - -struct DropStruct { - fancy: FancyNum -} - -impl Drop for DropStruct { - fn drop(&mut self) { - // Destruct DropStruct, possibly using FancyNum - } -} - -fn main() { - let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; - let ref fancy_field = drop_struct.fancy; // No more errors! - println!("Fancy: {}", fancy_field.num); - // implicit call to `drop_struct.drop()` as drop_struct goes out of scope -} -``` - -Note that this technique can also be used in the arms of a match expression: - -``` -struct FancyNum { - num: usize -} - -enum DropEnum { - Fancy(FancyNum) -} - -impl Drop for DropEnum { - fn drop(&mut self) { - // Destruct DropEnum, possibly using FancyNum - } -} - -fn main() { - // Creates and enum of type `DropEnum`, which implements `Drop` - let drop_enum = DropEnum::Fancy(FancyNum{num: 10}); - match drop_enum { - // Creates a reference to the inside of `DropEnum::Fancy` - DropEnum::Fancy(ref fancy_field) => // No error! - println!("It was fancy-- {}!", fancy_field.num), - } - // implicit call to `drop_enum.drop()` as drop_enum goes out of scope -} -``` -"##, - E0595: r##" Closures cannot mutate immutable captured variables. diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 33a9cb58c51..0270e3618e2 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -39,6 +39,8 @@ //! previous revision to compare things to. //! +use std::collections::HashSet; +use std::vec::Vec; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; @@ -54,6 +56,8 @@ use rustc::ty::TyCtxt; const LABEL: &'static str = "label"; const CFG: &'static str = "cfg"; +type Labels = HashSet<String>; + pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { // can't add `#[rustc_dirty]` etc without opting in to this feature if !tcx.sess.features.borrow().rustc_attrs { @@ -87,23 +91,46 @@ pub struct DirtyCleanVisitor<'a, 'tcx:'a> { } impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { - fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { - let def_path_hash = self.tcx.def_path_hash(def_id); + fn labels(&self, attr: &Attribute) -> Labels { for item in attr.meta_item_list().unwrap_or_else(Vec::new) { if item.check_name(LABEL) { let value = expect_associated_value(self.tcx, &item); - match DepNode::from_label_string(&value.as_str(), def_path_hash) { - Ok(dep_node) => return dep_node, - Err(()) => { - self.tcx.sess.span_fatal( - item.span, - &format!("dep-node label `{}` not recognized", value)); - } + return self.resolve_labels(&item, value.as_str().as_ref()); + } + } + self.tcx.sess.span_fatal(attr.span, "no `label` found"); + } + + fn resolve_labels(&self, item: &NestedMetaItem, value: &str) -> Labels { + let mut out: Labels = HashSet::new(); + for label in value.split(',') { + let label = label.trim(); + if DepNode::has_label_string(label) { + if out.contains(label) { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` is repeated", label)); } + out.insert(label.to_string()); + } else { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` not recognized", label)); } } + out + } - self.tcx.sess.span_fatal(attr.span, "no `label` found"); + fn dep_nodes(&self, labels: &Labels, def_id: DefId) -> Vec<DepNode> { + let mut out = Vec::with_capacity(labels.len()); + let def_path_hash = self.tcx.def_path_hash(def_id); + for label in labels.iter() { + match DepNode::from_label_string(label, def_path_hash) { + Ok(dep_node) => out.push(dep_node), + Err(()) => unreachable!(), + } + } + out } fn dep_node_str(&self, dep_node: &DepNode) -> String { @@ -150,12 +177,18 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { if attr.check_name(ATTR_DIRTY) { if check_config(self.tcx, attr) { self.checked_attrs.insert(attr.id); - self.assert_dirty(item_span, self.dep_node(attr, def_id)); + let labels = self.labels(attr); + for dep_node in self.dep_nodes(&labels, def_id) { + self.assert_dirty(item_span, dep_node); + } } } else if attr.check_name(ATTR_CLEAN) { if check_config(self.tcx, attr) { self.checked_attrs.insert(attr.id); - self.assert_clean(item_span, self.dep_node(attr, def_id)); + let labels = self.labels(attr); + for dep_node in self.dep_nodes(&labels, def_id) { + self.assert_clean(item_span, dep_node); + } } } } diff --git a/src/librustc_llvm/build.rs b/src/librustc_llvm/build.rs index dde7a38efc7..75efe135f65 100644 --- a/src/librustc_llvm/build.rs +++ b/src/librustc_llvm/build.rs @@ -88,7 +88,7 @@ fn main() { let is_crossed = target != host; let mut optional_components = - vec!["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", + vec!["x86", "arm", "aarch64", "mips", "powerpc", "systemz", "jsbackend", "webassembly", "msp430", "sparc", "nvptx"]; let mut version_cmd = Command::new(&llvm_config); @@ -115,6 +115,7 @@ fn main() { "linker", "asmparser", "mcjit", + "lto", "interpreter", "instrumentation"]; diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index fd4a136f50b..3399bf2acd8 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -345,6 +345,20 @@ pub enum PassKind { Module, } +/// LLVMRustThinLTOData +pub enum ThinLTOData {} + +/// LLVMRustThinLTOBuffer +pub enum ThinLTOBuffer {} + +/// LLVMRustThinLTOModule +#[repr(C)] +pub struct ThinLTOModule { + pub identifier: *const c_char, + pub data: *const u8, + pub len: usize, +} + // Opaque pointer types #[allow(missing_copy_implementations)] pub enum Module_opaque {} @@ -1271,6 +1285,9 @@ extern "C" { PM: PassManagerRef, Internalize: Bool, RunInliner: Bool); + pub fn LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + PMB: PassManagerBuilderRef, + PM: PassManagerRef) -> bool; // Stuff that's in rustllvm/ because it's not upstream yet. @@ -1685,4 +1702,43 @@ extern "C" { pub fn LLVMRustModuleBufferLen(p: *const ModuleBuffer) -> usize; pub fn LLVMRustModuleBufferFree(p: *mut ModuleBuffer); pub fn LLVMRustModuleCost(M: ModuleRef) -> u64; + + pub fn LLVMRustThinLTOAvailable() -> bool; + pub fn LLVMRustWriteThinBitcodeToFile(PMR: PassManagerRef, + M: ModuleRef, + BC: *const c_char) -> bool; + pub fn LLVMRustThinLTOBufferCreate(M: ModuleRef) -> *mut ThinLTOBuffer; + pub fn LLVMRustThinLTOBufferFree(M: *mut ThinLTOBuffer); + pub fn LLVMRustThinLTOBufferPtr(M: *const ThinLTOBuffer) -> *const c_char; + pub fn LLVMRustThinLTOBufferLen(M: *const ThinLTOBuffer) -> size_t; + pub fn LLVMRustCreateThinLTOData( + Modules: *const ThinLTOModule, + NumModules: c_uint, + PreservedSymbols: *const *const c_char, + PreservedSymbolsLen: c_uint, + ) -> *mut ThinLTOData; + pub fn LLVMRustPrepareThinLTORename( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOResolveWeak( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOInternalize( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustPrepareThinLTOImport( + Data: *const ThinLTOData, + Module: ModuleRef, + ) -> bool; + pub fn LLVMRustFreeThinLTOData(Data: *mut ThinLTOData); + pub fn LLVMRustParseBitcodeForThinLTO( + Context: ContextRef, + Data: *const u8, + len: usize, + Identifier: *const c_char, + ) -> ModuleRef; + pub fn LLVMGetModuleIdentifier(M: ModuleRef, size: *mut usize) -> *const c_char; } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 9be5f5b5486..98172bca177 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -346,10 +346,6 @@ pub fn initialize_available_targets() { LLVMInitializePowerPCTargetMC, LLVMInitializePowerPCAsmPrinter, LLVMInitializePowerPCAsmParser); - init_target!(llvm_component = "pnacl", - LLVMInitializePNaClTargetInfo, - LLVMInitializePNaClTarget, - LLVMInitializePNaClTargetMC); init_target!(llvm_component = "systemz", LLVMInitializeSystemZTargetInfo, LLVMInitializeSystemZTarget, diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs index 14b6e319132..db6a0ee4ba5 100644 --- a/src/librustc_mir/borrow_check.rs +++ b/src/librustc_mir/borrow_check.rs @@ -30,6 +30,7 @@ use dataflow::{MoveDataParamEnv}; use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}; use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use dataflow::{Borrows, BorrowData, BorrowIndex}; +use dataflow::move_paths::{MoveError, IllegalMoveOriginKind}; use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult}; use util::borrowck_errors::{BorrowckErrors, Origin}; @@ -59,7 +60,33 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) { let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|_infcx| { - let move_data = MoveData::gather_moves(mir, tcx, param_env); + let move_data = match MoveData::gather_moves(mir, tcx, param_env) { + Ok(move_data) => move_data, + Err((move_data, move_errors)) => { + for move_error in move_errors { + let (span, kind): (Span, IllegalMoveOriginKind) = match move_error { + MoveError::UnionMove { .. } => + unimplemented!("dont know how to report union move errors yet."), + MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind), + }; + let origin = Origin::Mir; + let mut err = match kind { + IllegalMoveOriginKind::Static => + tcx.cannot_move_out_of(span, "static item", origin), + IllegalMoveOriginKind::BorrowedContent => + tcx.cannot_move_out_of(span, "borrowed_content", origin), + IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => + tcx.cannot_move_out_of_interior_of_drop(span, ty, origin), + IllegalMoveOriginKind::InteriorOfSlice { elem_ty: ty, is_index } => + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + IllegalMoveOriginKind::InteriorOfArray { elem_ty: ty, is_index } => + tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin), + }; + err.emit(); + } + move_data + } + }; let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds, @@ -1106,9 +1133,7 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> // Retrieve span of given borrow from the current MIR representation fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span { - self.mir.basic_blocks()[borrow.location.block] - .statements[borrow.location.statement_index] - .source_info.span + self.mir.source_info(borrow.location).span } } diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 86298c3b83e..0790d937ceb 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -22,17 +22,15 @@ use std::mem; use super::abs_domain::Lift; use super::{LocationMap, MoveData, MovePath, MovePathLookup, MovePathIndex, MoveOut, MoveOutIndex}; +use super::{MoveError}; +use super::IllegalMoveOriginKind::*; -pub(super) struct MoveDataBuilder<'a, 'tcx: 'a> { +struct MoveDataBuilder<'a, 'tcx: 'a> { mir: &'a Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, -} - -pub enum MovePathError { - IllegalMove, - UnionMove { path: MovePathIndex }, + errors: Vec<MoveError<'tcx>>, } impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { @@ -47,6 +45,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { mir, tcx, param_env, + errors: Vec::new(), data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(mir), @@ -85,7 +84,9 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { assert_eq!(path_map_ent, move_path); move_path } +} +impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { /// This creates a MovePath for a given lvalue, returning an `MovePathError` /// if that lvalue can't be moved from. /// @@ -94,13 +95,15 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { /// /// Maybe we should have separate "borrowck" and "moveck" modes. fn move_path_for(&mut self, lval: &Lvalue<'tcx>) - -> Result<MovePathIndex, MovePathError> + -> Result<MovePathIndex, MoveError<'tcx>> { debug!("lookup({:?})", lval); match *lval { - Lvalue::Local(local) => Ok(self.data.rev_lookup.locals[local]), - // error: can't move out of a static - Lvalue::Static(..) => Err(MovePathError::IllegalMove), + Lvalue::Local(local) => Ok(self.builder.data.rev_lookup.locals[local]), + Lvalue::Static(..) => { + let span = self.builder.mir.source_info(self.loc).span; + Err(MoveError::cannot_move_out_of(span, Static)) + } Lvalue::Projection(ref proj) => { self.move_path_for_projection(lval, proj) } @@ -116,37 +119,52 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { fn move_path_for_projection(&mut self, lval: &Lvalue<'tcx>, proj: &LvalueProjection<'tcx>) - -> Result<MovePathIndex, MovePathError> + -> Result<MovePathIndex, MoveError<'tcx>> { let base = try!(self.move_path_for(&proj.base)); - let lv_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + let mir = self.builder.mir; + let tcx = self.builder.tcx; + let lv_ty = proj.base.ty(mir, tcx).to_ty(tcx); match lv_ty.sty { - // error: can't move out of borrowed content - ty::TyRef(..) | ty::TyRawPtr(..) => return Err(MovePathError::IllegalMove), - // error: can't move out of struct with destructor - ty::TyAdt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => - return Err(MovePathError::IllegalMove), + ty::TyRef(..) | ty::TyRawPtr(..) => + return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span, + BorrowedContent)), + ty::TyAdt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => + return Err(MoveError::cannot_move_out_of(mir.source_info(self.loc).span, + InteriorOfTypeWithDestructor { + container_ty: lv_ty + })), // move out of union - always move the entire union ty::TyAdt(adt, _) if adt.is_union() => - return Err(MovePathError::UnionMove { path: base }), - // error: can't move out of a slice - ty::TySlice(..) => - return Err(MovePathError::IllegalMove), - ty::TyArray(..) => match proj.elem { - // error: can't move out of an array - ProjectionElem::Index(..) => return Err(MovePathError::IllegalMove), + return Err(MoveError::UnionMove { path: base }), + ty::TySlice(elem_ty) => + return Err(MoveError::cannot_move_out_of( + mir.source_info(self.loc).span, + InteriorOfSlice { + elem_ty, is_index: match proj.elem { + ProjectionElem::Index(..) => true, + _ => false + }, + })), + ty::TyArray(elem_ty, _num_elems) => match proj.elem { + ProjectionElem::Index(..) => + return Err(MoveError::cannot_move_out_of( + mir.source_info(self.loc).span, + InteriorOfArray { + elem_ty, is_index: true + })), _ => { // FIXME: still badly broken } }, _ => {} }; - match self.data.rev_lookup.projections.entry((base, proj.elem.lift())) { + match self.builder.data.rev_lookup.projections.entry((base, proj.elem.lift())) { Entry::Occupied(ent) => Ok(*ent.get()), Entry::Vacant(ent) => { - let path = Self::new_move_path( - &mut self.data.move_paths, - &mut self.data.path_map, + let path = MoveDataBuilder::new_move_path( + &mut self.builder.data.move_paths, + &mut self.builder.data.path_map, Some(base), lval.clone() ); @@ -155,8 +173,10 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } } +} - fn finalize(self) -> MoveData<'tcx> { +impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { + fn finalize(self) -> Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<MoveError<'tcx>>)> { debug!("{}", { debug!("moves for {:?}:", self.mir.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -168,14 +188,20 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } "done dumping moves" }); - self.data + + if self.errors.len() > 0 { + Err((self.data, self.errors)) + } else { + Ok(self.data) + } } } pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) - -> MoveData<'tcx> { + -> Result<MoveData<'tcx>, + (MoveData<'tcx>, Vec<MoveError<'tcx>>)> { let mut builder = MoveDataBuilder::new(mir, tcx, param_env); for (bb, block) in mir.basic_blocks().iter_enumerated() { @@ -197,6 +223,22 @@ pub(super) fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { fn gather_statement(&mut self, loc: Location, stmt: &Statement<'tcx>) { debug!("gather_statement({:?}, {:?})", loc, stmt); + (Gatherer { builder: self, loc }).gather_statement(stmt); + } + + fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { + debug!("gather_terminator({:?}, {:?})", loc, term); + (Gatherer { builder: self, loc }).gather_terminator(term); + } +} + +struct Gatherer<'b, 'a: 'b, 'tcx: 'a> { + builder: &'b mut MoveDataBuilder<'a, 'tcx>, + loc: Location, +} + +impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { + fn gather_statement(&mut self, stmt: &Statement<'tcx>) { match stmt.kind { StatementKind::Assign(ref lval, ref rval) => { self.create_move_path(lval); @@ -206,7 +248,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { // the exterior. self.create_move_path(&lval.clone().deref()); } - self.gather_rvalue(loc, rval); + self.gather_rvalue(rval); } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => {} @@ -221,22 +263,22 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } - fn gather_rvalue(&mut self, loc: Location, rvalue: &Rvalue<'tcx>) { + fn gather_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { match *rvalue { Rvalue::Use(ref operand) | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) | Rvalue::UnaryOp(_, ref operand) => { - self.gather_operand(loc, operand) + self.gather_operand(operand) } Rvalue::BinaryOp(ref _binop, ref lhs, ref rhs) | Rvalue::CheckedBinaryOp(ref _binop, ref lhs, ref rhs) => { - self.gather_operand(loc, lhs); - self.gather_operand(loc, rhs); + self.gather_operand(lhs); + self.gather_operand(rhs); } Rvalue::Aggregate(ref _kind, ref operands) => { for operand in operands { - self.gather_operand(loc, operand); + self.gather_operand(operand); } } Rvalue::Ref(..) | @@ -258,8 +300,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } - fn gather_terminator(&mut self, loc: Location, term: &Terminator<'tcx>) { - debug!("gather_terminator({:?}, {:?})", loc, term); + fn gather_terminator(&mut self, term: &Terminator<'tcx>) { match term.kind { TerminatorKind::Goto { target: _ } | TerminatorKind::Resume | @@ -267,7 +308,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { TerminatorKind::Unreachable => { } TerminatorKind::Return => { - self.gather_move(loc, &Lvalue::Local(RETURN_POINTER)); + self.gather_move(&Lvalue::Local(RETURN_POINTER)); } TerminatorKind::Assert { .. } | @@ -276,20 +317,20 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } TerminatorKind::Yield { ref value, .. } => { - self.gather_operand(loc, value); + self.gather_operand(value); } TerminatorKind::Drop { ref location, target: _, unwind: _ } => { - self.gather_move(loc, location); + self.gather_move(location); } TerminatorKind::DropAndReplace { ref location, ref value, .. } => { self.create_move_path(location); - self.gather_operand(loc, value); + self.gather_operand(value); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { - self.gather_operand(loc, func); + self.gather_operand(func); for arg in args { - self.gather_operand(loc, arg); + self.gather_operand(arg); } if let Some((ref destination, _bb)) = *destination { self.create_move_path(destination); @@ -298,40 +339,38 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } - fn gather_operand(&mut self, loc: Location, operand: &Operand<'tcx>) { + fn gather_operand(&mut self, operand: &Operand<'tcx>) { match *operand { Operand::Constant(..) => {} // not-a-move Operand::Consume(ref lval) => { // a move - self.gather_move(loc, lval); + self.gather_move(lval); } } } - fn gather_move(&mut self, loc: Location, lval: &Lvalue<'tcx>) { - debug!("gather_move({:?}, {:?})", loc, lval); + fn gather_move(&mut self, lval: &Lvalue<'tcx>) { + debug!("gather_move({:?}, {:?})", self.loc, lval); - let lv_ty = lval.ty(self.mir, self.tcx).to_ty(self.tcx); - if !lv_ty.moves_by_default(self.tcx, self.param_env, DUMMY_SP) { - debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", loc, lval, lv_ty); + let tcx = self.builder.tcx; + let lv_ty = lval.ty(self.builder.mir, tcx).to_ty(tcx); + if !lv_ty.moves_by_default(tcx, self.builder.param_env, DUMMY_SP) { + debug!("gather_move({:?}, {:?}) - {:?} is Copy. skipping", self.loc, lval, lv_ty); return } let path = match self.move_path_for(lval) { - Ok(path) | Err(MovePathError::UnionMove { path }) => path, - Err(MovePathError::IllegalMove) => { - // Moving out of a bad path. Eventually, this should be a MIR - // borrowck error instead of a bug. - span_bug!(self.mir.span, - "Broken MIR: moving out of lvalue {:?}: {:?} at {:?}", - lval, lv_ty, loc); + Ok(path) | Err(MoveError::UnionMove { path }) => path, + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push(error); + return; } }; - let move_out = self.data.moves.push(MoveOut { path: path, source: loc }); + let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc }); debug!("gather_move({:?}, {:?}): adding move {:?} of {:?}", - loc, lval, move_out, path); + self.loc, lval, move_out, path); - self.data.path_map[path].push(move_out); - self.data.loc_map[loc].push(move_out); + self.builder.data.path_map[path].push(move_out); + self.builder.data.loc_map[self.loc].push(move_out); } } diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index d2d80649846..9369156a223 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -13,6 +13,7 @@ use rustc::ty::{self, TyCtxt}; use rustc::mir::*; use rustc::util::nodemap::FxHashMap; use rustc_data_structures::indexed_vec::{IndexVec}; +use syntax_pos::{Span}; use std::fmt; use std::ops::{Index, IndexMut}; @@ -227,11 +228,39 @@ impl<'tcx> MovePathLookup<'tcx> { } } +#[derive(Debug)] +pub struct IllegalMoveOrigin<'tcx> { + pub(crate) span: Span, + pub(crate) kind: IllegalMoveOriginKind<'tcx>, +} + +#[derive(Debug)] +pub(crate) enum IllegalMoveOriginKind<'tcx> { + Static, + BorrowedContent, + InteriorOfTypeWithDestructor { container_ty: ty::Ty<'tcx> }, + InteriorOfSlice { elem_ty: ty::Ty<'tcx>, is_index: bool, }, + InteriorOfArray { elem_ty: ty::Ty<'tcx>, is_index: bool, }, +} + +#[derive(Debug)] +pub enum MoveError<'tcx> { + IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> }, + UnionMove { path: MovePathIndex }, +} + +impl<'tcx> MoveError<'tcx> { + fn cannot_move_out_of(span: Span, kind: IllegalMoveOriginKind<'tcx>) -> Self { + let origin = IllegalMoveOrigin { span, kind }; + MoveError::IllegalMove { cannot_move_out_of: origin } + } +} + impl<'a, 'tcx> MoveData<'tcx> { pub fn gather_moves(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>) - -> Self { + -> Result<Self, (Self, Vec<MoveError<'tcx>>)> { builder::gather_moves(mir, tcx, param_env) } } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 950bdff1d0f..645af0bff64 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -999,6 +999,272 @@ fn print_fancy_ref(fancy_ref: &FancyNum){ ``` "##, +E0507: r##" +You tried to move out of a value which was borrowed. Erroneous code example: + +```compile_fail,E0507 +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // error: cannot move out of borrowed content +} +``` + +Here, the `nothing_is_true` method takes the ownership of `self`. However, +`self` cannot be moved because `.borrow()` only provides an `&TheDarkKnight`, +which is a borrow of the content owned by the `RefCell`. To fix this error, +you have three choices: + +* Try to avoid moving the variable. +* Somehow reclaim the ownership. +* Implement the `Copy` trait on the type. + +Examples: + +``` +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(&self) {} // First case, we don't take ownership +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // ok! +} +``` + +Or: + +``` +use std::cell::RefCell; + +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + let x = x.into_inner(); // we get back ownership + + x.nothing_is_true(); // ok! +} +``` + +Or: + +``` +use std::cell::RefCell; + +#[derive(Clone, Copy)] // we implement the Copy trait +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +fn main() { + let x = RefCell::new(TheDarkKnight); + + x.borrow().nothing_is_true(); // ok! +} +``` + +Moving a member out of a mutably borrowed struct will also cause E0507 error: + +```compile_fail,E0507 +struct TheDarkKnight; + +impl TheDarkKnight { + fn nothing_is_true(self) {} +} + +struct Batcave { + knight: TheDarkKnight +} + +fn main() { + let mut cave = Batcave { + knight: TheDarkKnight + }; + let borrowed = &mut cave; + + borrowed.knight.nothing_is_true(); // E0507 +} +``` + +It is fine only if you put something back. `mem::replace` can be used for that: + +``` +# struct TheDarkKnight; +# impl TheDarkKnight { fn nothing_is_true(self) {} } +# struct Batcave { knight: TheDarkKnight } +use std::mem; + +let mut cave = Batcave { + knight: TheDarkKnight +}; +let borrowed = &mut cave; + +mem::replace(&mut borrowed.knight, TheDarkKnight).nothing_is_true(); // ok! +``` + +You can find more information about borrowing in the rust-book: +http://doc.rust-lang.org/book/first-edition/references-and-borrowing.html +"##, + +E0508: r##" +A value was moved out of a non-copy fixed-size array. + +Example of erroneous code: + +```compile_fail,E0508 +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = array[0]; // error: cannot move out of type `[NonCopy; 1]`, + // a non-copy fixed-size array +} +``` + +The first element was moved out of the array, but this is not +possible because `NonCopy` does not implement the `Copy` trait. + +Consider borrowing the element instead of moving it: + +``` +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + let _value = &array[0]; // Borrowing is allowed, unlike moving. +} +``` + +Alternatively, if your type implements `Clone` and you need to own the value, +consider borrowing and then cloning: + +``` +#[derive(Clone)] +struct NonCopy; + +fn main() { + let array = [NonCopy; 1]; + // Now you can clone the array element. + let _value = array[0].clone(); +} +``` +"##, + +E0509: r##" +This error occurs when an attempt is made to move out of a value whose type +implements the `Drop` trait. + +Example of erroneous code: + +```compile_fail,E0509 +struct FancyNum { + num: usize +} + +struct DropStruct { + fancy: FancyNum +} + +impl Drop for DropStruct { + fn drop(&mut self) { + // Destruct DropStruct, possibly using FancyNum + } +} + +fn main() { + let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; + let fancy_field = drop_struct.fancy; // Error E0509 + println!("Fancy: {}", fancy_field.num); + // implicit call to `drop_struct.drop()` as drop_struct goes out of scope +} +``` + +Here, we tried to move a field out of a struct of type `DropStruct` which +implements the `Drop` trait. However, a struct cannot be dropped if one or +more of its fields have been moved. + +Structs implementing the `Drop` trait have an implicit destructor that gets +called when they go out of scope. This destructor may use the fields of the +struct, so moving out of the struct could make it impossible to run the +destructor. Therefore, we must think of all values whose type implements the +`Drop` trait as single units whose fields cannot be moved. + +This error can be fixed by creating a reference to the fields of a struct, +enum, or tuple using the `ref` keyword: + +``` +struct FancyNum { + num: usize +} + +struct DropStruct { + fancy: FancyNum +} + +impl Drop for DropStruct { + fn drop(&mut self) { + // Destruct DropStruct, possibly using FancyNum + } +} + +fn main() { + let drop_struct = DropStruct{fancy: FancyNum{num: 5}}; + let ref fancy_field = drop_struct.fancy; // No more errors! + println!("Fancy: {}", fancy_field.num); + // implicit call to `drop_struct.drop()` as drop_struct goes out of scope +} +``` + +Note that this technique can also be used in the arms of a match expression: + +``` +struct FancyNum { + num: usize +} + +enum DropEnum { + Fancy(FancyNum) +} + +impl Drop for DropEnum { + fn drop(&mut self) { + // Destruct DropEnum, possibly using FancyNum + } +} + +fn main() { + // Creates and enum of type `DropEnum`, which implements `Drop` + let drop_enum = DropEnum::Fancy(FancyNum{num: 10}); + match drop_enum { + // Creates a reference to the inside of `DropEnum::Fancy` + DropEnum::Fancy(ref fancy_field) => // No error! + println!("It was fancy-- {}!", fancy_field.num), + } + // implicit call to `drop_enum.drop()` as drop_enum goes out of scope +} +``` +"##, + } register_diagnostics! { diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index c833904adba..be1b794ecdf 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -45,7 +45,7 @@ impl MirPass for ElaborateDrops { } let id = src.item_id(); let param_env = tcx.param_env(tcx.hir.local_def_id(id)); - let move_data = MoveData::gather_moves(mir, tcx, param_env); + let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap(); let elaborate_patch = { let mir = &*mir; let env = MoveDataParamEnv { diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index ceff52409b2..8d6458d7934 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -45,7 +45,7 @@ impl MirPass for SanityCheck { let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(mir, tcx, param_env); + let move_data = MoveData::gather_moves(mir, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len()); let flow_inits = diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs index 81e3cd7f61f..37d53ca829e 100644 --- a/src/librustc_mir/util/borrowck_errors.rs +++ b/src/librustc_mir/util/borrowck_errors.rs @@ -248,6 +248,52 @@ pub trait BorrowckErrors { { self.cannot_assign(span, &format!("immutable static item `{}`", desc), o) } + + fn cannot_move_out_of(&self, move_from_span: Span, move_from_desc: &str, o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, move_from_span, E0507, + "cannot move out of {}{OGN}", + move_from_desc, OGN=o); + err.span_label( + move_from_span, + format!("cannot move out of {}", move_from_desc)); + err + } + + fn cannot_move_out_of_interior_noncopy(&self, + move_from_span: Span, + ty: ty::Ty, + is_index: bool, + o: Origin) + -> DiagnosticBuilder + { + let type_name = match (&ty.sty, is_index) { + (&ty::TyArray(_, _), true) => "array", + (&ty::TySlice(_), _) => "slice", + _ => span_bug!(move_from_span, "this path should not cause illegal move"), + }; + let mut err = struct_span_err!(self, move_from_span, E0508, + "cannot move out of type `{}`, \ + a non-copy {}{OGN}", + ty, type_name, OGN=o); + err.span_label(move_from_span, "cannot move out of here"); + err + } + + fn cannot_move_out_of_interior_of_drop(&self, + move_from_span: Span, + container_ty: ty::Ty, + o: Origin) + -> DiagnosticBuilder + { + let mut err = struct_span_err!(self, move_from_span, E0509, + "cannot move out of type `{}`, \ + which implements the `Drop` trait{OGN}", + container_ty, OGN=o); + err.span_label(move_from_span, "cannot move out of here"); + err + } } impl<'b, 'tcx, 'gcx> BorrowckErrors for TyCtxt<'b, 'tcx, 'gcx> { diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index 2aecc016a5c..c3b6ede24b0 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -37,6 +37,7 @@ use type_of; use rustc::hir; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Layout, LayoutTyper, TyLayout, Size}; +use rustc_back::PanicStrategy; use libc::c_uint; use std::cmp; @@ -750,9 +751,7 @@ impl<'a, 'tcx> FnType<'tcx> { Some(ty.boxed_ty()) } - ty::TyRef(b, mt) => { - use rustc::ty::{BrAnon, ReLateBound}; - + ty::TyRef(_, mt) => { // `&mut` pointer parameters never alias other parameters, or mutable global data // // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as @@ -760,7 +759,17 @@ impl<'a, 'tcx> FnType<'tcx> { // on memory dependencies rather than pointer equality let is_freeze = ccx.shared().type_is_freeze(mt.ty); - if mt.mutbl != hir::MutMutable && is_freeze { + let no_alias_is_safe = + if ccx.shared().tcx().sess.opts.debugging_opts.mutable_noalias || + ccx.shared().tcx().sess.panic_strategy() == PanicStrategy::Abort { + // Mutable refrences or immutable shared references + mt.mutbl == hir::MutMutable || is_freeze + } else { + // Only immutable shared references + mt.mutbl != hir::MutMutable && is_freeze + }; + + if no_alias_is_safe { arg.attrs.set(ArgAttribute::NoAlias); } @@ -768,13 +777,6 @@ impl<'a, 'tcx> FnType<'tcx> { arg.attrs.set(ArgAttribute::ReadOnly); } - // When a reference in an argument has no named lifetime, it's - // impossible for that reference to escape this function - // (returned or stored beyond the call by a closure). - if let ReLateBound(_, BrAnon(_)) = *b { - arg.attrs.set(ArgAttribute::NoCapture); - } - Some(mt.ty) } _ => None diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 3badc1b9a69..3f25c182fa2 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -16,6 +16,7 @@ use super::rpath::RPathConfig; use super::rpath; use metadata::METADATA_FILENAME; use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; +use rustc::session::config::RUST_CGU_EXT; use rustc::session::filesearch; use rustc::session::search_paths::PathKind; use rustc::session::Session; @@ -45,13 +46,9 @@ use syntax::attr; /// The LLVM module name containing crate-metadata. This includes a `.` on /// purpose, so it cannot clash with the name of a user-defined module. pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; -/// The name of the crate-metadata object file the compiler generates. Must -/// match up with `METADATA_MODULE_NAME`. -pub const METADATA_OBJ_NAME: &'static str = "crate.metadata.o"; // same as for metadata above, but for allocator shim pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; -pub const ALLOCATOR_OBJ_NAME: &'static str = "crate.allocator.o"; pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target, invalid_output_for_target, build_link_meta, out_filename, @@ -129,6 +126,14 @@ fn command_path(sess: &Session) -> OsString { env::join_paths(new_path).unwrap() } +fn metadata_obj(outputs: &OutputFilenames) -> PathBuf { + outputs.temp_path(OutputType::Object, Some(METADATA_MODULE_NAME)) +} + +fn allocator_obj(outputs: &OutputFilenames) -> PathBuf { + outputs.temp_path(OutputType::Object, Some(ALLOCATOR_MODULE_NAME)) +} + pub fn remove(sess: &Session, path: &Path) { match fs::remove_file(path) { Ok(..) => {} @@ -174,9 +179,9 @@ pub fn link_binary(sess: &Session, remove(sess, &obj.object); } } - remove(sess, &outputs.with_extension(METADATA_OBJ_NAME)); + remove(sess, &metadata_obj(outputs)); if trans.allocator_module.is_some() { - remove(sess, &outputs.with_extension(ALLOCATOR_OBJ_NAME)); + remove(sess, &allocator_obj(outputs)); } } @@ -478,7 +483,7 @@ fn link_rlib<'a>(sess: &'a Session, RlibFlavor::StaticlibBase => { if trans.allocator_module.is_some() { - ab.add_file(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + ab.add_file(&allocator_obj(outputs)); } } } @@ -908,11 +913,11 @@ fn link_args(cmd: &mut Linker, // object file, so we link that in here. if crate_type == config::CrateTypeDylib || crate_type == config::CrateTypeProcMacro { - cmd.add_object(&outputs.with_extension(METADATA_OBJ_NAME)); + cmd.add_object(&metadata_obj(outputs)); } if trans.allocator_module.is_some() { - cmd.add_object(&outputs.with_extension(ALLOCATOR_OBJ_NAME)); + cmd.add_object(&allocator_obj(outputs)); } // Try to strip as much out of the generated object by removing unused @@ -1265,11 +1270,23 @@ fn add_upstream_rust_crates(cmd: &mut Linker, let canonical = f.replace("-", "_"); let canonical_name = name.replace("-", "_"); + // Look for `.rust-cgu.o` at the end of the filename to conclude + // that this is a Rust-related object file. + fn looks_like_rust(s: &str) -> bool { + let path = Path::new(s); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + return false + } + let ext2 = path.file_stem() + .and_then(|s| Path::new(s).extension()) + .and_then(|s| s.to_str()); + ext2 == Some(RUST_CGU_EXT) + } + let is_rust_object = - canonical.starts_with(&canonical_name) && { - let num = &f[name.len()..f.len() - 2]; - num.len() > 0 && num[1..].parse::<u32>().is_ok() - }; + canonical.starts_with(&canonical_name) && + looks_like_rust(&f); // If we've been requested to skip all native object files // (those not generated by the rust compiler) then we can skip diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 8651b95b12a..8f75b891a30 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -9,23 +9,25 @@ // except according to those terms. use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; -use back::write; use back::symbol_export; -use rustc::session::config; +use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; +use back::write; use errors::{FatalError, Handler}; -use llvm; use llvm::archive_ro::ArchiveRO; use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm; +use rustc::hir::def_id::LOCAL_CRATE; use rustc::middle::exported_symbols::SymbolExportLevel; +use rustc::session::config; use rustc::util::common::time; -use rustc::hir::def_id::LOCAL_CRATE; -use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; -use {ModuleTranslation, ModuleKind}; +use time_graph::Timeline; +use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource}; use libc; use std::ffi::CString; use std::slice; +use std::sync::Arc; pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { match crate_type { @@ -45,14 +47,14 @@ pub enum LtoModuleTranslation { _serialized_bitcode: Vec<SerializedModule>, }, - // Note the lack of other entries in this enum! Ideally one day this gap is - // intended to be filled with a "Thin" LTO variant. + Thin(ThinModule), } impl LtoModuleTranslation { pub fn name(&self) -> &str { match *self { LtoModuleTranslation::Fat { .. } => "everything", + LtoModuleTranslation::Thin(ref m) => m.name(), } } @@ -62,7 +64,9 @@ impl LtoModuleTranslation { /// points to LLVM data structures owned by this `LtoModuleTranslation`. /// It's intended that the module returned is immediately code generated and /// dropped, and then this LTO module is dropped. - pub unsafe fn optimize(&mut self, cgcx: &CodegenContext) + pub unsafe fn optimize(&mut self, + cgcx: &CodegenContext, + timeline: &mut Timeline) -> Result<ModuleTranslation, FatalError> { match *self { @@ -71,9 +75,11 @@ impl LtoModuleTranslation { let config = cgcx.config(trans.kind); let llmod = trans.llvm().unwrap().llmod; let tm = trans.llvm().unwrap().tm; - run_pass_manager(cgcx, tm, llmod, config); + run_pass_manager(cgcx, tm, llmod, config, false); + timeline.record("fat-done"); Ok(trans) } + LtoModuleTranslation::Thin(ref mut thin) => thin.optimize(cgcx, timeline), } } @@ -83,33 +89,31 @@ impl LtoModuleTranslation { match *self { // Only one module with fat LTO, so the cost doesn't matter. LtoModuleTranslation::Fat { .. } => 0, + LtoModuleTranslation::Thin(ref m) => m.cost(), } } } -pub fn run(cgcx: &CodegenContext, modules: Vec<ModuleTranslation>) +pub enum LTOMode { + WholeCrateGraph, + JustThisCrate, +} + +pub fn run(cgcx: &CodegenContext, + modules: Vec<ModuleTranslation>, + mode: LTOMode, + timeline: &mut Timeline) -> Result<Vec<LtoModuleTranslation>, FatalError> { let diag_handler = cgcx.create_diag_handler(); - if cgcx.opts.cg.prefer_dynamic { - diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") - .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ - supported with LTO") - .emit(); - return Err(FatalError) - } - - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ - static library outputs"); - return Err(e) + let export_threshold = match mode { + LTOMode::WholeCrateGraph => { + symbol_export::crates_export_threshold(&cgcx.crate_types) } - } - - let export_threshold = - symbol_export::crates_export_threshold(&cgcx.crate_types); + LTOMode::JustThisCrate => { + SymbolExportLevel::Rust + } + }; let symbol_filter = &|&(ref name, _, level): &(String, _, SymbolExportLevel)| { if level.is_below_threshold(export_threshold) { @@ -121,55 +125,81 @@ pub fn run(cgcx: &CodegenContext, modules: Vec<ModuleTranslation>) } }; - let mut symbol_white_list: Vec<CString> = cgcx.exported_symbols[&LOCAL_CRATE] + let mut symbol_white_list = cgcx.exported_symbols[&LOCAL_CRATE] .iter() .filter_map(symbol_filter) - .collect(); - info!("{} symbols in whitelist", symbol_white_list.len()); + .collect::<Vec<CString>>(); + timeline.record("whitelist"); - // For each of our upstream dependencies, find the corresponding rlib and - // load the bitcode from the archive. Then merge it into the current LLVM - // module that we've got. + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, find the corresponding rlib and load the bitcode + // from the archive. + // + // We save off all the bytecode and LLVM module ids for later processing + // with either fat or thin LTO let mut upstream_modules = Vec::new(); - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - symbol_white_list.extend( - cgcx.exported_symbols[&cnum] - .iter() - .filter_map(symbol_filter)); - info!("{} symbols in whitelist after {}", symbol_white_list.len(), cnum); - - let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let bytecodes = archive.iter().filter_map(|child| { - child.ok().and_then(|c| c.name().map(|name| (name, c))) - }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); - for (name, data) in bytecodes { - info!("adding bytecode {}", name); - let bc_encoded = data.data(); - - let (bc, id) = time(cgcx.time_passes, &format!("decode {}", name), || { - match DecodedBytecode::new(bc_encoded) { - Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), - Err(e) => Err(diag_handler.fatal(&e)), - } - })?; - let bc = SerializedModule::FromRlib(bc); - upstream_modules.push((bc, CString::new(id).unwrap())); + if let LTOMode::WholeCrateGraph = mode { + if cgcx.opts.cg.prefer_dynamic { + diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ + supported with LTO") + .emit(); + return Err(FatalError) + } + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ + static library outputs"); + return Err(e) + } } - } - // Internalize everything but the exported symbols of the current module - let arr: Vec<*const libc::c_char> = symbol_white_list.iter() - .map(|c| c.as_ptr()) - .collect(); + for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + symbol_white_list.extend( + cgcx.exported_symbols[&cnum] + .iter() + .filter_map(symbol_filter)); + + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); + let bytecodes = archive.iter().filter_map(|child| { + child.ok().and_then(|c| c.name().map(|name| (name, c))) + }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); + for (name, data) in bytecodes { + info!("adding bytecode {}", name); + let bc_encoded = data.data(); + + let (bc, id) = time(cgcx.time_passes, &format!("decode {}", name), || { + match DecodedBytecode::new(bc_encoded) { + Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), + Err(e) => Err(diag_handler.fatal(&e)), + } + })?; + let bc = SerializedModule::FromRlib(bc); + upstream_modules.push((bc, CString::new(id).unwrap())); + } + timeline.record(&format!("load: {}", path.display())); + } + } - fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr) + let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::<Vec<_>>(); + match mode { + LTOMode::WholeCrateGraph if !cgcx.thinlto => { + fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) + } + _ => { + thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) + } + } } fn fat_lto(cgcx: &CodegenContext, diag_handler: &Handler, mut modules: Vec<ModuleTranslation>, mut serialized_modules: Vec<(SerializedModule, CString)>, - symbol_white_list: &[*const libc::c_char]) + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) -> Result<Vec<LtoModuleTranslation>, FatalError> { info!("going for a fat lto"); @@ -228,6 +258,7 @@ fn fat_lto(cgcx: &CodegenContext, Err(write::llvm_err(&diag_handler, msg)) } })?; + timeline.record(&format!("link {:?}", name)); serialized_bitcode.push(bc_decoded); } cgcx.save_temp_bitcode(&module, "lto.input"); @@ -248,6 +279,7 @@ fn fat_lto(cgcx: &CodegenContext, } cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); } + timeline.record("passes"); Ok(vec![LtoModuleTranslation::Fat { module: Some(module), @@ -255,11 +287,143 @@ fn fat_lto(cgcx: &CodegenContext, }]) } +/// Prepare "thin" LTO to get run on these modules. +/// +/// The general structure of ThinLTO is quite different from the structure of +/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into +/// one giant LLVM module, and then we run more optimization passes over this +/// big module after internalizing most symbols. Thin LTO, on the other hand, +/// avoid this large bottleneck through more targeted optimization. +/// +/// At a high level Thin LTO looks like: +/// +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. +/// +/// The summary for each module is intended to be quite cheap, and the global +/// index is relatively quite cheap to create as well. As a result, the goal of +/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more +/// situations. For example one cheap optimization is that we can parallelize +/// all codegen modules, easily making use of all the cores on a machine. +/// +/// With all that in mind, the function here is designed at specifically just +/// calculating the *index* for ThinLTO. This index will then be shared amongst +/// all of the `LtoModuleTranslation` units returned below and destroyed once +/// they all go out of scope. +fn thin_lto(diag_handler: &Handler, + modules: Vec<ModuleTranslation>, + serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result<Vec<LtoModuleTranslation>, FatalError> +{ + unsafe { + info!("going for that thin, thin LTO"); + + let mut thin_buffers = Vec::new(); + let mut module_names = Vec::new(); + let mut thin_modules = Vec::new(); + + // FIXME: right now, like with fat LTO, we serialize all in-memory + // modules before working with them and ThinLTO. We really + // shouldn't do this, however, and instead figure out how to + // extract a summary from an in-memory module and then merge that + // into the global index. It turns out that this loop is by far + // the most expensive portion of this small bit of global + // analysis! + for (i, module) in modules.iter().enumerate() { + info!("local module: {} - {}", i, module.llmod_id); + let llvm = module.llvm().expect("can't lto pretranslated module"); + let name = CString::new(module.llmod_id.clone()).unwrap(); + let buffer = llvm::LLVMRustThinLTOBufferCreate(llvm.llmod); + let buffer = ThinBuffer(buffer); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: buffer.data().as_ptr(), + len: buffer.data().len(), + }); + thin_buffers.push(buffer); + module_names.push(name); + timeline.record(&module.llmod_id); + } + + // FIXME: All upstream crates are deserialized internally in the + // function below to extract their summary and modules. Note that + // unlike the loop above we *must* decode and/or read something + // here as these are all just serialized files on disk. An + // improvement, however, to make here would be to store the + // module summary separately from the actual module itself. Right + // now this is store in one large bitcode file, and the entire + // file is deflate-compressed. We could try to bypass some of the + // decompression by storing the index uncompressed and only + // lazily decompressing the bytecode if necessary. + // + // Note that truly taking advantage of this optimization will + // likely be further down the road. We'd have to implement + // incremental ThinLTO first where we could actually avoid + // looking at upstream modules entirely sometimes (the contents, + // we must always unconditionally look at the index). + let mut serialized = Vec::new(); + for (module, name) in serialized_modules { + info!("foreign module {:?}", name); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: module.data().as_ptr(), + len: module.data().len(), + }); + serialized.push(module); + module_names.push(name); + } + + // Delegate to the C++ bindings to create some data here. Once this is a + // tried-and-true interface we may wish to try to upstream some of this + // to LLVM itself, right now we reimplement a lot of what they do + // upstream... + let data = llvm::LLVMRustCreateThinLTOData( + thin_modules.as_ptr(), + thin_modules.len() as u32, + symbol_white_list.as_ptr(), + symbol_white_list.len() as u32, + ); + if data.is_null() { + let msg = format!("failed to prepare thin LTO context"); + return Err(write::llvm_err(&diag_handler, msg)) + } + let data = ThinData(data); + info!("thin LTO data created"); + timeline.record("data"); + + // Throw our data in an `Arc` as we'll be sharing it across threads. We + // also put all memory referenced by the C++ data (buffers, ids, etc) + // into the arc as well. After this we'll create a thin module + // translation per module in this data. + let shared = Arc::new(ThinShared { + data, + thin_buffers, + serialized_modules: serialized, + module_names, + }); + Ok((0..shared.module_names.len()).map(|i| { + LtoModuleTranslation::Thin(ThinModule { + shared: shared.clone(), + idx: i, + }) + }).collect()) + } +} + fn run_pass_manager(cgcx: &CodegenContext, tm: TargetMachineRef, llmod: ModuleRef, - config: &ModuleConfig) { - + config: &ModuleConfig, + thin: bool) { // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. // @@ -274,9 +438,15 @@ fn run_pass_manager(cgcx: &CodegenContext, llvm::LLVMRustAddPass(pm, pass); with_llvm_pmb(llmod, config, &mut |b| { - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, - /* Internalize = */ False, - /* RunInliner = */ True); + if thin { + if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { + panic!("this version of LLVM does not support ThinLTO"); + } + } else { + llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, + /* Internalize = */ False, + /* RunInliner = */ True); + } }); let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); @@ -331,3 +501,158 @@ impl Drop for ModuleBuffer { unsafe { llvm::LLVMRustModuleBufferFree(self.0); } } } + +pub struct ThinModule { + shared: Arc<ThinShared>, + idx: usize, +} + +struct ThinShared { + data: ThinData, + thin_buffers: Vec<ThinBuffer>, + serialized_modules: Vec<SerializedModule>, + module_names: Vec<CString>, +} + +struct ThinData(*mut llvm::ThinLTOData); + +unsafe impl Send for ThinData {} +unsafe impl Sync for ThinData {} + +impl Drop for ThinData { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustFreeThinLTOData(self.0); + } + } +} + +struct ThinBuffer(*mut llvm::ThinLTOBuffer); + +unsafe impl Send for ThinBuffer {} +unsafe impl Sync for ThinBuffer {} + +impl ThinBuffer { + fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ThinBuffer { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustThinLTOBufferFree(self.0); + } + } +} + +impl ThinModule { + fn name(&self) -> &str { + self.shared.module_names[self.idx].to_str().unwrap() + } + + fn cost(&self) -> u64 { + // Yes, that's correct, we're using the size of the bytecode as an + // indicator for how costly this codegen unit is. + self.data().len() as u64 + } + + fn data(&self) -> &[u8] { + let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); + a.unwrap_or_else(|| { + let len = self.shared.thin_buffers.len(); + self.shared.serialized_modules[self.idx - len].data() + }) + } + + unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) + -> Result<ModuleTranslation, FatalError> + { + let diag_handler = cgcx.create_diag_handler(); + let tm = (cgcx.tm_factory)().map_err(|e| { + write::llvm_err(&diag_handler, e) + })?; + + // Right now the implementation we've got only works over serialized + // modules, so we create a fresh new LLVM context and parse the module + // into that context. One day, however, we may do this for upstream + // crates but for locally translated modules we may be able to reuse + // that LLVM Context and Module. + let llcx = llvm::LLVMContextCreate(); + let llmod = llvm::LLVMRustParseBitcodeForThinLTO( + llcx, + self.data().as_ptr(), + self.data().len(), + self.shared.module_names[self.idx].as_ptr(), + ); + assert!(!llmod.is_null()); + let mtrans = ModuleTranslation { + source: ModuleSource::Translated(ModuleLlvm { + llmod, + llcx, + tm, + }), + llmod_id: self.name().to_string(), + name: self.name().to_string(), + kind: ModuleKind::Regular, + }; + cgcx.save_temp_bitcode(&mtrans, "thin-lto-input"); + + // Like with "fat" LTO, get some better optimizations if landing pads + // are disabled by removing all landing pads. + if cgcx.no_landing_pads { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-nounwind"); + timeline.record("nounwind"); + } + + // Up next comes the per-module local analyses that we do for Thin LTO. + // Each of these functions is basically copied from the LLVM + // implementation and then tailored to suit this implementation. Ideally + // each of these would be supported by upstream LLVM but that's perhaps + // a patch for another day! + // + // You can find some more comments about these functions in the LLVM + // bindings we've got (currently `PassWrapper.cpp`) + if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rename"); + timeline.record("rename"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-resolve"); + timeline.record("resolve"); + if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-internalize"); + timeline.record("internalize"); + if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-import"); + timeline.record("import"); + + // Alright now that we've done everything related to the ThinLTO + // analysis it's time to run some optimizations! Here we use the same + // `run_pass_manager` as the "fat" LTO above except that we tell it to + // populate a thin-specific pass manager, which presumably LLVM treats a + // little differently. + info!("running thin lto passes over {}", mtrans.name); + let config = cgcx.config(mtrans.kind); + run_pass_manager(cgcx, tm, llmod, config, true); + cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); + timeline.record("thin-done"); + Ok(mtrans) + } +} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 1988b9f76e4..b3b2384a027 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -19,7 +19,7 @@ use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Pas AllPasses, Sanitizer}; use rustc::session::Session; use rustc::util::nodemap::FxHashMap; -use time_graph::{self, TimeGraph}; +use time_graph::{self, TimeGraph, Timeline}; use llvm; use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; use llvm::{SMDiagnosticRef, ContextRef}; @@ -303,6 +303,7 @@ pub struct CodegenContext { // Resouces needed when running LTO pub time_passes: bool, pub lto: bool, + pub thinlto: bool, pub no_landing_pads: bool, pub save_temps: bool, pub exported_symbols: Arc<ExportedSymbols>, @@ -315,6 +316,8 @@ pub struct CodegenContext { allocator_module_config: Arc<ModuleConfig>, pub tm_factory: Arc<Fn() -> Result<TargetMachineRef, String> + Send + Sync>, + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, // Handler to use for diagnostics produced during codegen. pub diag_emitter: SharedEmitter, // LLVM passes added by plugins. @@ -450,7 +453,8 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo unsafe fn optimize(cgcx: &CodegenContext, diag_handler: &Handler, mtrans: &ModuleTranslation, - config: &ModuleConfig) + config: &ModuleConfig, + timeline: &mut Timeline) -> Result<(), FatalError> { let (llmod, llcx, tm) = match mtrans.source { @@ -529,6 +533,7 @@ unsafe fn optimize(cgcx: &CodegenContext, // Finally, run the actual optimization passes time(config.time_passes, &format!("llvm function passes [{}]", module_name.unwrap()), || llvm::LLVMRustRunFunctionPassManager(fpm, llmod)); + timeline.record("fpm"); time(config.time_passes, &format!("llvm module passes [{}]", module_name.unwrap()), || llvm::LLVMRunPassManager(mpm, llmod)); @@ -543,7 +548,18 @@ fn generate_lto_work(cgcx: &CodegenContext, modules: Vec<ModuleTranslation>) -> Vec<(WorkItem, u64)> { - let lto_modules = lto::run(cgcx, modules).unwrap_or_else(|e| panic!(e)); + let mut timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(TRANS_WORKER_TIMELINE, + TRANS_WORK_PACKAGE_KIND, + "generate lto") + }).unwrap_or(Timeline::noop()); + let mode = if cgcx.lto { + lto::LTOMode::WholeCrateGraph + } else { + lto::LTOMode::JustThisCrate + }; + let lto_modules = lto::run(cgcx, modules, mode, &mut timeline) + .unwrap_or_else(|e| panic!(e)); lto_modules.into_iter().map(|module| { let cost = module.cost(); @@ -554,9 +570,11 @@ fn generate_lto_work(cgcx: &CodegenContext, unsafe fn codegen(cgcx: &CodegenContext, diag_handler: &Handler, mtrans: ModuleTranslation, - config: &ModuleConfig) + config: &ModuleConfig, + timeline: &mut Timeline) -> Result<CompiledModule, FatalError> { + timeline.record("codegen"); let (llmod, llcx, tm) = match mtrans.source { ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), ModuleSource::Preexisting(_) => { @@ -601,7 +619,18 @@ unsafe fn codegen(cgcx: &CodegenContext, if write_bc { let bc_out_c = path2cstr(&bc_out); - llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr()); + if llvm::LLVMRustThinLTOAvailable() { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + llvm::LLVMRustWriteThinBitcodeToFile( + cpm, + llmod, + bc_out_c.as_ptr(), + ) + }); + } else { + llvm::LLVMWriteBitcodeToFile(llmod, bc_out_c.as_ptr()); + } + timeline.record("bc"); } time(config.time_passes, &format!("codegen passes [{}]", module_name.unwrap()), @@ -644,7 +673,8 @@ unsafe fn codegen(cgcx: &CodegenContext, with_codegen(tm, llmod, config.no_builtins, |cpm| { llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); llvm::LLVMDisposePassManager(cpm); - }) + }); + timeline.record("ir"); } if config.emit_asm { @@ -665,6 +695,7 @@ unsafe fn codegen(cgcx: &CodegenContext, if config.emit_obj { llvm::LLVMDisposeModule(llmod); } + timeline.record("asm"); } if write_obj { @@ -672,6 +703,7 @@ unsafe fn codegen(cgcx: &CodegenContext, write_output_file(diag_handler, tm, cpm, llmod, &obj_out, llvm::FileType::ObjectFile) })?; + timeline.record("obj"); } Ok(()) @@ -712,7 +744,8 @@ pub fn start_async_translation(tcx: TyCtxt, time_graph: Option<TimeGraph>, link: LinkMeta, metadata: EncodedMetadata, - coordinator_receive: Receiver<Box<Any + Send>>) + coordinator_receive: Receiver<Box<Any + Send>>, + total_cgus: usize) -> OngoingCrateTranslation { let sess = tcx.sess; let crate_output = tcx.output_filenames(LOCAL_CRATE); @@ -836,6 +869,7 @@ pub fn start_async_translation(tcx: TyCtxt, shared_emitter, trans_worker_send, coordinator_receive, + total_cgus, client, time_graph.clone(), Arc::new(modules_config), @@ -1080,7 +1114,9 @@ enum WorkItemResult { NeedsLTO(ModuleTranslation), } -fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) +fn execute_work_item(cgcx: &CodegenContext, + work_item: WorkItem, + timeline: &mut Timeline) -> Result<WorkItemResult, FatalError> { let diag_handler = cgcx.create_diag_handler(); @@ -1089,8 +1125,8 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) WorkItem::Optimize(mtrans) => mtrans, WorkItem::LTO(mut lto) => { unsafe { - let module = lto.optimize(cgcx)?; - let module = codegen(cgcx, &diag_handler, module, config)?; + let module = lto.optimize(cgcx, timeline)?; + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; return Ok(WorkItemResult::Compiled(module)) } } @@ -1140,9 +1176,27 @@ fn execute_work_item(cgcx: &CodegenContext, work_item: WorkItem) debug!("llvm-optimizing {:?}", module_name); unsafe { - optimize(cgcx, &diag_handler, &mtrans, config)?; - if !cgcx.lto || mtrans.kind == ModuleKind::Metadata { - let module = codegen(cgcx, &diag_handler, mtrans, config)?; + optimize(cgcx, &diag_handler, &mtrans, config, timeline)?; + + let lto = cgcx.lto; + + let auto_thin_lto = + cgcx.thinlto && + cgcx.total_cgus > 1 && + mtrans.kind != ModuleKind::Allocator; + + // If we're a metadata module we never participate in LTO. + // + // If LTO was explicitly requested on the command line, we always + // LTO everything else. + // + // If LTO *wasn't* explicitly requested and we're not a metdata + // module, then we may automatically do ThinLTO if we've got + // multiple codegen units. Note, however, that the allocator module + // doesn't participate here automatically because of linker + // shenanigans later on. + if mtrans.kind == ModuleKind::Metadata || (!lto && !auto_thin_lto) { + let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?; Ok(WorkItemResult::Compiled(module)) } else { Ok(WorkItemResult::NeedsLTO(mtrans)) @@ -1187,6 +1241,7 @@ fn start_executing_work(tcx: TyCtxt, shared_emitter: SharedEmitter, trans_worker_send: Sender<Message>, coordinator_receive: Receiver<Box<Any + Send>>, + total_cgus: usize, jobserver: Client, time_graph: Option<TimeGraph>, modules_config: Arc<ModuleConfig>, @@ -1229,6 +1284,7 @@ fn start_executing_work(tcx: TyCtxt, crate_types: sess.crate_types.borrow().clone(), each_linked_rlib_for_lto, lto: sess.lto(), + thinlto: sess.opts.debugging_opts.thinlto, no_landing_pads: sess.no_landing_pads(), save_temps: sess.opts.cg.save_temps, opts: Arc::new(sess.opts.clone()), @@ -1246,6 +1302,7 @@ fn start_executing_work(tcx: TyCtxt, metadata_module_config: metadata_config, allocator_module_config: allocator_config, tm_factory: target_machine_factory(tcx.sess), + total_cgus, }; // This is the "main loop" of parallel work happening for parallel codegen. @@ -1743,12 +1800,13 @@ fn spawn_work(cgcx: CodegenContext, work: WorkItem) { // as a diagnostic was already sent off to the main thread - just // surface that there was an error in this worker. bomb.result = { - let _timing_guard = cgcx.time_graph.as_ref().map(|tg| { + let timeline = cgcx.time_graph.as_ref().map(|tg| { tg.start(time_graph::TimelineId(cgcx.worker), LLVM_WORK_PACKAGE_KIND, &work.name()) }); - execute_work_item(&cgcx, work).ok() + let mut timeline = timeline.unwrap_or(Timeline::noop()); + execute_work_item(&cgcx, work, &mut timeline).ok() }; }); } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 8c39f579b3e..17e00ac1346 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -886,6 +886,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, check_for_rustc_errors_attr(tcx); + if tcx.sess.opts.debugging_opts.thinlto { + if unsafe { !llvm::LLVMRustThinLTOAvailable() } { + tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); + } + } let crate_hash = tcx.dep_graph .fingerprint_of(&DepNode::new_no_params(DepKind::Krate)); @@ -925,7 +930,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time_graph.clone(), link_meta, metadata, - rx); + rx, + 1); ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); ongoing_translation.translation_finished(tcx); @@ -961,7 +967,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time_graph.clone(), link_meta, metadata, - rx); + rx, + codegen_units.len()); // Translate an allocator shim, if any let allocator_module = if let Some(kind) = tcx.sess.allocator_kind.get() { @@ -1224,9 +1231,6 @@ fn collect_and_partition_translation_items<'a, 'tcx>( .collect::<Vec<_>>() }); - assert!(tcx.sess.opts.codegen_units == codegen_units.len() || - tcx.sess.opts.debugging_opts.incremental.is_some()); - let translation_items: DefIdSet = items.iter().filter_map(|trans_item| { match *trans_item { TransItem::Fn(ref instance) => Some(instance.def_id()), @@ -1372,7 +1376,9 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // crashes if the module identifier is same as other symbols // such as a function name in the module. // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}.rs", cgu.name()); + let llmod_id = format!("{}-{}.rs", + cgu.name(), + tcx.crate_disambiguator(LOCAL_CRATE)); // Instantiate translation items without filling out definitions yet... let scx = SharedCrateContext::new(tcx); diff --git a/src/librustc_trans/partitioning.rs b/src/librustc_trans/partitioning.rs index 7b6daa7d133..4656d212f9b 100644 --- a/src/librustc_trans/partitioning.rs +++ b/src/librustc_trans/partitioning.rs @@ -108,7 +108,6 @@ use rustc::dep_graph::{DepNode, WorkProductId}; use rustc::hir::def_id::DefId; use rustc::hir::map::DefPathData; use rustc::middle::trans::{Linkage, Visibility}; -use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER; use rustc::ty::{self, TyCtxt, InstanceDef}; use rustc::ty::item_path::characteristic_def_id_of_type; use rustc::util::nodemap::{FxHashMap, FxHashSet}; @@ -391,15 +390,6 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< for (index, cgu) in codegen_units.iter_mut().enumerate() { cgu.set_name(numbered_codegen_unit_name(crate_name, index)); } - - // If the initial partitioning contained less than target_cgu_count to begin - // with, we won't have enough codegen units here, so add a empty units until - // we reach the target count - while codegen_units.len() < target_cgu_count { - let index = codegen_units.len(); - let name = numbered_codegen_unit_name(crate_name, index); - codegen_units.push(CodegenUnit::new(name)); - } } fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, @@ -627,7 +617,7 @@ fn compute_codegen_unit_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } fn numbered_codegen_unit_name(crate_name: &str, index: usize) -> InternedString { - Symbol::intern(&format!("{}{}{}", crate_name, NUMBERED_CODEGEN_UNIT_MARKER, index)).as_str() + Symbol::intern(&format!("{}{}", crate_name, index)).as_str() } fn debug_dump<'a, 'b, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, diff --git a/src/librustc_trans/time_graph.rs b/src/librustc_trans/time_graph.rs index ec57af888e5..a8502682a80 100644 --- a/src/librustc_trans/time_graph.rs +++ b/src/librustc_trans/time_graph.rs @@ -9,11 +9,12 @@ // except according to those terms. use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; use std::marker::PhantomData; +use std::mem; use std::sync::{Arc, Mutex}; use std::time::Instant; -use std::io::prelude::*; -use std::fs::File; const OUTPUT_WIDTH_IN_PX: u64 = 1000; const TIME_LINE_HEIGHT_IN_PX: u64 = 20; @@ -25,6 +26,7 @@ struct Timing { end: Instant, work_package_kind: WorkPackageKind, name: String, + events: Vec<(String, Instant)>, } #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] @@ -44,9 +46,14 @@ pub struct TimeGraph { #[derive(Clone, Copy)] pub struct WorkPackageKind(pub &'static [&'static str]); -pub struct RaiiToken { +pub struct Timeline { + token: Option<RaiiToken>, +} + +struct RaiiToken { graph: TimeGraph, timeline: TimelineId, + events: Vec<(String, Instant)>, // The token must not be Send: _marker: PhantomData<*const ()> } @@ -54,7 +61,7 @@ pub struct RaiiToken { impl Drop for RaiiToken { fn drop(&mut self) { - self.graph.end(self.timeline); + self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); } } @@ -68,7 +75,7 @@ impl TimeGraph { pub fn start(&self, timeline: TimelineId, work_package_kind: WorkPackageKind, - name: &str) -> RaiiToken { + name: &str) -> Timeline { { let mut table = self.data.lock().unwrap(); @@ -81,14 +88,17 @@ impl TimeGraph { data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); } - RaiiToken { - graph: self.clone(), - timeline, - _marker: PhantomData, + Timeline { + token: Some(RaiiToken { + graph: self.clone(), + timeline, + events: Vec::new(), + _marker: PhantomData, + }), } } - fn end(&self, timeline: TimelineId) { + fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { let end = Instant::now(); let mut table = self.data.lock().unwrap(); @@ -100,6 +110,7 @@ impl TimeGraph { end, work_package_kind, name, + events, }); } else { bug!("end timing without start?") @@ -113,13 +124,13 @@ impl TimeGraph { assert!(data.open_work_package.is_none()); } - let mut timelines: Vec<PerThread> = + let mut threads: Vec<PerThread> = table.values().map(|data| data.clone()).collect(); - timelines.sort_by_key(|timeline| timeline.timings[0].start); + threads.sort_by_key(|timeline| timeline.timings[0].start); - let earliest_instant = timelines[0].timings[0].start; - let latest_instant = timelines.iter() + let earliest_instant = threads[0].timings[0].start; + let latest_instant = threads.iter() .map(|timeline| timeline.timings .last() .unwrap() @@ -130,16 +141,46 @@ impl TimeGraph { let mut file = File::create(format!("{}.html", output_filename)).unwrap(); - writeln!(file, "<html>").unwrap(); - writeln!(file, "<head></head>").unwrap(); - writeln!(file, "<body>").unwrap(); + writeln!(file, " + <html> + <head> + <style> + #threads a {{ + position: absolute; + overflow: hidden; + }} + #threads {{ + height: {total_height}px; + width: {width}px; + }} + + .timeline {{ + display: none; + width: {width}px; + position: relative; + }} + + .timeline:target {{ + display: block; + }} + + .event {{ + position: absolute; + }} + </style> + </head> + <body> + <div id='threads'> + ", + total_height = threads.len() * TIME_LINE_HEIGHT_STRIDE_IN_PX, + width = OUTPUT_WIDTH_IN_PX, + ).unwrap(); let mut color = 0; - - for (line_index, timeline) in timelines.iter().enumerate() { + for (line_index, thread) in threads.iter().enumerate() { let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX; - for span in &timeline.timings { + for span in &thread.timings { let start = distance(earliest_instant, span.start); let end = distance(earliest_instant, span.end); @@ -148,13 +189,13 @@ impl TimeGraph { let colors = span.work_package_kind.0; - writeln!(file, "<div style='position:absolute; \ - overflow:hidden; \ - top:{}px; \ - left:{}px; \ - width:{}px; \ - height:{}px; \ - background:{};'>{}</div>", + writeln!(file, "<a href='#timing{}' + style='top:{}px; \ + left:{}px; \ + width:{}px; \ + height:{}px; \ + background:{};'>{}</a>", + color, line_top, start, end - start, @@ -167,8 +208,61 @@ impl TimeGraph { } } - writeln!(file, "</body>").unwrap(); - writeln!(file, "</html>").unwrap(); + writeln!(file, " + </div> + ").unwrap(); + + let mut idx = 0; + for thread in threads.iter() { + for timing in &thread.timings { + let colors = timing.work_package_kind.0; + let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); + writeln!(file, "<div class='timeline' + id='timing{}' + style='background:{};height:{}px;'>", + idx, + colors[idx % colors.len()], + height).unwrap(); + idx += 1; + let max = distance(timing.start, timing.end); + for (i, &(ref event, time)) in timing.events.iter().enumerate() { + let i = i as u64; + let time = distance(timing.start, time); + let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); + writeln!(file, "<span class='event' + style='left:{}px;\ + top:{}px;'>{}</span>", + at, + TIME_LINE_HEIGHT_IN_PX * i, + event).unwrap(); + } + writeln!(file, "</div>").unwrap(); + } + } + + writeln!(file, " + </body> + </html> + ").unwrap(); + } +} + +impl Timeline { + pub fn noop() -> Timeline { + Timeline { token: None } + } + + /// Record an event which happened at this moment on this timeline. + /// + /// Events are displayed in the eventual HTML output where you can click on + /// a particular timeline and it'll expand to all of the events that + /// happened on that timeline. This can then be used to drill into a + /// particular timeline and see what events are happening and taking the + /// most time. + pub fn record(&mut self, name: &str) { + if let Some(ref mut token) = self.token { + token.events.push((name.to_string(), Instant::now())); + } } } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index da8c3a5cf20..e3ce403f3c1 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -337,7 +337,6 @@ impl<'a> fmt::Display for Html<'a> { "l4re" => "L4Re", "linux" => "Linux", "macos" => "macOS", - "nacl" => "NaCl", "netbsd" => "NetBSD", "openbsd" => "OpenBSD", "redox" => "Redox", @@ -886,4 +885,4 @@ mod test { only." ); } -} \ No newline at end of file +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c9afa3646b2..424f48a17e9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2491,7 +2491,7 @@ impl Clean<BareFunctionDecl> for hir::BareFnTy { type_params: Vec::new(), where_predicates: Vec::new() }, - decl: (&*self.decl, &[][..]).clean(cx), + decl: (&*self.decl, &self.arg_names[..]).clean(cx), abi: self.abi, } } diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index d6b41ceda43..fe1179a3b4a 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -206,6 +206,14 @@ impl<'a> Read for &'a [u8] { *self = b; Ok(()) } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + buf.extend_from_slice(*self); + let len = self.len(); + *self = &self[len..]; + Ok(len) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index b460bd90f17..122f15d1d4c 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -38,7 +38,6 @@ pub mod linux; #[cfg(all(not(dox), target_os = "haiku"))] pub mod haiku; #[cfg(all(not(dox), target_os = "ios"))] pub mod ios; #[cfg(all(not(dox), target_os = "macos"))] pub mod macos; -#[cfg(all(not(dox), target_os = "nacl"))] pub mod nacl; #[cfg(all(not(dox), target_os = "netbsd"))] pub mod netbsd; #[cfg(all(not(dox), target_os = "openbsd"))] pub mod openbsd; #[cfg(all(not(dox), target_os = "solaris"))] pub mod solaris; diff --git a/src/libstd/os/nacl/fs.rs b/src/libstd/os/nacl/fs.rs deleted file mode 100644 index 3e0fb44b01e..00000000000 --- a/src/libstd/os/nacl/fs.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![stable(feature = "metadata_ext", since = "1.1.0")] - -use libc; - -use fs::Metadata; -use sys_common::AsInner; - -#[allow(deprecated)] -use os::nacl::raw; - -/// OS-specific extension methods for `fs::Metadata` -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Gain a reference to the underlying `stat` structure which contains - /// the raw information returned by the OS. - /// - /// The contents of the returned `stat` are **not** consistent across - /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the - /// cross-Unix abstractions contained within the raw stat. - #[stable(feature = "metadata_ext", since = "1.1.0")] - #[rustc_deprecated(since = "1.8.0", - reason = "deprecated in favor of the accessor \ - methods of this trait")] - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat; - - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_dev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ino(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mode(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_nlink(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_uid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_gid(&self) -> u32; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_rdev(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_size(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_atime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_mtime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_ctime_nsec(&self) -> i64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blksize(&self) -> u64; - #[stable(feature = "metadata_ext2", since = "1.8.0")] - fn st_blocks(&self) -> u64; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for Metadata { - #[allow(deprecated)] - fn as_raw_stat(&self) -> &raw::stat { - unsafe { - &*(self.as_inner().as_inner() as *const libc::stat64 - as *const raw::stat) - } - } - fn st_dev(&self) -> u64 { - self.as_inner().as_inner().st_dev as u64 - } - fn st_ino(&self) -> u64 { - self.as_inner().as_inner().st_ino as u64 - } - fn st_mode(&self) -> u32 { - self.as_inner().as_inner().st_mode as u32 - } - fn st_nlink(&self) -> u64 { - self.as_inner().as_inner().st_nlink as u64 - } - fn st_uid(&self) -> u32 { - self.as_inner().as_inner().st_uid as u32 - } - fn st_gid(&self) -> u32 { - self.as_inner().as_inner().st_gid as u32 - } - fn st_rdev(&self) -> u64 { - self.as_inner().as_inner().st_rdev as u64 - } - fn st_size(&self) -> u64 { - self.as_inner().as_inner().st_size as u64 - } - fn st_atime(&self) -> i64 { - self.as_inner().as_inner().st_atime as i64 - } - fn st_atime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_atime_nsec as i64 - } - fn st_mtime(&self) -> i64 { - self.as_inner().as_inner().st_mtime as i64 - } - fn st_mtime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_mtime_nsec as i64 - } - fn st_ctime(&self) -> i64 { - self.as_inner().as_inner().st_ctime as i64 - } - fn st_ctime_nsec(&self) -> i64 { - self.as_inner().as_inner().st_ctime_nsec as i64 - } - fn st_blksize(&self) -> u64 { - self.as_inner().as_inner().st_blksize as u64 - } - fn st_blocks(&self) -> u64 { - self.as_inner().as_inner().st_blocks as u64 - } -} diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs deleted file mode 100644 index 3c3d4410a2a..00000000000 --- a/src/libstd/os/nacl/raw.rs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Nacl-specific raw type definitions - -#![stable(feature = "raw_ext", since = "1.1.0")] -#![rustc_deprecated(since = "1.8.0", - reason = "these type aliases are no longer supported by \ - the standard library, the `libc` crate on \ - crates.io should be used instead for the correct \ - definitions")] -#![allow(deprecated)] - -#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type ino_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type nlink_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; -#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; - -#[stable(feature = "pthread_t", since = "1.8.0")] -pub type pthread_t = usize; - -#[repr(C)] -#[derive(Copy, Clone)] -#[stable(feature = "raw_ext", since = "1.1.0")] -pub struct stat { - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_dev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ino: ino_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mode: mode_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_nlink: nlink_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_uid: uid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_gid: gid_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_rdev: dev_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_size: off_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blksize: blksize_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_blocks: blkcnt_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_atime_nsec: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_mtime_nsec: i64, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime: time_t, - #[stable(feature = "raw_ext", since = "1.1.0")] pub st_ctime_nsec: i64, -} diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index 3d9a06bedd5..00cf7eca75d 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -118,27 +118,6 @@ pub mod os { pub const EXE_EXTENSION: &'static str = ""; } -#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] -pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "nacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".nexe"; - pub const EXE_EXTENSION: &'static str = "nexe"; -} -#[cfg(all(target_os = "nacl", target_arch = "le32"))] -pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "pnacl"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".pso"; - pub const DLL_EXTENSION: &'static str = "pso"; - pub const EXE_SUFFIX: &'static str = ".pexe"; - pub const EXE_EXTENSION: &'static str = "pexe"; -} - #[cfg(target_os = "haiku")] pub mod os { pub const FAMILY: &'static str = "unix"; diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 1b3f1000b77..c2772e2e2cc 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -22,7 +22,6 @@ use libc; #[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform; #[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform; #[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform; -#[cfg(all(not(dox), target_os = "nacl"))] pub use os::nacl as platform; #[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform; #[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform; #[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform; @@ -77,11 +76,11 @@ pub fn init() { reset_sigpipe(); } - #[cfg(not(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia")))] + #[cfg(not(any(target_os = "emscripten", target_os="fuchsia")))] unsafe fn reset_sigpipe() { assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); } - #[cfg(any(target_os = "nacl", target_os = "emscripten", target_os="fuchsia"))] + #[cfg(any(target_os = "emscripten", target_os="fuchsia"))] unsafe fn reset_sigpipe() {} } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 5ef98d24710..d8c30534eed 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -483,12 +483,10 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(any(target_os = "android", target_os = "ios", - target_os = "nacl", target_os = "emscripten"))] unsafe fn fallback() -> Option<OsString> { None } #[cfg(not(any(target_os = "android", target_os = "ios", - target_os = "nacl", target_os = "emscripten")))] unsafe fn fallback() -> Option<OsString> { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { diff --git a/src/libstd/sys/unix/process/process_common.rs b/src/libstd/sys/unix/process/process_common.rs index 689ccd78524..383434b1cd8 100644 --- a/src/libstd/sys/unix/process/process_common.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -464,7 +464,6 @@ mod tests { // test from being flaky we ignore it on macOS. #[test] #[cfg_attr(target_os = "macos", ignore)] - #[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl. // When run under our current QEMU emulation test suite this test fails, // although the reason isn't very clear as to why. For now this test is // ignored there. diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs index 5d34da04446..a7a67ed36e8 100644 --- a/src/libstd/sys/unix/process/process_fuchsia.rs +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -9,7 +9,7 @@ // except according to those terms. use io; -use libc; +use libc::{self, size_t}; use mem; use ptr; @@ -148,8 +148,8 @@ impl Process { use sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: zx_size_t = 0; - let mut avail: zx_size_t = 0; + let mut actual: size_t = 0; + let mut avail: size_t = 0; unsafe { zx_cvt(zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, @@ -171,8 +171,8 @@ impl Process { use sys::process::zircon::*; let mut proc_info: zx_info_process_t = Default::default(); - let mut actual: zx_size_t = 0; - let mut avail: zx_size_t = 0; + let mut actual: size_t = 0; + let mut avail: size_t = 0; unsafe { let status = zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index 870db820027..743c458d580 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -184,8 +184,8 @@ impl Command { *sys::os::environ() = envp.as_ptr(); } - // NaCl has no signal support. - #[cfg(not(any(target_os = "nacl", target_os = "emscripten")))] + // emscripten has no signal support. + #[cfg(not(any(target_os = "emscripten")))] { use mem; // Reset signal handling so the child process starts in a diff --git a/src/libstd/sys/unix/process/zircon.rs b/src/libstd/sys/unix/process/zircon.rs index b5ec11b40fd..90864e6ef3f 100644 --- a/src/libstd/sys/unix/process/zircon.rs +++ b/src/libstd/sys/unix/process/zircon.rs @@ -15,15 +15,13 @@ use io; use os::raw::c_char; use u64; -use libc::{c_int, c_void}; +use libc::{c_int, c_void, size_t}; -pub type zx_handle_t = i32; +pub type zx_handle_t = u32; pub type zx_vaddr_t = usize; pub type zx_rights_t = u32; pub type zx_status_t = i32; -pub type zx_size_t = usize; - pub const ZX_HANDLE_INVALID: zx_handle_t = 0; pub type zx_time_t = u64; @@ -115,36 +113,37 @@ extern { pending: *mut zx_signals_t) -> zx_status_t; pub fn zx_object_get_info(handle: zx_handle_t, topic: u32, buffer: *mut c_void, - buffer_size: zx_size_t, actual_size: *mut zx_size_t, - avail: *mut zx_size_t) -> zx_status_t; + buffer_size: size_t, actual_size: *mut size_t, + avail: *mut size_t) -> zx_status_t; } // From `enum special_handles` in system/ulib/launchpad/launchpad.c // HND_LOADER_SVC = 0 // HND_EXEC_VMO = 1 -pub const HND_SPECIAL_COUNT: usize = 2; +// HND_SEGMENTS_VMAR = 2 +const HND_SPECIAL_COUNT: c_int = 3; #[repr(C)] pub struct launchpad_t { argc: u32, envc: u32, args: *const c_char, - args_len: usize, + args_len: size_t, env: *const c_char, - env_len: usize, + env_len: size_t, handles: *mut zx_handle_t, handles_info: *mut u32, - handle_count: usize, - handle_alloc: usize, + handle_count: size_t, + handle_alloc: size_t, entry: zx_vaddr_t, base: zx_vaddr_t, vdso_base: zx_vaddr_t, - stack_size: usize, + stack_size: size_t, - special_handles: [zx_handle_t; HND_SPECIAL_COUNT], + special_handles: [zx_handle_t; HND_SPECIAL_COUNT as usize], loader_message: bool, } diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 617218fe7a5..8f78c2e6f59 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg_attr(target_os = "nacl", allow(dead_code))] - /// Common code for printing the backtrace in the same way across the different /// supported platforms. diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 30887b16c60..07bbddc62b9 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -485,15 +485,17 @@ impl Builder { /// let (tx, rx) = channel(); /// /// let sender = thread::spawn(move || { -/// let _ = tx.send("Hello, thread".to_owned()); +/// tx.send("Hello, thread".to_owned()) +/// .expect("Unable to send on channel"); /// }); /// /// let receiver = thread::spawn(move || { -/// println!("{}", rx.recv().unwrap()); +/// let value = rx.recv().expect("Unable to receive from channel"); +/// println!("{}", value); /// }); /// -/// let _ = sender.join(); -/// let _ = receiver.join(); +/// sender.join().expect("The sender thread has panicked"); +/// receiver.join().expect("The receiver thread has panicked"); /// ``` /// /// A thread can also return a value through its [`JoinHandle`], you can use @@ -1192,7 +1194,7 @@ impl<T> JoinInner<T> { /// }); /// }); /// -/// let _ = original_thread.join(); +/// original_thread.join().expect("The thread being joined has panicked"); /// println!("Original thread is joined."); /// /// // We make sure that the new thread has time to run, before the main diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 65dabe98a06..978e06c75dd 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3671,12 +3671,17 @@ impl<'a> Parser<'a> { None }; let init = self.parse_initializer()?; + let hi = if self.token == token::Semi { + self.span + } else { + self.prev_span + }; Ok(P(ast::Local { ty, pat, init, id: ast::DUMMY_NODE_ID, - span: lo.to(self.prev_span), + span: lo.to(hi), attrs, })) } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 642eb285564..e8a1242c814 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -1554,16 +1554,14 @@ impl MetricMap { /// elimination. /// /// This function is a no-op, and does not even read from `dummy`. -#[cfg(not(any(all(target_os = "nacl", target_arch = "le32"), - target_arch = "asmjs", target_arch = "wasm32")))] +#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))] pub fn black_box<T>(dummy: T) -> T { // we need to "use" the argument in some way LLVM can't // introspect. unsafe { asm!("" : : "r"(&dummy)) } dummy } -#[cfg(any(all(target_os = "nacl", target_arch = "le32"), - target_arch = "asmjs", target_arch = "wasm32"))] +#[cfg(any(target_arch = "asmjs", target_arch = "wasm32"))] #[inline(never)] pub fn black_box<T>(dummy: T) -> T { dummy diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 2f966e5a1c5..e37f048dd47 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -26,7 +26,11 @@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #if LLVM_VERSION_GE(4, 0) +#include "llvm/Object/ModuleSummaryIndexObjectFile.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" +#include "llvm/LTO/LTO.h" #endif #include "llvm-c/Transforms/PassManagerBuilder.h" @@ -102,6 +106,19 @@ extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) { PMB->add(Pass); } +extern "C" +bool LLVMRustPassManagerBuilderPopulateThinLTOPassManager( + LLVMPassManagerBuilderRef PMBR, + LLVMPassManagerRef PMR +) { +#if LLVM_VERSION_GE(4, 0) + unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR)); + return true; +#else + return false; +#endif +} + #ifdef LLVM_COMPONENT_X86 #define SUBTARGET_X86 SUBTARGET(X86) #else @@ -740,3 +757,447 @@ extern "C" void LLVMRustSetModulePIELevel(LLVMModuleRef M) { unwrap(M)->setPIELevel(PIELevel::Level::Large); #endif } + +extern "C" bool +LLVMRustThinLTOAvailable() { +#if LLVM_VERSION_GE(4, 0) + return true; +#else + return false; +#endif +} + +#if LLVM_VERSION_GE(4, 0) + +// Here you'll find an implementation of ThinLTO as used by the Rust compiler +// right now. This ThinLTO support is only enabled on "recent ish" versions of +// LLVM, and otherwise it's just blanket rejected from other compilers. +// +// Most of this implementation is straight copied from LLVM. At the time of +// this writing it wasn't *quite* suitable to reuse more code from upstream +// for our purposes, but we should strive to upstream this support once it's +// ready to go! I figure we may want a bit of testing locally first before +// sending this upstream to LLVM. I hear though they're quite eager to receive +// feedback like this! +// +// If you're reading this code and wondering "what in the world" or you're +// working "good lord by LLVM upgrade is *still* failing due to these bindings" +// then fear not! (ok maybe fear a little). All code here is mostly based +// on `lib/LTO/ThinLTOCodeGenerator.cpp` in LLVM. +// +// You'll find that the general layout here roughly corresponds to the `run` +// method in that file as well as `ProcessThinLTOModule`. Functions are +// specifically commented below as well, but if you're updating this code +// or otherwise trying to understand it, the LLVM source will be useful in +// interpreting the mysteries within. +// +// Otherwise I'll apologize in advance, it probably requires a relatively +// significant investment on your part to "truly understand" what's going on +// here. Not saying I do myself, but it took me awhile staring at LLVM's source +// and various online resources about ThinLTO to make heads or tails of all +// this. + +extern "C" bool +LLVMRustWriteThinBitcodeToFile(LLVMPassManagerRef PMR, + LLVMModuleRef M, + const char *BcFile) { + llvm::legacy::PassManager *PM = unwrap<llvm::legacy::PassManager>(PMR); + std::error_code EC; + llvm::raw_fd_ostream bc(BcFile, EC, llvm::sys::fs::F_None); + if (EC) { + LLVMRustSetLastError(EC.message().c_str()); + return false; + } + PM->add(createWriteThinLTOBitcodePass(bc)); + PM->run(*unwrap(M)); + delete PM; + return true; +} + +// This is a shared data structure which *must* be threadsafe to share +// read-only amongst threads. This also corresponds basically to the arguments +// of the `ProcessThinLTOModule` function in the LLVM source. +struct LLVMRustThinLTOData { + // The combined index that is the global analysis over all modules we're + // performing ThinLTO for. This is mostly managed by LLVM. + ModuleSummaryIndex Index; + + // All modules we may look at, stored as in-memory serialized versions. This + // is later used when inlining to ensure we can extract any module to inline + // from. + StringMap<MemoryBufferRef> ModuleMap; + + // A set that we manage of everything we *don't* want internalized. Note that + // this includes all transitive references right now as well, but it may not + // always! + DenseSet<GlobalValue::GUID> GUIDPreservedSymbols; + + // Not 100% sure what these are, but they impact what's internalized and + // what's inlined across modules, I believe. + StringMap<FunctionImporter::ImportMapTy> ImportLists; + StringMap<FunctionImporter::ExportSetTy> ExportLists; + StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries; +}; + +// Just an argument to the `LLVMRustCreateThinLTOData` function below. +struct LLVMRustThinLTOModule { + const char *identifier; + const char *data; + size_t len; +}; + +// This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it +// does. +static const GlobalValueSummary * +getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { + auto StrongDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage) && + !GlobalValue::isWeakForLinker(Linkage); + }); + if (StrongDefForLinker != GVSummaryList.end()) + return StrongDefForLinker->get(); + + auto FirstDefForLinker = llvm::find_if( + GVSummaryList, [](const std::unique_ptr<GlobalValueSummary> &Summary) { + auto Linkage = Summary->linkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + if (FirstDefForLinker == GVSummaryList.end()) + return nullptr; + return FirstDefForLinker->get(); +} + +// This is a helper function we added that isn't present in LLVM's source. +// +// The way LTO works in Rust is that we typically have a number of symbols that +// we know ahead of time need to be preserved. We want to ensure that ThinLTO +// doesn't accidentally internalize any of these and otherwise is always +// ready to keep them linking correctly. +// +// This function will recursively walk the `GUID` provided and all of its +// references, as specified in the `Index`. In other words, we're taking a +// `GUID` as input, adding it to `Preserved`, and then taking all `GUID` +// items that the input references and recursing. +static void +addPreservedGUID(const ModuleSummaryIndex &Index, + DenseSet<GlobalValue::GUID> &Preserved, + GlobalValue::GUID GUID) { + if (Preserved.count(GUID)) + return; + Preserved.insert(GUID); + + auto SummaryList = Index.findGlobalValueSummaryList(GUID); + if (SummaryList == Index.end()) + return; + for (auto &Summary : SummaryList->second) { + for (auto &Ref : Summary->refs()) { + if (Ref.isGUID()) { + addPreservedGUID(Index, Preserved, Ref.getGUID()); + } else { + auto Value = Ref.getValue(); + addPreservedGUID(Index, Preserved, Value->getGUID()); + } + } + + GlobalValueSummary *GVSummary = Summary.get(); + if (isa<FunctionSummary>(GVSummary)) { + FunctionSummary *FS = cast<FunctionSummary>(GVSummary); + for (auto &Call: FS->calls()) { + if (Call.first.isGUID()) { + addPreservedGUID(Index, Preserved, Call.first.getGUID()); + } else { + auto Value = Call.first.getValue(); + addPreservedGUID(Index, Preserved, Value->getGUID()); + } + } + for (auto &GUID: FS->type_tests()) { + addPreservedGUID(Index, Preserved, GUID); + } + } + } +} + +// The main entry point for creating the global ThinLTO analysis. The structure +// here is basically the same as before threads are spawned in the `run` +// function of `lib/LTO/ThinLTOCodeGenerator.cpp`. +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { + auto Ret = llvm::make_unique<LLVMRustThinLTOData>(); + + // Load each module's summary and merge it into one combined index + for (int i = 0; i < num_modules; i++) { + auto module = &modules[i]; + StringRef buffer(module->data, module->len); + MemoryBufferRef mem_buffer(buffer, module->identifier); + + Ret->ModuleMap[module->identifier] = mem_buffer; + + Expected<std::unique_ptr<object::ModuleSummaryIndexObjectFile>> ObjOrErr = + object::ModuleSummaryIndexObjectFile::create(mem_buffer); + if (!ObjOrErr) { + LLVMRustSetLastError(toString(ObjOrErr.takeError()).c_str()); + return nullptr; + } + auto Index = (*ObjOrErr)->takeIndex(); + Ret->Index.mergeFrom(std::move(Index), i); + } + + // Collect for each module the list of function it defines (GUID -> Summary) + Ret->Index.collectDefinedGVSummariesPerModule(Ret->ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is then needed + // for internalization. We use `addPreservedGUID` to include any transitively + // used symbol as well. + for (int i = 0; i < num_symbols; i++) { + addPreservedGUID(Ret->Index, + Ret->GUIDPreservedSymbols, + GlobalValue::getGUID(preserved_symbols[i])); + } + + // Collect the import/export lists for all modules from the call-graph in the + // combined index + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` + computeDeadSymbols(Ret->Index, Ret->GUIDPreservedSymbols); + ComputeCrossModuleImport( + Ret->Index, + Ret->ModuleToDefinedGVSummaries, + Ret->ImportLists, + Ret->ExportLists + ); + + // Resolve LinkOnce/Weak symbols, this has to be computed early be cause it + // impacts the caching. + // + // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` + StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; + DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy; + for (auto &I : Ret->Index) { + if (I.second.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(I.second); + } + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + auto recordNewLinkage = [&](StringRef ModuleIdentifier, + GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) { + ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + }; + thinLTOResolveWeakForLinkerInIndex(Ret->Index, isPrevailing, recordNewLinkage); + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = Ret->ExportLists.find(ModuleIdentifier); + return (ExportList != Ret->ExportLists.end() && + ExportList->second.count(GUID)) || + Ret->GUIDPreservedSymbols.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(Ret->Index, isExported); + + return Ret.release(); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + delete Data; +} + +// Below are the various passes that happen *per module* when doing ThinLTO. +// +// In other words, these are the functions that are all run concurrently +// with one another, one per module. The passes here correspond to the analysis +// passes in `lib/LTO/ThinLTOCodeGenerator.cpp`, currently found in the +// `ProcessThinLTOModule` function. Here they're split up into separate steps +// so rustc can save off the intermediate bytecode between each step. + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + if (renameModuleForThinLTO(Mod, Data->Index)) { + LLVMRustSetLastError("renameModuleForThinLTO failed"); + return false; + } + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(Mod.getModuleIdentifier()); + thinLTOInternalizeModule(Mod, DefinedGlobals); + return true; +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + Module &Mod = *unwrap(M); + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); + auto Loader = [&](StringRef Identifier) { + const auto &Memory = Data->ModuleMap.lookup(Identifier); + auto &Context = Mod.getContext(); + return getLazyBitcodeModule(Memory, Context, true, true); + }; + FunctionImporter Importer(Data->Index, Loader); + Expected<bool> Result = Importer.importFunctions(Mod, ImportList); + if (!Result) { + LLVMRustSetLastError(toString(Result.takeError()).c_str()); + return false; + } + return true; +} + +// This struct and various functions are sort of a hack right now, but the +// problem is that we've got in-memory LLVM modules after we generate and +// optimize all codegen-units for one compilation in rustc. To be compatible +// with the LTO support above we need to serialize the modules plus their +// ThinLTO summary into memory. +// +// This structure is basically an owned version of a serialize module, with +// a ThinLTO summary attached. +struct LLVMRustThinLTOBuffer { + std::string data; +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { + auto Ret = llvm::make_unique<LLVMRustThinLTOBuffer>(); + { + raw_string_ostream OS(Ret->data); + { + legacy::PassManager PM; + PM.add(createWriteThinLTOBitcodePass(OS)); + PM.run(*unwrap(M)); + } + } + return Ret.release(); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + delete Buffer; +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.data(); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + return Buffer->data.length(); +} + +// This is what we used to parse upstream bitcode for actual ThinLTO +// processing. We'll call this once per module optimized through ThinLTO, and +// it'll be called concurrently on many threads. +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + StringRef Data(data, len); + MemoryBufferRef Buffer(Data, identifier); + unwrap(Context)->enableDebugTypeODRUniquing(); + Expected<std::unique_ptr<Module>> SrcOrError = + parseBitcodeFile(Buffer, *unwrap(Context)); + if (!SrcOrError) { + LLVMRustSetLastError(toString(SrcOrError.takeError()).c_str()); + return nullptr; + } + return wrap(std::move(*SrcOrError).release()); +} + +#else + +extern "C" bool +LLVMRustWriteThinBitcodeToFile(LLVMPassManagerRef PMR, + LLVMModuleRef M, + const char *BcFile) { + llvm_unreachable("ThinLTO not available"); +} + +struct LLVMRustThinLTOData { +}; + +struct LLVMRustThinLTOModule { +}; + +extern "C" LLVMRustThinLTOData* +LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, + int num_modules, + const char **preserved_symbols, + int num_symbols) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOResolveWeak(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" bool +LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" void +LLVMRustFreeThinLTOData(LLVMRustThinLTOData *Data) { + llvm_unreachable("ThinLTO not available"); +} + +struct LLVMRustThinLTOBuffer { +}; + +extern "C" LLVMRustThinLTOBuffer* +LLVMRustThinLTOBufferCreate(LLVMModuleRef M) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" void +LLVMRustThinLTOBufferFree(LLVMRustThinLTOBuffer *Buffer) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" const void* +LLVMRustThinLTOBufferPtr(const LLVMRustThinLTOBuffer *Buffer) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" size_t +LLVMRustThinLTOBufferLen(const LLVMRustThinLTOBuffer *Buffer) { + llvm_unreachable("ThinLTO not available"); +} + +extern "C" LLVMModuleRef +LLVMRustParseBitcodeForThinLTO(LLVMContextRef Context, + const char *data, + size_t len, + const char *identifier) { + llvm_unreachable("ThinLTO not available"); +} +#endif // LLVM_VERSION_GE(4, 0) diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index d8e6028b799..bd1325e7501 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // compile-flags:-Zprint-trans-items=eager -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<drop_in_place_intrinsic::StructWithDtor[0]> @@ drop_in_place_intrinsic.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<drop_in_place_intrinsic::StructWithDtor[0]> @@ drop_in_place_intrinsic0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { @@ -22,7 +22,7 @@ impl Drop for StructWithDtor { //~ TRANS_ITEM fn drop_in_place_intrinsic::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -34,7 +34,7 @@ fn drop_slice_in_place(x: &[StructWithDtor]) { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index 06e02b10015..108a8b570de 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -45,7 +45,7 @@ enum EnumNoDrop<T1, T2> { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::NonGenericWithDrop[0]> @@ generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::NonGenericWithDrop[0]> @@ generic_drop_glue0[Internal] impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] @@ -54,11 +54,11 @@ impl Drop for NonGenericWithDrop { //~ TRANS_ITEM fn generic_drop_glue::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructWithDrop[0]<i8, char>> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructWithDrop[0]<i8, char>> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<i8, char> let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]>> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructWithDrop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]>> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; @@ -67,17 +67,17 @@ fn main() { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructNoDrop[0]<generic_drop_glue::NonGenericWithDrop[0], f64>> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::StructNoDrop[0]<generic_drop_glue::NonGenericWithDrop[0], f64>> @@ generic_drop_glue0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::EnumWithDrop[0]<i32, i64>> @@ generic_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::EnumWithDrop[0]<i32, i64>> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0]<i32, i64> let _ = match EnumWithDrop::A::<i32, i64>(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::EnumWithDrop[0]<f64, f32>> @@ generic_drop_glue.cgu-0[Internal] + //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<generic_drop_glue::EnumWithDrop[0]<f64, f32>> @@ generic_drop_glue0[Internal] //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0]<f64, f32> let _ = match EnumWithDrop::B::<f64, f32>(1.0) { EnumWithDrop::A(x) => x, diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index 9c6bdb6624e..875cacb3907 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -31,13 +31,13 @@ impl<T> Trait for Struct<T> { fn main() { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<instantiation_through_vtable::Struct[0]<u32>> @@ instantiation_through_vtable.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<instantiation_through_vtable::Struct[0]<u32>> @@ instantiation_through_vtable0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0]<u32> //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0]<u32> let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<instantiation_through_vtable::Struct[0]<u64>> @@ instantiation_through_vtable.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<instantiation_through_vtable::Struct[0]<u64>> @@ instantiation_through_vtable0[Internal] //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0]<u64> //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0]<u64> let _ = &s1 as &Trait; diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index 5f70ff396dd..a6081b2c5eb 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -13,7 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<non_generic_drop_glue::StructWithDrop[0]> @@ non_generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<non_generic_drop_glue::StructWithDrop[0]> @@ non_generic_drop_glue0[Internal] struct StructWithDrop { x: i32 } @@ -27,7 +27,7 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<non_generic_drop_glue::EnumWithDrop[0]> @@ non_generic_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<non_generic_drop_glue::EnumWithDrop[0]> @@ non_generic_drop_glue0[Internal] enum EnumWithDrop { A(i32) } diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index e41cb34eec6..8eef5f00f2a 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -13,11 +13,11 @@ #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Root[0]> @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Root[0]> @@ transitive_drop_glue0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Intermediate[0]> @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Intermediate[0]> @@ transitive_drop_glue0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Leaf[0]> @@ transitive_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::Leaf[0]> @@ transitive_drop_glue0[Internal] struct Leaf; impl Drop for Leaf { @@ -38,15 +38,15 @@ fn main() { let _ = Root(Intermediate(Leaf)); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::RootGen[0]<u32>> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::IntermediateGen[0]<u32>> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::LeafGen[0]<u32>> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::RootGen[0]<u32>> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::IntermediateGen[0]<u32>> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::LeafGen[0]<u32>> @@ transitive_drop_glue0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0]<u32> let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::RootGen[0]<i16>> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::IntermediateGen[0]<i16>> @@ transitive_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::LeafGen[0]<i16>> @@ transitive_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::RootGen[0]<i16>> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::IntermediateGen[0]<i16>> @@ transitive_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<transitive_drop_glue::LeafGen[0]<i16>> @@ transitive_drop_glue0[Internal] //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0]<i16> let _ = RootGen(IntermediateGen(LeafGen(0i16))); } diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index 39043cf87cb..7edb1c14525 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -13,7 +13,7 @@ #![deny(dead_code)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<tuple_drop_glue::Dropped[0]> @@ tuple_drop_glue.cgu-0[Internal] +//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<tuple_drop_glue::Dropped[0]> @@ tuple_drop_glue0[Internal] struct Dropped; impl Drop for Dropped { @@ -23,10 +23,10 @@ impl Drop for Dropped { //~ TRANS_ITEM fn tuple_drop_glue::main[0] fn main() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] let x = (0u32, Dropped); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue.cgu-0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] let x = (0i16, (Dropped, true)); } diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index de7613741b2..cf0c6643238 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -57,13 +57,13 @@ fn main() { // simple case let bool_sized = &true; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<bool> @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<bool> @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; let char_sized = &'a'; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<char> @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<char> @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; @@ -73,13 +73,13 @@ fn main() _b: 2, _c: 3.0f64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<f64> @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<f64> @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct<Trait>; // custom coercion let wrapper_sized = Wrapper(&0u32); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<u32> @@ unsizing.cgu-0[Internal] + //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<u32> @@ unsizing0[Internal] //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper<Trait>; } diff --git a/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs b/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs index 3098807f272..fcdcf198c28 100644 --- a/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs +++ b/src/test/compile-fail/borrowck/borrowck-fn-in-const-a.rs @@ -8,12 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + // Check that we check fns appearing in constant declarations. // Issue #22382. const MOVE: fn(&String) -> String = { fn broken(x: &String) -> String { - return *x //~ ERROR cannot move + return *x //[ast]~ ERROR cannot move out of borrowed content [E0507] + //[mir]~^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] } broken }; diff --git a/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs b/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs index ec505faf885..99b5ef794c2 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-in-irrefut-pat.rs @@ -8,19 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + fn with<F>(f: F) where F: FnOnce(&String) {} fn arg_item(&_x: &String) {} - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] fn arg_closure() { with(|&_x| ()) - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] } fn let_pat() { let &_x = &"hi".to_string(); - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] } pub fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs index bf4c7474136..c7e1ea1b758 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-overloaded-auto-deref.rs @@ -8,9 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + use std::rc::Rc; pub fn main() { let _x = Rc::new(vec![1, 2]).into_iter(); - //~^ ERROR cannot move out of borrowed content + //[ast]~^ ERROR cannot move out of borrowed content [E0507] + //[mir]~^^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs index 8b83b945fd1..9e8021fd108 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-static-item.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + // Ensure that moves out of static items is forbidden struct Foo { @@ -22,5 +25,7 @@ fn test(f: Foo) { } fn main() { - test(BAR); //~ ERROR cannot move out of static item + test(BAR); //[ast]~ ERROR cannot move out of static item [E0507] + //[mir]~^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs index 16302d276ce..982f31b1341 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-of-struct-with-dtor.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + struct S {f:String} impl Drop for S { fn drop(&mut self) { println!("{}", self.f); } @@ -16,17 +19,23 @@ impl Drop for S { fn move_in_match() { match (S {f:"foo".to_string()}) { S {f:_s} => {} - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR (Ast) [E0509] + //[mir]~| ERROR (Mir) [E0509] } } fn move_in_let() { let S {f:_s} = S {f:"foo".to_string()}; - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR (Ast) [E0509] + //[mir]~| ERROR (Mir) [E0509] } fn move_in_fn_arg(S {f:_s}: S) { - //~^ ERROR cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ ERROR cannot move out of type `S`, which implements the `Drop` trait [E0509] + //[mir]~^^ ERROR (Ast) [E0509] + //[mir]~| ERROR (Mir) [E0509] } fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs b/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs index c364788a9cc..4a1828c6958 100644 --- a/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs +++ b/src/test/compile-fail/borrowck/borrowck-struct-update-with-dtor.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + // Issue 4691: Ensure that functional-struct-update can only copy, not // move, when the struct implements Drop. @@ -20,12 +23,16 @@ impl Drop for T { fn drop(&mut self) { } } fn f(s0:S) { let _s2 = S{a: 2, ..s0}; - //~^ error: cannot move out of type `S`, which implements the `Drop` trait + //[ast]~^ error: cannot move out of type `S`, which implements the `Drop` trait + //[mir]~^^ ERROR (Ast) [E0509] + //[mir]~| ERROR (Mir) [E0509] } fn g(s0:T) { let _s2 = T{a: 2, ..s0}; - //~^ error: cannot move out of type `T`, which implements the `Drop` trait + //[ast]~^ error: cannot move out of type `T`, which implements the `Drop` trait + //[mir]~^^ ERROR (Ast) [E0509] + //[mir]~| ERROR (Mir) [E0509] } fn main() { } diff --git a/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs b/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs index 3c1980e5b36..7f3120cc83e 100644 --- a/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs +++ b/src/test/compile-fail/borrowck/move-in-static-initializer-issue-38520.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// revisions: ast mir +//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir + // Regression test for #38520. Check that moves of `Foo` are not // permitted as `Foo` is not copy (even in a static/const // initializer). @@ -21,8 +24,12 @@ const fn get(x: Foo) -> usize { } const X: Foo = Foo(22); -static Y: usize = get(*&X); //~ ERROR E0507 -const Z: usize = get(*&X); //~ ERROR E0507 +static Y: usize = get(*&X); //[ast]~ ERROR E0507 + //[mir]~^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] +const Z: usize = get(*&X); //[ast]~ ERROR E0507 + //[mir]~^ ERROR (Ast) [E0507] + //[mir]~| ERROR (Mir) [E0507] fn main() { } diff --git a/src/test/compile-fail/issue-30355.rs b/src/test/compile-fail/issue-30355.rs new file mode 100644 index 00000000000..ee19d040318 --- /dev/null +++ b/src/test/compile-fail/issue-30355.rs @@ -0,0 +1,21 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct X([u8]); + +pub static Y: &'static X = { + const Y: &'static [u8] = b""; + &X(*Y) + //~^ ERROR cannot move out + //~^^ ERROR cannot move a + //~^^^ ERROR cannot move a +}; + +fn main() {} diff --git a/src/test/compile-fail/issue-33241.rs b/src/test/compile-fail/issue-33241.rs new file mode 100644 index 00000000000..6a411b4c59c --- /dev/null +++ b/src/test/compile-fail/issue-33241.rs @@ -0,0 +1,23 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +use std::fmt; + +// CoerceUnsized is not implemented for tuples. You can still create +// an unsized tuple by transmuting a trait object. +fn any<T>() -> T { unreachable!() } + +#[rustc_error] +fn main() { //~ ERROR compilation successful + let t: &(u8, fmt::Debug) = any(); + println!("{:?}", &t.1); +} diff --git a/src/test/compile-fail/issue-37887.rs b/src/test/compile-fail/issue-37887.rs new file mode 100644 index 00000000000..f120bbbfc9f --- /dev/null +++ b/src/test/compile-fail/issue-37887.rs @@ -0,0 +1,14 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + extern crate libc; //~ ERROR use of unstable + use libc::*; //~ ERROR unresolved import +} diff --git a/src/test/compile-fail/issue-44578.rs b/src/test/compile-fail/issue-44578.rs new file mode 100644 index 00000000000..a6ae21c3b54 --- /dev/null +++ b/src/test/compile-fail/issue-44578.rs @@ -0,0 +1,34 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + const AMT: usize; +} + +enum Bar<A, B> { + First(A), + Second(B), +} + +impl<A: Foo, B: Foo> Foo for Bar<A, B> { + const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize]; //~ ERROR constant evaluation +} + +impl Foo for u8 { + const AMT: usize = 1; +} + +impl Foo for u16 { + const AMT: usize = 2; +} + +fn main() { + println!("{}", <Bar<u16, u8> as Foo>::AMT); +} diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs index ec044225b83..d2ca65775a4 100644 --- a/src/test/mir-opt/validate_1.rs +++ b/src/test/mir-opt/validate_1.rs @@ -30,7 +30,7 @@ fn main() { // END RUST SOURCE // START rustc.node12.EraseRegions.after.mir // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:5) => validate_1/8cd878b::{{impl}}[0]::foo[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{impl}}[0]::foo[0] }, BrAnon(0)) Test, _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:5) => validate_1[8cd8]::{{impl}}[0]::foo[0] }, BrAnon(1)) mut i32]); // return; // } // END rustc.node12.EraseRegions.after.mir @@ -57,7 +57,7 @@ fn main() { // START rustc.node50.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(50)], _2: &ReErased mut i32) -> i32 { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:11) => validate_1/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:11) => validate_1/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(50)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:11) => validate_1[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); // _3 = _2; // StorageLive(_4); diff --git a/src/test/mir-opt/validate_4.rs b/src/test/mir-opt/validate_4.rs index 571ed425402..d240b51e222 100644 --- a/src/test/mir-opt/validate_4.rs +++ b/src/test/mir-opt/validate_4.rs @@ -48,8 +48,8 @@ fn main() { // START rustc.node22.EraseRegions.after.mir // fn write_42::{{closure}}(_1: &ReErased [closure@NodeId(22)], _2: *mut i32) -> () { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_4/8cd878b::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_4/8cd878b::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); +// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_4[8cd8]::write_42[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(22)], _2: *mut i32]); // StorageLive(_3); // _3 = _2; // (*_3) = const 23i32; @@ -61,8 +61,8 @@ fn main() { // START rustc.node31.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_4/8cd878b::test[0] }, BrAnon(0)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::test[0] }, BrAnon(0)) mut i32]); +// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_4[8cd8]::test[0] }, BrAnon(0)) mut i32]); // _3 = const write_42(_4) -> bb1; // } // bb1: { @@ -74,8 +74,8 @@ fn main() { // START rustc.node60.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(60)], _2: &ReErased mut i32) -> bool { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); -// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:10) => validate_4/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Release, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(60)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:10) => validate_4[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); // _0 = const write_42(_4) -> bb1; // } diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs index ff0c781d1e3..e1eeb2102d1 100644 --- a/src/test/mir-opt/validate_5.rs +++ b/src/test/mir-opt/validate_5.rs @@ -36,7 +36,7 @@ fn main() { // START rustc.node17.EraseRegions.after.mir // fn test(_1: &ReErased mut i32) -> () { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(0:4) => validate_5/8cd878b::test[0] }, BrAnon(0)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(0:4) => validate_5[8cd8]::test[0] }, BrAnon(0)) mut i32]); // Validate(Release, [_3: bool, _4: *mut i32]); // _3 = const write_42(_4) -> bb1; // } @@ -45,7 +45,7 @@ fn main() { // START rustc.node46.EraseRegions.after.mir // fn main::{{closure}}(_1: &ReErased [closure@NodeId(46)], _2: &ReErased mut i32) -> bool { // bb0: { -// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_5/8cd878b::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), node: DefIndex(1:9) => validate_5/8cd878b::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); +// Validate(Acquire, [_1: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, "BrEnv") [closure@NodeId(46)], _2: &ReFree(DefId { krate: CrateNum(0), index: DefIndex(1:9) => validate_5[8cd8]::main[0]::{{closure}}[0] }, BrAnon(1)) mut i32]); // StorageLive(_3); // _3 = _2; // StorageLive(_4); diff --git a/src/test/run-make/extra-filename-with-temp-outputs/Makefile b/src/test/run-make/extra-filename-with-temp-outputs/Makefile index d33c18a6f3c..13ca397eaf2 100644 --- a/src/test/run-make/extra-filename-with-temp-outputs/Makefile +++ b/src/test/run-make/extra-filename-with-temp-outputs/Makefile @@ -2,5 +2,5 @@ all: $(RUSTC) -C extra-filename=bar foo.rs -C save-temps - rm $(TMPDIR)/foobar.0.o + rm $(TMPDIR)/foobar.foo0.rust-cgu.o rm $(TMPDIR)/$(call BIN,foobar) diff --git a/src/test/run-make/sepcomp-cci-copies/Makefile b/src/test/run-make/sepcomp-cci-copies/Makefile index 189088219d5..8324a074d6c 100644 --- a/src/test/run-make/sepcomp-cci-copies/Makefile +++ b/src/test/run-make/sepcomp-cci-copies/Makefile @@ -6,4 +6,4 @@ all: $(RUSTC) cci_lib.rs $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*cci_fn)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*cci_fn)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-inlining/Makefile b/src/test/run-make/sepcomp-inlining/Makefile index 720dfff2c04..6dc837b8a78 100644 --- a/src/test/run-make/sepcomp-inlining/Makefile +++ b/src/test/run-make/sepcomp-inlining/Makefile @@ -8,7 +8,7 @@ all: $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "0" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ] - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ i32\ .*inlined)" -eq "0" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ] diff --git a/src/test/run-make/sepcomp-separate/Makefile b/src/test/run-make/sepcomp-separate/Makefile index a475bdfd74a..5b8bdb0fad8 100644 --- a/src/test/run-make/sepcomp-separate/Makefile +++ b/src/test/run-make/sepcomp-separate/Makefile @@ -6,4 +6,4 @@ all: $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ .*magic_fn)" -eq "3" ] + [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*magic_fn)" -eq "3" ] diff --git a/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs b/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs index 8ba38875eff..52a8652e65b 100644 --- a/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs +++ b/src/test/run-pass-fulldeps/proc-macro/attr-on-trait.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:attr-on-trait.rs +// ignore-stage1 #![feature(proc_macro)] diff --git a/src/libstd/os/nacl/mod.rs b/src/test/run-pass/auxiliary/thin-lto-inlines-aux.rs index 7dfa2eabe3e..ccbb0e7a718 100644 --- a/src/libstd/os/nacl/mod.rs +++ b/src/test/run-pass/auxiliary/thin-lto-inlines-aux.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Nacl-specific definitions +// no-prefer-dynamic -#![stable(feature = "raw_ext", since = "1.1.0")] +#![crate_type = "rlib"] -pub mod raw; -pub mod fs; +pub fn bar() -> u32 { + 3 +} diff --git a/src/test/run-pass/thin-lto-inlines.rs b/src/test/run-pass/thin-lto-inlines.rs new file mode 100644 index 00000000000..3135a682d86 --- /dev/null +++ b/src/test/run-pass/thin-lto-inlines.rs @@ -0,0 +1,39 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=8 -O +// min-llvm-version 4.0 +// ignore-emscripten + +// We want to assert here that ThinLTO will inline across codegen units. There's +// not really a great way to do that in general so we sort of hack around it by +// praying two functions go into separate codegen units and then assuming that +// if inlining *doesn't* happen the first byte of the functions will differ. + +pub fn foo() -> u32 { + bar::bar() +} + +mod bar { + pub fn bar() -> u32 { + 3 + } +} + +fn main() { + println!("{} {}", foo(), bar::bar()); + + unsafe { + let foo = foo as usize as *const u8; + let bar = bar::bar as usize as *const u8; + + assert_eq!(*foo, *bar); + } +} diff --git a/src/test/run-pass/thin-lto-inlines2.rs b/src/test/run-pass/thin-lto-inlines2.rs new file mode 100644 index 00000000000..ed899d2b115 --- /dev/null +++ b/src/test/run-pass/thin-lto-inlines2.rs @@ -0,0 +1,38 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z thinlto -C codegen-units=8 -O -C lto +// aux-build:thin-lto-inlines-aux.rs +// min-llvm-version 4.0 +// no-prefer-dynamic +// ignore-emscripten + +// We want to assert here that ThinLTO will inline across codegen units. There's +// not really a great way to do that in general so we sort of hack around it by +// praying two functions go into separate codegen units and then assuming that +// if inlining *doesn't* happen the first byte of the functions will differ. + +extern crate thin_lto_inlines_aux as bar; + +pub fn foo() -> u32 { + bar::bar() +} + +fn main() { + println!("{} {}", foo(), bar::bar()); + + unsafe { + let foo = foo as usize as *const u8; + let bar = bar::bar as usize as *const u8; + + assert_eq!(*foo, *bar); + } +} + diff --git a/src/test/rustdoc/fn-pointer-arg-name.rs b/src/test/rustdoc/fn-pointer-arg-name.rs new file mode 100644 index 00000000000..af87f1b4669 --- /dev/null +++ b/src/test/rustdoc/fn-pointer-arg-name.rs @@ -0,0 +1,15 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +// @has foo/fn.f.html +// @has - '//*[@class="rust fn"]' 'pub fn f(callback: fn(len: usize, foo: u32))' +pub fn f(callback: fn(len: usize, foo: u32)) {} diff --git a/src/test/ui/issue-36400.rs b/src/test/ui/issue-36400.rs new file mode 100644 index 00000000000..c0aec5b4296 --- /dev/null +++ b/src/test/ui/issue-36400.rs @@ -0,0 +1,16 @@ +// Copyright 2017 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f(x: &mut u32) {} + +fn main() { + let x = Box::new(3); + f(&mut *x); +} diff --git a/src/test/ui/issue-36400.stderr b/src/test/ui/issue-36400.stderr new file mode 100644 index 00000000000..69e9c455f35 --- /dev/null +++ b/src/test/ui/issue-36400.stderr @@ -0,0 +1,10 @@ +error[E0596]: cannot borrow immutable `Box` content `*x` as mutable + --> $DIR/issue-36400.rs:15:12 + | +14 | let x = Box::new(3); + | - consider changing this to `mut x` +15 | f(&mut *x); + | ^^ cannot borrow as mutable + +error: aborting due to previous error + diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index db957a7a0fc..daeac35a017 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -83,6 +83,7 @@ static TARGETS: &'static [&'static str] = &[ "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", "sparc64-unknown-linux-gnu", + "sparcv9-sun-solaris", "wasm32-unknown-emscripten", "x86_64-linux-android", "x86_64-apple-darwin", @@ -90,6 +91,7 @@ static TARGETS: &'static [&'static str] = &[ "x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", "x86_64-rumprun-netbsd", + "x86_64-sun-solaris", "x86_64-unknown-freebsd", "x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index bb9bf57d55e..19195838791 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -567,6 +567,19 @@ impl Config { None } } + + pub fn find_rust_src_root(&self) -> Option<PathBuf> { + let mut path = self.src_base.clone(); + let path_postfix = Path::new("src/etc/lldb_batchmode.py"); + + while path.pop() { + if path.join(&path_postfix).is_file() { + return Some(path); + } + } + + None + } } pub fn lldb_version_to_int(version_string: &str) -> isize { diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 26c447d01d3..306497da9e3 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -489,15 +489,28 @@ fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf { } fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool { + let rust_src_dir = config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let stamp = mtime(&stamp(config, testpaths)); - let mut inputs = vec![ - mtime(&testpaths.file), - mtime(&config.rustc_path), - ]; + let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)]; for aux in props.aux.iter() { - inputs.push(mtime(&testpaths.file.parent().unwrap() - .join("auxiliary") - .join(aux))); + inputs.push(mtime( + &testpaths.file.parent().unwrap().join("auxiliary").join( + aux, + ), + )); + } + // Relevant pretty printer files + let pretty_printer_files = [ + "src/etc/debugger_pretty_printers_common.py", + "src/etc/gdb_load_rust_pretty_printers.py", + "src/etc/gdb_rust_pretty_printing.py", + "src/etc/lldb_batchmode.py", + "src/etc/lldb_rust_formatters.py", + ]; + for pretty_printer_file in &pretty_printer_files { + inputs.push(mtime(&rust_src_dir.join(pretty_printer_file))); } for lib in config.run_lib_path.read_dir().unwrap() { let lib = lib.unwrap(); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 10ef326d9db..870e08cc6e5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -571,9 +571,10 @@ actual:\n\ } } - _=> { - let rust_src_root = self.find_rust_src_root() - .expect("Could not find Rust source root"); + _ => { + let rust_src_root = self.config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let rust_pp_module_rel_path = Path::new("./src/etc"); let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path) .to_str() @@ -664,19 +665,6 @@ actual:\n\ self.check_debugger_output(&debugger_run_result, &check_lines); } - fn find_rust_src_root(&self) -> Option<PathBuf> { - let mut path = self.config.src_base.clone(); - let path_postfix = Path::new("src/etc/lldb_batchmode.py"); - - while path.pop() { - if path.join(&path_postfix).is_file() { - return Some(path); - } - } - - None - } - fn run_debuginfo_lldb_test(&self) { assert!(self.revision.is_none(), "revisions not relevant here"); @@ -735,7 +723,9 @@ actual:\n\ script_str.push_str("version\n"); // Switch LLDB into "Rust mode" - let rust_src_root = self.find_rust_src_root().expect("Could not find Rust source root"); + let rust_src_root = self.config.find_rust_src_root().expect( + "Could not find Rust source root", + ); let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py"); let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path) .to_str() @@ -1717,11 +1707,13 @@ actual:\n\ if self.props.check_test_line_numbers_match { self.check_rustdoc_test_option(proc_res); } else { - let root = self.find_rust_src_root().unwrap(); - let res = self.cmd2procres(Command::new(&self.config.docck_python) - .arg(root.join("src/etc/htmldocck.py")) - .arg(out_dir) - .arg(&self.testpaths.file)); + let root = self.config.find_rust_src_root().unwrap(); + let res = self.cmd2procres( + Command::new(&self.config.docck_python) + .arg(root.join("src/etc/htmldocck.py")) + .arg(out_dir) + .arg(&self.testpaths.file), + ); if !res.status.success() { self.fatal_proc_rec("htmldocck failed!", &res); } |
