From f9de964ccf767498ed7b1b4a879aaca1777a9d3d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2015 16:07:45 -0700 Subject: [PATCH 01/23] msvc: Enable landing pads by default This commit turns on landing pads for MSVC by default, which means that we'll now be running cleanups for values on the stack when an exception is thrown. This commit "fixes" the previously seen LLVM abort by attaching the `noinline` attribute to all generated drop glue to prevent landing pads from being inlined into other landing pads. The performance of MSVC is highly likely to decrease from this commit, but there are various routes we can taken in the future if this ends up staying for quite a while, such as generating a shim function only called from landing pads which calls the actual drop glue, and this shim is marked noinline. For now, however, this patch enables MSVC to successfully bootstrap itself! --- src/librustc_trans/trans/base.rs | 7 +------ src/librustc_trans/trans/glue.rs | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 0cd6bbad03aa9..b829a9c6ec449 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -751,12 +751,7 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } pub fn need_invoke(bcx: Block) -> bool { - // FIXME(#25869) currently unwinding is not implemented for MSVC and our - // normal unwinding infrastructure ends up just causing linker - // errors with the current LLVM implementation, so landing - // pads are disabled entirely for MSVC targets - if bcx.sess().no_landing_pads() || - bcx.sess().target.target.options.is_like_msvc { + if bcx.sess().no_landing_pads() { return false; } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 3bcdcd89c47f5..be8510c8e968b 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -22,8 +22,9 @@ use middle::lang_items::ExchangeFreeFnLangItem; use middle::subst; use middle::subst::{Subst, Substs}; use middle::ty::{self, Ty}; -use trans::adt; use trans::adt::GetDtorType; // for tcx.dtor_type() +use trans::adt; +use trans::attributes; use trans::base::*; use trans::build::*; use trans::callee; @@ -43,6 +44,7 @@ use trans::type_::Type; use arena::TypedArena; use libc::c_uint; use syntax::ast; +use syntax::attr::InlineAttr; pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, @@ -250,6 +252,25 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, update_linkage(ccx, llfn, None, OriginalTranslation); + // FIXME: Currently LLVM has a bug where if an SSA value is created in one + // landing pad and then used in another it will abort during + // compilation. The compiler never actually generates nested landing + // pads, but this often arises when destructors are inlined into + // other functions. To prevent this inlining from happening (and thus + // preventing the LLVM abort) we mark all drop glue as inline(never) + // on MSVC. + // + // For more information about the bug, see: + // + // https://llvm.org/bugs/show_bug.cgi?id=23884 + // + // This is clearly not the ideal solution to the problem (due to the + // perf hits), so this should be removed once the upstream bug is + // fixed. + if ccx.sess().target.target.options.is_like_msvc { + attributes::inline(llfn, InlineAttr::Never); + } + ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1); // All glue functions take values passed *by alias*; this is a // requirement since in many contexts glue is invoked indirectly and From 91c22b63020e15908859b11bfe777d65bc55aa98 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2015 23:16:24 -0700 Subject: [PATCH 02/23] msvc: Lookup linker in windows registry This commit alters the compiler to no longer "just run link.exe" but instead probe the system's registry to find where the linker is located. The default library search path (normally found through LIB) is also found through the registry. This also brings us in line with the default behavior of Clang, and much of the logic of where to look for information is copied over from Clang as well. Finally, this commit removes the makefile logic for updating the environment variables for the compiler, except for stage0 where it's still necessary. The motivation for this change is rooted in two positions: * Not having to set up these environment variables is much less hassle both for the bootstrap and for running the compiler itself. This means that the compiler can be run outside of VS shells and be run inside of cmd.exe or a MSYS shell. * When dealing with cross compilation, there's not actually a set of environment variables that can be set for the compiler. This means, for example, if a Cargo compilation is targeting 32-bit from 64-bit you can't actually set up one set of environment variables. Having the compiler deal with the logic instead is generally much more convenient! --- mk/target.mk | 10 +- src/librustc_trans/back/link.rs | 16 +- src/librustc_trans/back/msvc/mod.rs | 239 +++++++++++++++++++++++ src/librustc_trans/back/msvc/registry.rs | 170 ++++++++++++++++ src/librustc_trans/back/write.rs | 17 +- src/librustc_trans/lib.rs | 2 +- 6 files changed, 430 insertions(+), 24 deletions(-) create mode 100644 src/librustc_trans/back/msvc/mod.rs create mode 100644 src/librustc_trans/back/msvc/registry.rs diff --git a/mk/target.mk b/mk/target.mk index c398950965f54..c2de9af39c764 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -249,11 +249,9 @@ endef $(foreach host,$(CFG_HOST), \ $(foreach target,$(CFG_TARGET), \ - $(foreach stage,$(STAGES), \ - $(foreach crate,$(CRATES), \ - $(eval $(call SETUP_LIB_MSVC_ENV_VARS,$(stage),$(target),$(host),$(crate))))))) + $(foreach crate,$(CRATES), \ + $(eval $(call SETUP_LIB_MSVC_ENV_VARS,0,$(target),$(host),$(crate)))))) $(foreach host,$(CFG_HOST), \ $(foreach target,$(CFG_TARGET), \ - $(foreach stage,$(STAGES), \ - $(foreach tool,$(TOOLS), \ - $(eval $(call SETUP_TOOL_MSVC_ENV_VARS,$(stage),$(target),$(host),$(tool))))))) + $(foreach tool,$(TOOLS), \ + $(eval $(call SETUP_TOOL_MSVC_ENV_VARS,0,$(target),$(host),$(tool)))))) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index cf5feabcc57e2..e0495226d9061 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -12,6 +12,7 @@ use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; use super::linker::{Linker, GnuLinker, MsvcLinker}; use super::rpath::RPathConfig; use super::rpath; +use super::msvc; use super::svh::Svh; use session::config; use session::config::NoDebugInfo; @@ -358,10 +359,14 @@ pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> Stri mangle(path.chain(Some(gensym_name(flav))), None) } -pub fn get_cc_prog(sess: &Session) -> String { - match sess.opts.cg.linker { - Some(ref linker) => return linker.to_string(), - None => sess.target.target.options.linker.clone(), +pub fn get_linker(sess: &Session) -> (String, Command) { + if let Some(ref linker) = sess.opts.cg.linker { + (linker.clone(), Command::new(linker)) + } else if sess.target.target.options.is_like_msvc { + ("link.exe".to_string(), msvc::link_exe_cmd(sess)) + } else { + (sess.target.target.options.linker.clone(), + Command::new(&sess.target.target.options.linker)) } } @@ -807,8 +812,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); // The invocations of cc share some flags across platforms - let pname = get_cc_prog(sess); - let mut cmd = Command::new(&pname); + let (pname, mut cmd) = get_linker(sess); cmd.env("PATH", command_path(sess)); let root = sess.target_filesearch(PathKind::Native).get_lib_path(); diff --git a/src/librustc_trans/back/msvc/mod.rs b/src/librustc_trans/back/msvc/mod.rs new file mode 100644 index 0000000000000..0077e7eed52d0 --- /dev/null +++ b/src/librustc_trans/back/msvc/mod.rs @@ -0,0 +1,239 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! MSVC-specific logic for linkers and such. +//! +//! This module contains a cross-platform interface but has a blank unix +//! implementation. The Windows implementation builds on top of Windows native +//! libraries (reading registry keys), so it otherwise wouldn't link on unix. +//! +//! Note that we don't have much special logic for finding the system linker on +//! any other platforms, so it may seem a little odd to single out MSVC to have +//! a good deal of code just to find the linker. Unlike Unix systems, however, +//! the MSVC linker is not in the system PATH by default. It also additionally +//! needs a few environment variables or command line flags to be able to link +//! against system libraries. +//! +//! In order to have a nice smooth experience on Windows, the logic in this file +//! is here to find the MSVC linker and set it up in the default configuration +//! one would need to set up anyway. This means that the Rust compiler can be +//! run not only in the developer shells of MSVC but also the standard cmd.exe +//! shell or MSYS shells. +//! +//! As a high-level note, all logic in this module for looking up various +//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but +//! comments can also be found below leading through the various code paths. + +use std::process::Command; +use session::Session; + +#[cfg(windows)] +mod registry; + +#[cfg(windows)] +pub fn link_exe_cmd(sess: &Session) -> Command { + use std::env; + use std::ffi::OsString; + use std::fs; + use std::path::PathBuf; + use self::registry::{RegistryKey, LOCAL_MACHINE}; + + // When finding the link.exe binary the 32-bit version is at the top level + // but the versions to cross to other architectures are stored in + // sub-folders. Unknown architectures also just bail out early to return the + // standard `link.exe` command. + let extra = match &sess.target.target.arch[..] { + "x86" => "", + "x86_64" => "amd64", + "arm" => "arm", + _ => return Command::new("link.exe"), + }; + + let vs_install_dir = get_vs_install_dir(); + + // First up, we need to find the `link.exe` binary itself, and there's a few + // locations that we can look. First up is the standard VCINSTALLDIR + // environment variable which is normally set by the vcvarsall.bat file. If + // an environment is set up manually by whomever's driving the compiler then + // we shouldn't muck with that decision and should instead respect that. + // + // Next up is looking in PATH itself. Here we look for `cl.exe` and then + // assume that `link.exe` is next to it if we find it. Note that we look for + // `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in + // PATH but we're not interested in finding that. + // + // Finally we read the Windows registry to discover the VS install root. + // From here we probe for `link.exe` just to make sure that it exists. + let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| { + let mut p = PathBuf::from(dir); + p.push("bin"); + p.push(extra); + p.push("link.exe"); + if fs::metadata(&p).is_ok() {Some(p)} else {None} + }).or_else(|| { + env::var_os("PATH").and_then(|path| { + env::split_paths(&path).find(|path| { + fs::metadata(&path.join("cl.exe")).is_ok() + }).map(|p| { + p.join("link.exe") + }) + }) + }).or_else(|| { + vs_install_dir.as_ref().and_then(|p| { + let mut p = p.join("VC/bin"); + p.push(extra); + p.push("link.exe"); + if fs::metadata(&p).is_ok() {Some(p)} else {None} + }) + }).map(|linker| { + Command::new(linker) + }).unwrap_or_else(|| { + Command::new("link.exe") + }); + + // The MSVC linker uses the LIB environment variable as the default lookup + // path for libraries. This environment variable is normally set up by the + // VS shells, so we only want to start adding our own pieces if it's not + // set. + // + // If we're adding our own pieces, then we need to add two primary + // directories to the default search path for the linker. The first is in + // the VS install direcotry and the next is the Windows SDK directory. + if env::var_os("LIB").is_none() { + if let Some(mut vs_install_dir) = vs_install_dir { + vs_install_dir.push("VC/lib"); + vs_install_dir.push(extra); + let mut arg = OsString::from("/LIBPATH:"); + arg.push(&vs_install_dir); + cmd.arg(arg); + } + if let Some(path) = get_windows_sdk_lib_path(sess) { + let mut arg = OsString::from("/LIBPATH:"); + arg.push(&path); + cmd.arg(arg); + } + } + + return cmd; + + // When looking for the Visual Studio installation directory we look in a + // number of locations in varying degrees of precedence: + // + // 1. The Visual Studio registry keys + // 2. The Visual Studio Express registry keys + // 3. A number of somewhat standard environment variables + // + // If we find a hit from any of these keys then we strip off the IDE/Tools + // folders which are typically found at the end. + // + // As a final note, when we take a look at the registry keys they're + // typically found underneath the version of what's installed, but we don't + // quite know what's installed. As a result we probe all sub-keys of the two + // keys we're looking at to find out the maximum version of what's installed + // and we use that root directory. + fn get_vs_install_dir() -> Option { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| { + LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref()) + }).ok().and_then(|key| { + max_version(&key).and_then(|(_vers, key)| { + key.query_str("InstallDir").ok() + }) + }).or_else(|| { + env::var_os("VS120COMNTOOLS") + }).or_else(|| { + env::var_os("VS100COMNTOOLS") + }).or_else(|| { + env::var_os("VS90COMNTOOLS") + }).or_else(|| { + env::var_os("VS80COMNTOOLS") + }).map(PathBuf::from).and_then(|mut dir| { + if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") { + dir.pop(); + dir.pop(); + Some(dir) + } else { + None + } + }) + } + + // Given a registry key, look at all the sub keys and find the one which has + // the maximal numeric value. + // + // Returns the name of the maximal key as well as the opened maximal key. + fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> { + let mut max_vers = 0; + let mut max_key = None; + for subkey in key.iter().filter_map(|k| k.ok()) { + let val = subkey.to_str().and_then(|s| { + s.trim_left_matches("v").replace(".", "").parse().ok() + }); + let val = match val { + Some(s) => s, + None => continue, + }; + if val > max_vers { + if let Ok(k) = key.open(&subkey) { + max_vers = val; + max_key = Some((subkey, k)); + } + } + } + return max_key + } + + fn get_windows_sdk_lib_path(sess: &Session) -> Option { + let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows"; + let key = LOCAL_MACHINE.open(key.as_ref()); + let (n, k) = match key.ok().as_ref().and_then(max_version) { + Some(p) => p, + None => return None, + }; + let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, "."); + let major = parts.next().unwrap().parse::().unwrap(); + let _minor = parts.next().unwrap().parse::().unwrap(); + let path = match k.query_str("InstallationFolder") { + Ok(p) => PathBuf::from(p).join("Lib"), + Err(..) => return None, + }; + if major <= 7 { + // In Windows SDK 7.x, x86 libraries are directly in the Lib folder, + // x64 libraries are inside, and it's not necessary to link agains + // the SDK 7.x when targeting ARM or other architectures. + let x86 = match &sess.target.target.arch[..] { + "x86" => true, + "x86_64" => false, + _ => return None, + }; + Some(if x86 {path} else {path.join("x64")}) + } else { + // Windows SDK 8.x installes libraries in a folder whose names + // depend on the version of the OS you're targeting. By default + // choose the newest, which usually corresponds to the version of + // the OS you've installed the SDK on. + let extra = match &sess.target.target.arch[..] { + "x86" => "x86", + "x86_64" => "x64", + "arm" => "arm", + _ => return None, + }; + ["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| { + fs::metadata(part).is_ok() + }).map(|path| { + path.join("um").join(extra) + }) + } + } +} + +#[cfg(not(windows))] +pub fn link_exe_cmd(_sess: &Session) -> Command { + Command::new("link.exe") +} diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs new file mode 100644 index 0000000000000..97fd7f99d197e --- /dev/null +++ b/src/librustc_trans/back/msvc/registry.rs @@ -0,0 +1,170 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::io; +use std::ffi::{OsString, OsStr}; +use std::os::windows::prelude::*; +use std::ops::RangeFrom; +use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS}; + +const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY; +const KEY_WOW64_32KEY: REGSAM = 0x0200; +const KEY_READ: REGSAM = (STANDARD_RIGTS_READ | KEY_QUERY_VALUE | + KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & !SYNCHRONIZE; +const STANDARD_RIGTS_READ: REGSAM = READ_CONTROL; +const READ_CONTROL: REGSAM = 0x00020000; +const KEY_QUERY_VALUE: REGSAM = 0x0001; +const KEY_ENUMERATE_SUB_KEYS: REGSAM = 0x0008; +const KEY_NOTIFY: REGSAM = 0x0010; +const SYNCHRONIZE: REGSAM = 0x00100000; +const REG_SZ: DWORD = 1; +const ERROR_NO_MORE_ITEMS: DWORD = 259; + +enum __HKEY__ {} +pub type HKEY = *mut __HKEY__; +pub type PHKEY = *mut HKEY; +pub type REGSAM = DWORD; +pub type LPWSTR = *mut u16; +pub type PFILETIME = *mut (); + +#[link(name = "advapi32")] +extern "system" { + fn RegOpenKeyExW(hKey: HKEY, + lpSubKey: LPCWSTR, + ulOptions: DWORD, + samDesired: REGSAM, + phkResult: PHKEY) -> LONG; + fn RegQueryValueExW(hKey: HKEY, + lpValueName: LPCWSTR, + lpReserved: LPDWORD, + lpType: LPDWORD, + lpData: LPBYTE, + lpcbData: LPDWORD) -> LONG; + fn RegEnumKeyExW(hKey: HKEY, + dwIndex: DWORD, + lpName: LPWSTR, + lpcName: LPDWORD, + lpReserved: LPDWORD, + lpClass: LPWSTR, + lpcClass: LPDWORD, + lpftLastWriteTime: PFILETIME) -> LONG; + fn RegCloseKey(hKey: HKEY) -> LONG; +} + +pub struct RegistryKey(Repr); + +struct OwnedKey(HKEY); + +enum Repr { + Const(HKEY), + Owned(OwnedKey), +} + +pub struct Iter<'a> { + idx: RangeFrom, + key: &'a RegistryKey, +} + +unsafe impl Sync for RegistryKey {} +unsafe impl Send for RegistryKey {} + +pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE)); + +impl RegistryKey { + fn raw(&self) -> HKEY { + match self.0 { + Repr::Const(val) => val, + Repr::Owned(ref val) => val.0, + } + } + + pub fn open(&self, key: &OsStr) -> io::Result { + let key = key.encode_wide().chain(Some(0)).collect::>(); + let mut ret = 0 as *mut _; + let err = unsafe { + RegOpenKeyExW(self.raw(), key.as_ptr(), 0, + KEY_READ | KEY_WOW64_32KEY, &mut ret) + }; + if err == ERROR_SUCCESS { + Ok(RegistryKey(Repr::Owned(OwnedKey(ret)))) + } else { + Err(io::Error::from_raw_os_error(err as i32)) + } + } + + pub fn iter(&self) -> Iter { + Iter { idx: 0.., key: self } + } + + pub fn query_str(&self, name: &str) -> io::Result { + let name: &OsStr = name.as_ref(); + let name = name.encode_wide().chain(Some(0)).collect::>(); + let mut len = 0; + let mut kind = 0; + unsafe { + let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _, + &mut kind, 0 as *mut _, &mut len); + if err != ERROR_SUCCESS { + return Err(io::Error::from_raw_os_error(err as i32)) + } + if kind != REG_SZ { + return Err(io::Error::new(io::ErrorKind::Other, + "registry key wasn't a string")) + } + + // The length here is the length in bytes, but we're using wide + // characters so we need to be sure to halve it for the capacity + // passed in. + let mut v = Vec::with_capacity(len as usize / 2); + let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _, + 0 as *mut _, v.as_mut_ptr() as *mut _, + &mut len); + if err != ERROR_SUCCESS { + return Err(io::Error::from_raw_os_error(err as i32)) + } + v.set_len(len as usize / 2); + + // Some registry keys may have a terminating nul character, but + // we're not interested in that, so chop it off if it's there. + if v[v.len() - 1] == 0 { + v.pop(); + } + Ok(OsString::from_wide(&v)) + } + } +} + +impl Drop for OwnedKey { + fn drop(&mut self) { + unsafe { RegCloseKey(self.0); } + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.idx.next().and_then(|i| unsafe { + let mut v = Vec::with_capacity(256); + let mut len = v.capacity() as DWORD; + let ret = RegEnumKeyExW(self.key.raw(), i, v.as_mut_ptr(), &mut len, + 0 as *mut _, 0 as *mut _, 0 as *mut _, + 0 as *mut _); + if ret == ERROR_NO_MORE_ITEMS as LONG { + None + } else if ret != ERROR_SUCCESS { + Some(Err(io::Error::from_raw_os_error(ret as i32))) + } else { + v.set_len(len as usize); + Some(Ok(OsString::from_wide(&v))) + } + }) + } +} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 267f0b6d95329..90ddba4e09c58 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -9,7 +9,7 @@ // except according to those terms. use back::lto; -use back::link::{get_cc_prog, remove}; +use back::link::{get_linker, remove}; use session::config::{OutputFilenames, Passes, SomePasses, AllPasses}; use session::Session; use session::config; @@ -27,7 +27,7 @@ use std::ffi::{CStr, CString}; use std::fs; use std::mem; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::Stdio; use std::ptr; use std::str; use std::sync::{Arc, Mutex}; @@ -737,8 +737,7 @@ pub fn run_passes(sess: &Session, None }; - let pname = get_cc_prog(sess); - let mut cmd = Command::new(&pname[..]); + let (pname, mut cmd) = get_linker(sess); cmd.args(&sess.target.target.options.pre_link_args); cmd.arg("-nostdlib"); @@ -767,8 +766,7 @@ pub fn run_passes(sess: &Session, }, Err(e) => { sess.err(&format!("could not exec the linker `{}`: {}", - pname, - e)); + pname, e)); sess.abort_if_errors(); }, } @@ -986,8 +984,7 @@ fn run_work_multithreaded(sess: &Session, } pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { - let pname = get_cc_prog(sess); - let mut cmd = Command::new(&pname[..]); + let (pname, mut cmd) = get_linker(sess); cmd.arg("-c").arg("-o").arg(&outputs.path(config::OutputTypeObject)) .arg(&outputs.temp_path(config::OutputTypeAssembly)); @@ -1007,9 +1004,7 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) { } }, Err(e) => { - sess.err(&format!("could not exec the linker `{}`: {}", - pname, - e)); + sess.err(&format!("could not exec the linker `{}`: {}", pname, e)); sess.abort_if_errors(); } } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index dc692b0e765dd..66881edaa8b86 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -83,7 +83,7 @@ pub mod back { pub mod link; pub mod lto; pub mod write; - + pub mod msvc; } pub mod trans; From ae0eb675db97a57a63f941b29754d8e32040ecc9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2015 23:28:39 -0700 Subject: [PATCH 03/23] msvc: Fix TLS destructors Just like the original article our Windows TLS support is based on predicted, this symbol must be linked in on MSVC to pull in the necessary support for TLS variables. This commit fixes a number of unit tests which require that TLS destructors are run. --- src/libstd/lib.rs | 1 + src/libstd/sys/windows/thread_local.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 2117291817754..73e45619774d7 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -151,6 +151,7 @@ #![cfg_attr(windows, feature(str_utf16))] #![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras))] #![cfg_attr(test, feature(test, rustc_private, float_consts))] +#![cfg_attr(target_env = "msvc", feature(link_args))] // Don't link to std. We are std. #![no_std] diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 5002de559884d..a2dbb0f834243 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -221,8 +221,8 @@ unsafe fn unregister_dtor(key: Key) -> bool { // // # The article mentions crazy stuff about "/INCLUDE"? // -// It sure does! This seems to work for now, so maybe we'll just run into -// that if we start linking with msvc? +// It sure does! We include it below for MSVC targets, but it look like for GNU +// targets we don't require it. #[link_section = ".CRT$XLB"] #[linkage = "external"] @@ -231,6 +231,10 @@ pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD, LPVOID) = on_tls_callback; +#[cfg(target_env = "msvc")] +#[link_args = "/INCLUDE:_tls_used"] +extern {} + #[allow(warnings)] unsafe extern "system" fn on_tls_callback(h: LPVOID, dwReason: DWORD, From 407fb293db8ef375ab78500a62d9320c24c77a18 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2015 23:30:12 -0700 Subject: [PATCH 04/23] msvc: Ignore extern-pass-empty on MSVC The MSVC compiler doesn't like empty structs, so this test won't link on MSVC, so it's ignored. --- src/test/run-pass/extern-pass-empty.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/run-pass/extern-pass-empty.rs b/src/test/run-pass/extern-pass-empty.rs index 17b0bb580fce2..21948d2e5ad23 100644 --- a/src/test/run-pass/extern-pass-empty.rs +++ b/src/test/run-pass/extern-pass-empty.rs @@ -11,6 +11,7 @@ // Test a foreign function that accepts empty struct. // pretty-expanded FIXME #23616 +// ignore-msvc struct TwoU8s { one: u8, From 18c39e126a833f66f58fe6cbf7b97ca4262f1c90 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2015 23:30:49 -0700 Subject: [PATCH 05/23] msvc: Fix the link name for the lgamma The function is apparently just called lgamma on MSVC --- src/test/run-pass/issue-2214.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/run-pass/issue-2214.rs b/src/test/run-pass/issue-2214.rs index 775cfb0ee4219..316e379e664ae 100644 --- a/src/test/run-pass/issue-2214.rs +++ b/src/test/run-pass/issue-2214.rs @@ -37,7 +37,7 @@ mod m { #[link_name="lgamma_r"] pub fn lgamma(n: c_double, sign: &mut c_int) -> c_double; #[cfg(windows)] - #[link_name="__lgamma_r"] + #[link_name="lgamma"] pub fn lgamma(n: c_double, sign: &mut c_int) -> c_double; } } From 83ee47b054deb5939be20d7d6ce03ad33d005424 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 30 Jun 2015 21:55:00 -0700 Subject: [PATCH 06/23] windows: Don't link rust_builtin This library has no shims which are actually needed on Windows now, so translate that last easy one into Rust and then don't link it at all on Windows. --- src/libstd/rt/util.rs | 15 +++++++++++---- src/libstd/rtdeps.rs | 4 ++-- src/libtest/lib.rs | 20 +++++++++++++++++--- src/rt/rust_builtin.c | 36 +++++------------------------------- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 04f36d99c8eb5..031fda089c84c 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -13,7 +13,6 @@ use io::prelude::*; use env; use fmt; use intrinsics; -use libc::uintptr_t; use sync::atomic::{self, Ordering}; use sys::stdio::Stderr; @@ -22,10 +21,18 @@ use sys::stdio::Stderr; /// can't run correctly un-altered. Valgrind is there to help /// you notice weirdness in normal, un-doctored code paths! pub fn running_on_valgrind() -> bool { - extern { - fn rust_running_on_valgrind() -> uintptr_t; + return on_valgrind(); + #[cfg(windows)] + fn on_valgrind() -> bool { false } + + #[cfg(unix)] + fn on_valgrind() -> bool { + use libc::uintptr_t; + extern { + fn rust_running_on_valgrind() -> uintptr_t; + } + unsafe { rust_running_on_valgrind() != 0 } } - unsafe { rust_running_on_valgrind() != 0 } } /// Valgrind has a fixed-sized array (size around 2000) of segment descriptors diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index be674c83e2213..b7839423e5225 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -12,8 +12,8 @@ //! the standard library This varies per-platform, but these libraries are //! necessary for running libstd. -// All platforms need to link to rustrt -#[cfg(not(test))] +// A few small shims in C that haven't been translated to Rust yet +#[cfg(all(not(test), not(windows)))] #[link(name = "rust_builtin", kind = "static")] extern {} diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 0a3c350086cdd..724c0b2a8927f 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -872,7 +872,7 @@ fn run_tests(opts: &TestOpts, #[allow(deprecated)] fn get_concurrency() -> usize { - match env::var("RUST_TEST_THREADS") { + return match env::var("RUST_TEST_THREADS") { Ok(s) => { let opt_n: Option = s.parse().ok(); match opt_n { @@ -884,10 +884,24 @@ fn get_concurrency() -> usize { if std::rt::util::limit_thread_creation_due_to_osx_and_valgrind() { 1 } else { - extern { fn rust_get_num_cpus() -> libc::uintptr_t; } - unsafe { rust_get_num_cpus() as usize } + num_cpus() } } + }; + + #[cfg(windows)] + fn num_cpus() -> usize { + unsafe { + let mut sysinfo = std::mem::zeroed(); + libc::GetSystemInfo(&mut sysinfo); + sysinfo.dwNumberOfProcessors as usize + } + } + + #[cfg(unix)] + fn num_cpus() -> usize { + extern { fn rust_get_num_cpus() -> libc::uintptr_t; } + unsafe { rust_get_num_cpus() as usize } } } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 1a2917a1dd67f..76a3debef59a4 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#if !defined(_WIN32) + #include #include #include @@ -15,7 +17,6 @@ #include -#if !defined(_WIN32) #include #include #include @@ -23,12 +24,6 @@ #include #include #include -#else -#include -#include -#include -#include -#endif #ifdef __APPLE__ #include @@ -41,17 +36,8 @@ /* Foreign builtins. */ //include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64 -#ifndef _WIN32 #include "valgrind/valgrind.h" -#endif - -#if defined(_MSC_VER) -# define RUST_BUILTIN_API __declspec(dllexport) -#else -# define RUST_BUILTIN_API -#endif -#ifndef _WIN32 char* rust_list_dir_val(struct dirent* entry_ptr) { return entry_ptr->d_name; @@ -92,17 +78,8 @@ int rust_dirent_t_size() { return sizeof(struct dirent); } -#endif - -#if defined(_WIN32) -int -get_num_cpus() { - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return (int) sysinfo.dwNumberOfProcessors; -} -#elif defined(__BSD__) +#if defined(__BSD__) int get_num_cpus() { /* swiped from http://stackoverflow.com/questions/150355/ @@ -136,7 +113,6 @@ get_num_cpus() { } #endif -RUST_BUILTIN_API uintptr_t rust_get_num_cpus() { return get_num_cpus(); @@ -144,11 +120,7 @@ rust_get_num_cpus() { uintptr_t rust_running_on_valgrind() { -#ifdef _WIN32 - return 0; -#else return RUNNING_ON_VALGRIND; -#endif } #if defined(__DragonFly__) @@ -484,6 +456,8 @@ const char * rust_current_exe() { #endif +#endif // !defined(_WIN32) + // // Local Variables: // mode: C++ From 3e26e56a79ae33dfc8f2f4d0123b5080fd0a7853 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 30 Jun 2015 10:28:24 -0700 Subject: [PATCH 07/23] rustc_trans: Disable landing pads on 32-bit MSVC This is currently quite buggy in LLVM from what I can tell, so just disable it entirely. This commit also adds preliminary support, however, to actually target 32-bit MSVC by making sure the `rust_try_msvc_32.ll` file exists and wiring up exceptions to `_except_handler3` instead of `__C_specific_handler` (which doesn't exist on 32-bit). --- src/librustc_trans/trans/base.rs | 9 ++++++- src/librustc_trans/trans/cleanup.rs | 12 +++++---- src/rt/rust_try_msvc_32.ll | 42 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/rt/rust_try_msvc_32.ll diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index b829a9c6ec449..079cd9c3757e5 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -752,7 +752,14 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pub fn need_invoke(bcx: Block) -> bool { if bcx.sess().no_landing_pads() { - return false; + return false + } + + // Currently 32-bit MSVC unwinding is not super well implemented in LLVM, so + // we avoid it entirely. + if bcx.sess().target.target.options.is_like_msvc && + bcx.sess().target.target.arch == "x86" { + return false } // Avoid using invoke if we are already inside a landing pad. diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 588e4cea5048b..1891320313a85 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -851,8 +851,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx // an "exception", but for MSVC we want to force SEH. This means that we // can't actually have the personality function be our standard // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom `__C_specific_handler` personality funciton, which - // forces LLVM to consider landing pads as "landing pads for SEH". + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". let target = &self.ccx.sess().target.target; let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() { Some(def_id) if !target.options.is_like_msvc => { @@ -864,10 +864,12 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx match *personality { Some(llpersonality) => llpersonality, None => { - let name = if target.options.is_like_msvc { - "__C_specific_handler" - } else { + let name = if !target.options.is_like_msvc { "rust_eh_personality" + } else if target.arch == "x86" { + "_except_handler3" + } else { + "__C_specific_handler" }; let fty = Type::variadic_func(&[], &Type::i32(self.ccx)); let f = declare::declare_cfn(self.ccx, name, fty, diff --git a/src/rt/rust_try_msvc_32.ll b/src/rt/rust_try_msvc_32.ll new file mode 100644 index 0000000000000..bdee53b136e10 --- /dev/null +++ b/src/rt/rust_try_msvc_32.ll @@ -0,0 +1,42 @@ +; Copyright 2015 The Rust Project Developers. See the COPYRIGHT +; file at the top-level directory of this distribution and at +; http://rust-lang.org/COPYRIGHT. +; +; Licensed under the Apache License, Version 2.0 or the MIT license +; , at your +; option. This file may not be copied, modified, or distributed +; except according to those terms. + +; For more comments about what's going on here see rust_try_msvc_64.ll. The only +; difference between that and this file is the personality function used as it's +; different for 32-bit MSVC than it is for 64-bit. + +define i8* @rust_try(void (i8*)* %f, i8* %env) + personality i8* bitcast (i32 (...)* @_except_handler3 to i8*) +{ + invoke void %f(i8* %env) + to label %normal + unwind label %catch + +normal: + ret i8* null +catch: + %vals = landingpad { i8*, i32 } + catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*) + %ehptr = extractvalue { i8*, i32 } %vals, 0 + %sel = extractvalue { i8*, i32 } %vals, 1 + %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)) + %is_filter = icmp eq i32 %sel, %filter_sel + br i1 %is_filter, label %catch-return, label %catch-resume + +catch-return: + ret i8* %ehptr + +catch-resume: + resume { i8*, i32 } %vals +} + +declare i32 @_except_handler3(...) +declare i32 @__rust_try_filter(i8*, i8*) +declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind From f2fc9209d05f4e6f59c289635af2d107e40a51f5 Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 5 Jul 2015 06:40:49 -0400 Subject: [PATCH 08/23] liblibc: correct Linux ioctl request type --- src/liblibc/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 6e01796ad8227..a67372a63adb6 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -6121,7 +6121,7 @@ pub mod funcs { use types::os::arch::c95::{c_char, c_uchar, c_int, c_uint, c_ulong, size_t}; extern { - pub fn ioctl(d: c_int, request: c_ulong, ...) -> c_int; + pub fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; pub fn sysctl(name: *mut c_int, namelen: c_uint, oldp: *mut c_void, @@ -6153,12 +6153,12 @@ pub mod funcs { #[cfg(any(target_os = "linux", target_os = "android"))] pub mod bsd44 { use types::common::c95::{c_void}; - use types::os::arch::c95::{c_uchar, c_int, size_t}; + use types::os::arch::c95::{c_uchar, c_int, c_ulong, size_t}; extern { #[cfg(not(all(target_os = "android", target_arch = "aarch64")))] pub fn getdtablesize() -> c_int; - pub fn ioctl(d: c_int, request: c_int, ...) -> c_int; + pub fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; pub fn madvise(addr: *mut c_void, len: size_t, advice: c_int) -> c_int; pub fn mincore(addr: *mut c_void, len: size_t, vec: *mut c_uchar) From 0e714c19839412474ef7b7741a19f942c4327645 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Sun, 5 Jul 2015 22:41:23 +0300 Subject: [PATCH 09/23] rustc_trans: always use normalizing_infer_ctxt. --- src/librustc_trans/trans/_match.rs | 2 +- src/librustc_trans/trans/common.rs | 2 +- src/test/run-pass/issue-26805.rs | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/issue-26805.rs diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index c2397ec31cffa..936b0070dfccf 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -1351,7 +1351,7 @@ fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool reassigned: false }; { - let infcx = infer::new_infer_ctxt(bcx.tcx(), &bcx.tcx().tables, None, false); + let infcx = infer::normalizing_infer_ctxt(bcx.tcx(), &bcx.tcx().tables); let mut visitor = euv::ExprUseVisitor::new(&mut rc, &infcx); visitor.walk_expr(body); } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index e1c1ac9a772eb..a4559708bf4e8 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -936,7 +936,7 @@ pub fn normalize_and_test_predicates<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, predicates); let tcx = ccx.tcx(); - let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, true); + let infcx = infer::normalizing_infer_ctxt(tcx, &tcx.tables); let mut selcx = traits::SelectionContext::new(&infcx); let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut(); let cause = traits::ObligationCause::dummy(); diff --git a/src/test/run-pass/issue-26805.rs b/src/test/run-pass/issue-26805.rs new file mode 100644 index 0000000000000..c996c8f65ac8b --- /dev/null +++ b/src/test/run-pass/issue-26805.rs @@ -0,0 +1,15 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct NonOrd; + +fn main() { + let _: Box> = Box::new(vec![NonOrd].into_iter()); +} From 03afbf93ed6fff191e641bc021c42631e9197ba9 Mon Sep 17 00:00:00 2001 From: Liigo Zhuang Date: Mon, 6 Jul 2015 14:55:06 +0800 Subject: [PATCH 10/23] book: introduce `//!` doc comment in comments section Closes #26801 --- src/doc/trpl/comments.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/doc/trpl/comments.md b/src/doc/trpl/comments.md index 7687d2a57da92..e7eb48dc42c52 100644 --- a/src/doc/trpl/comments.md +++ b/src/doc/trpl/comments.md @@ -38,6 +38,17 @@ fn add_one(x: i32) -> i32 { } ``` +There is another style of doc comment, `//!`, to comment containing items (e.g. +crates, modules or functions), instead of the items following it. Commonly used +inside crates root (lib.rs) or modules root (mod.rs): + +``` +//! # The Rust Standard Library +//! +//! The Rust Standard Library provides the essential runtime +//! functionality for building portable Rust software. +``` + When writing doc comments, providing some examples of usage is very, very helpful. You’ll notice we’ve used a new macro here: `assert_eq!`. This compares two values, and `panic!`s if they’re not equal to each other. It’s very helpful From 106f3826e913924573292fa1c9ad9759c5c56663 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 3 Jul 2015 11:34:19 +0200 Subject: [PATCH 11/23] lower blanket unsafe block to actual cases of unsafe and adjust indents --- src/librustc_trans/trans/consts.rs | 594 ++++++++++++++--------------- 1 file changed, 284 insertions(+), 310 deletions(-) diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index e7e2793fc7e9d..ece52b3ad4d6d 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -486,13 +486,12 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, .map(|e| const_expr(cx, &**e, param_substs, fn_args).0) .collect() }; - unsafe { - let _icx = push_ctxt("const_expr"); - match e.node { - ast::ExprLit(ref lit) => { - const_lit(cx, e, &**lit) - } - ast::ExprBinary(b, ref e1, ref e2) => { + let _icx = push_ctxt("const_expr"); + match e.node { + ast::ExprLit(ref lit) => { + const_lit(cx, e, &**lit) + }, + ast::ExprBinary(b, ref e1, ref e2) => { /* Neither type is bottom, and we expect them to be unified * already, so the following is safe. */ let (te1, ty) = const_expr(cx, &**e1, param_substs, fn_args); @@ -512,145 +511,136 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, check_binary_expr_validity(cx, e, ty, te1, te2); - match b.node { - ast::BiAdd => { - if is_float { llvm::LLVMConstFAdd(te1, te2) } - else { llvm::LLVMConstAdd(te1, te2) } - } - ast::BiSub => { - if is_float { llvm::LLVMConstFSub(te1, te2) } - else { llvm::LLVMConstSub(te1, te2) } - } - ast::BiMul => { - if is_float { llvm::LLVMConstFMul(te1, te2) } - else { llvm::LLVMConstMul(te1, te2) } - } - ast::BiDiv => { - if is_float { llvm::LLVMConstFDiv(te1, te2) } - else if signed { llvm::LLVMConstSDiv(te1, te2) } - else { llvm::LLVMConstUDiv(te1, te2) } - } - ast::BiRem => { - if is_float { llvm::LLVMConstFRem(te1, te2) } - else if signed { llvm::LLVMConstSRem(te1, te2) } - else { llvm::LLVMConstURem(te1, te2) } - } - ast::BiAnd => llvm::LLVMConstAnd(te1, te2), - ast::BiOr => llvm::LLVMConstOr(te1, te2), - ast::BiBitXor => llvm::LLVMConstXor(te1, te2), - ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2), - ast::BiBitOr => llvm::LLVMConstOr(te1, te2), - ast::BiShl => { - let te2 = base::cast_shift_const_rhs(b.node, te1, te2); - llvm::LLVMConstShl(te1, te2) - } - ast::BiShr => { - let te2 = base::cast_shift_const_rhs(b.node, te1, te2); - if signed { llvm::LLVMConstAShr(te1, te2) } - else { llvm::LLVMConstLShr(te1, te2) } - } - ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGt | ast::BiGe => { - if is_float { - let cmp = base::bin_op_to_fcmp_predicate(cx, b.node); - ConstFCmp(cmp, te1, te2) - } else { - let cmp = base::bin_op_to_icmp_predicate(cx, b.node, signed); - let bool_val = ConstICmp(cmp, te1, te2); - if is_simd { - // LLVM outputs an `< size x i1 >`, so we need to perform - // a sign extension to get the correctly sized type. - llvm::LLVMConstIntCast(bool_val, val_ty(te1).to_ref(), True) - } else { - bool_val - } - } - } - } - }, - ast::ExprUnary(u, ref inner_e) => { + unsafe { match b.node { + ast::BiAdd if is_float => llvm::LLVMConstFAdd(te1, te2), + ast::BiAdd => llvm::LLVMConstAdd(te1, te2), + + ast::BiSub if is_float => llvm::LLVMConstFSub(te1, te2), + ast::BiSub => llvm::LLVMConstSub(te1, te2), + + ast::BiMul if is_float => llvm::LLVMConstFMul(te1, te2), + ast::BiMul => llvm::LLVMConstMul(te1, te2), + + ast::BiDiv if is_float => llvm::LLVMConstFDiv(te1, te2), + ast::BiDiv if signed => llvm::LLVMConstSDiv(te1, te2), + ast::BiDiv => llvm::LLVMConstUDiv(te1, te2), + + ast::BiRem if is_float => llvm::LLVMConstFRem(te1, te2), + ast::BiRem if signed => llvm::LLVMConstSRem(te1, te2), + ast::BiRem => llvm::LLVMConstURem(te1, te2), + + ast::BiAnd => llvm::LLVMConstAnd(te1, te2), + ast::BiOr => llvm::LLVMConstOr(te1, te2), + ast::BiBitXor => llvm::LLVMConstXor(te1, te2), + ast::BiBitAnd => llvm::LLVMConstAnd(te1, te2), + ast::BiBitOr => llvm::LLVMConstOr(te1, te2), + ast::BiShl => { + let te2 = base::cast_shift_const_rhs(b.node, te1, te2); + llvm::LLVMConstShl(te1, te2) + }, + ast::BiShr => { + let te2 = base::cast_shift_const_rhs(b.node, te1, te2); + if signed { llvm::LLVMConstAShr(te1, te2) } + else { llvm::LLVMConstLShr(te1, te2) } + }, + ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGt | ast::BiGe => { + if is_float { + let cmp = base::bin_op_to_fcmp_predicate(cx, b.node); + ConstFCmp(cmp, te1, te2) + } else { + let cmp = base::bin_op_to_icmp_predicate(cx, b.node, signed); + let bool_val = ConstICmp(cmp, te1, te2); + if is_simd { + // LLVM outputs an `< size x i1 >`, so we need to perform + // a sign extension to get the correctly sized type. + llvm::LLVMConstIntCast(bool_val, val_ty(te1).to_ref(), True) + } else { + bool_val + } + } + }, + } } // unsafe { match b.node { + }, + ast::ExprUnary(u, ref inner_e) => { let (te, ty) = const_expr(cx, &**inner_e, param_substs, fn_args); check_unary_expr_validity(cx, e, ty, te); let is_float = ty.is_fp(); - match u { - ast::UnUniq | ast::UnDeref => { - const_deref(cx, te, ty).0 - } - ast::UnNot => llvm::LLVMConstNot(te), - ast::UnNeg => { - if is_float { llvm::LLVMConstFNeg(te) } - else { llvm::LLVMConstNeg(te) } - } + unsafe { match u { + ast::UnUniq | ast::UnDeref => const_deref(cx, te, ty).0, + ast::UnNot => llvm::LLVMConstNot(te), + ast::UnNeg if is_float => llvm::LLVMConstFNeg(te), + ast::UnNeg => llvm::LLVMConstNeg(te), + } } + }, + ast::ExprField(ref base, field) => { + let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let brepr = adt::represent_type(cx, bt); + expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { + let ix = cx.tcx().field_idx_strict(field.node.name, field_tys); + adt::const_get_field(cx, &*brepr, bv, discr, ix) + }) + }, + ast::ExprTupField(ref base, idx) => { + let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let brepr = adt::represent_type(cx, bt); + expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { + adt::const_get_field(cx, &*brepr, bv, discr, idx.node) + }) + }, + + ast::ExprIndex(ref base, ref index) => { + let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); + let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) { + Ok(ConstVal::Int(i)) => i as u64, + Ok(ConstVal::Uint(u)) => u, + _ => cx.sess().span_bug(index.span, + "index is not an integer-constant expression") + }; + let (arr, len) = match bt.sty { + ty::TyArray(_, u) => (bv, C_uint(cx, u)), + ty::TySlice(_) | ty::TyStr => { + let e1 = const_get_elt(cx, bv, &[0]); + (const_deref_ptr(cx, e1), const_get_elt(cx, bv, &[1])) + }, + ty::TyRef(_, mt) => match mt.ty.sty { + ty::TyArray(_, u) => { + (const_deref_ptr(cx, bv), C_uint(cx, u)) + }, + _ => cx.sess().span_bug(base.span, + &format!("index-expr base must be a vector \ + or string type, found {:?}", + bt)), + }, + _ => cx.sess().span_bug(base.span, + &format!("index-expr base must be a vector \ + or string type, found {:?}", + bt)), + }; + + let len = unsafe { llvm::LLVMConstIntGetZExtValue(len) as u64 }; + let len = match bt.sty { + ty::TyBox(ty) | ty::TyRef(_, ty::mt{ty, ..}) => match ty.sty { + ty::TyStr => { + assert!(len > 0); + len - 1 + }, + _ => len, + }, + _ => len, + }; + if iv >= len { + // FIXME #3170: report this earlier on in the const-eval + // pass. Reporting here is a bit late. + cx.sess().span_err(e.span, + "const index-expr is out of bounds"); + C_undef(type_of::type_of(cx, bt).element_type()) + } else { + const_get_elt(cx, arr, &[iv as c_uint]) } - } - ast::ExprField(ref base, field) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); - let brepr = adt::represent_type(cx, bt); - expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| { - let ix = cx.tcx().field_idx_strict(field.node.name, field_tys); - adt::const_get_field(cx, &*brepr, bv, discr, ix) - }) - } - ast::ExprTupField(ref base, idx) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); - let brepr = adt::represent_type(cx, bt); - expr::with_field_tys(cx.tcx(), bt, None, |discr, _| { - adt::const_get_field(cx, &*brepr, bv, discr, idx.node) - }) - } - - ast::ExprIndex(ref base, ref index) => { - let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); - let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) { - Ok(ConstVal::Int(i)) => i as u64, - Ok(ConstVal::Uint(u)) => u, - _ => cx.sess().span_bug(index.span, - "index is not an integer-constant expression") - }; - let (arr, len) = match bt.sty { - ty::TyArray(_, u) => (bv, C_uint(cx, u)), - ty::TySlice(_) | ty::TyStr => { - let e1 = const_get_elt(cx, bv, &[0]); - (const_deref_ptr(cx, e1), const_get_elt(cx, bv, &[1])) - } - ty::TyRef(_, mt) => match mt.ty.sty { - ty::TyArray(_, u) => { - (const_deref_ptr(cx, bv), C_uint(cx, u)) - }, - _ => cx.sess().span_bug(base.span, - &format!("index-expr base must be a vector \ - or string type, found {:?}", - bt)) - }, - _ => cx.sess().span_bug(base.span, - &format!("index-expr base must be a vector \ - or string type, found {:?}", - bt)) - }; - - let len = llvm::LLVMConstIntGetZExtValue(len) as u64; - let len = match bt.sty { - ty::TyBox(ty) | ty::TyRef(_, ty::mt{ty, ..}) => match ty.sty { - ty::TyStr => { - assert!(len > 0); - len - 1 - } - _ => len - }, - _ => len - }; - if iv >= len { - // FIXME #3170: report this earlier on in the const-eval - // pass. Reporting here is a bit late. - cx.sess().span_err(e.span, - "const index-expr is out of bounds"); - C_undef(type_of::type_of(cx, bt).element_type()) - } else { - const_get_elt(cx, arr, &[iv as c_uint]) - } - } - ast::ExprCast(ref base, _) => { + }, + ast::ExprCast(ref base, _) => { let t_cast = ety; let llty = type_of::type_of(cx, t_cast); let (v, t_expr) = const_expr(cx, &**base, param_substs, fn_args); @@ -671,136 +661,121 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, return addr; } } - match (CastTy::from_ty(cx.tcx(), t_expr).expect("bad input type for cast"), - CastTy::from_ty(cx.tcx(), t_cast).expect("bad output type for cast")) { - (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => { - let repr = adt::represent_type(cx, t_expr); - let discr = adt::const_get_discrim(cx, &*repr, v); - let iv = C_integral(cx.int_type(), discr, false); - let s = adt::is_discr_signed(&*repr) as Bool; - llvm::LLVMConstIntCast(iv, llty.to_ref(), s) - } - (CastTy::Int(_), CastTy::Int(_)) => { - let s = t_expr.is_signed() as Bool; - llvm::LLVMConstIntCast(v, llty.to_ref(), s) - } - (CastTy::Int(_), CastTy::Float) => { - if t_expr.is_signed() { - llvm::LLVMConstSIToFP(v, llty.to_ref()) - } else { - llvm::LLVMConstUIToFP(v, llty.to_ref()) - } - } - (CastTy::Float, CastTy::Float) => { - llvm::LLVMConstFPCast(v, llty.to_ref()) - } - (CastTy::Float, CastTy::Int(IntTy::I)) => { - llvm::LLVMConstFPToSI(v, llty.to_ref()) - } - (CastTy::Float, CastTy::Int(_)) => { - llvm::LLVMConstFPToUI(v, llty.to_ref()) - } - (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_)) - | (CastTy::RPtr(_), CastTy::Ptr(_)) => { - ptrcast(v, llty) - } - (CastTy::FnPtr, CastTy::FnPtr) => ptrcast(v, llty), // isn't this a coercion? - (CastTy::Int(_), CastTy::Ptr(_)) => { - llvm::LLVMConstIntToPtr(v, llty.to_ref()) - } - (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { - llvm::LLVMConstPtrToInt(v, llty.to_ref()) - } - _ => { - cx.sess().impossible_case(e.span, - "bad combination of types for cast") - } - } - } - ast::ExprAddrOf(ast::MutImmutable, ref sub) => { - // If this is the address of some static, then we need to return - // the actual address of the static itself (short circuit the rest - // of const eval). - let mut cur = sub; - loop { - match cur.node { - ast::ExprParen(ref sub) => cur = sub, - ast::ExprBlock(ref blk) => { + unsafe { match ( + CastTy::from_ty(cx.tcx(), t_expr).expect("bad input type for cast"), + CastTy::from_ty(cx.tcx(), t_cast).expect("bad output type for cast"), + ) { + (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => { + let repr = adt::represent_type(cx, t_expr); + let discr = adt::const_get_discrim(cx, &*repr, v); + let iv = C_integral(cx.int_type(), discr, false); + let s = adt::is_discr_signed(&*repr) as Bool; + llvm::LLVMConstIntCast(iv, llty.to_ref(), s) + }, + (CastTy::Int(_), CastTy::Int(_)) => { + let s = t_expr.is_signed() as Bool; + llvm::LLVMConstIntCast(v, llty.to_ref(), s) + }, + (CastTy::Int(_), CastTy::Float) => { + if t_expr.is_signed() { + llvm::LLVMConstSIToFP(v, llty.to_ref()) + } else { + llvm::LLVMConstUIToFP(v, llty.to_ref()) + } + }, + (CastTy::Float, CastTy::Float) => llvm::LLVMConstFPCast(v, llty.to_ref()), + (CastTy::Float, CastTy::Int(IntTy::I)) => llvm::LLVMConstFPToSI(v, llty.to_ref()), + (CastTy::Float, CastTy::Int(_)) => llvm::LLVMConstFPToUI(v, llty.to_ref()), + (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FnPtr, CastTy::Ptr(_)) + | (CastTy::RPtr(_), CastTy::Ptr(_)) => { + ptrcast(v, llty) + }, + (CastTy::FnPtr, CastTy::FnPtr) => ptrcast(v, llty), // isn't this a coercion? + (CastTy::Int(_), CastTy::Ptr(_)) => llvm::LLVMConstIntToPtr(v, llty.to_ref()), + (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => { + llvm::LLVMConstPtrToInt(v, llty.to_ref()) + }, + _ => { + cx.sess().impossible_case(e.span, + "bad combination of types for cast") + }, + } } // unsafe { match ( ... ) { + }, + ast::ExprAddrOf(ast::MutImmutable, ref sub) => { + // If this is the address of some static, then we need to return + // the actual address of the static itself (short circuit the rest + // of const eval). + let mut cur = sub; + loop { + match cur.node { + ast::ExprParen(ref sub) => cur = sub, + ast::ExprBlock(ref blk) => { if let Some(ref sub) = blk.expr { cur = sub; } else { break; } - } - _ => break, - } - } - let opt_def = cx.tcx().def_map.borrow().get(&cur.id).map(|d| d.full_def()); - if let Some(def::DefStatic(def_id, _)) = opt_def { - get_static_val(cx, def_id, ety) - } else { - // If this isn't the address of a static, then keep going through - // normal constant evaluation. - let (v, _) = const_expr(cx, &**sub, param_substs, fn_args); - addr_of(cx, v, "ref") - } - } - ast::ExprAddrOf(ast::MutMutable, ref sub) => { - let (v, _) = const_expr(cx, &**sub, param_substs, fn_args); - addr_of_mut(cx, v, "ref_mut_slice") - } - ast::ExprTup(ref es) => { - let repr = adt::represent_type(cx, ety); - let vals = map_list(&es[..]); - adt::trans_const(cx, &*repr, 0, &vals[..]) - } - ast::ExprStruct(_, ref fs, ref base_opt) => { - let repr = adt::represent_type(cx, ety); - - let base_val = match *base_opt { + }, + _ => break, + } + } + let opt_def = cx.tcx().def_map.borrow().get(&cur.id).map(|d| d.full_def()); + if let Some(def::DefStatic(def_id, _)) = opt_def { + get_static_val(cx, def_id, ety) + } else { + // If this isn't the address of a static, then keep going through + // normal constant evaluation. + let (v, _) = const_expr(cx, &**sub, param_substs, fn_args); + addr_of(cx, v, "ref") + } + }, + ast::ExprAddrOf(ast::MutMutable, ref sub) => { + let (v, _) = const_expr(cx, &**sub, param_substs, fn_args); + addr_of_mut(cx, v, "ref_mut_slice") + }, + ast::ExprTup(ref es) => { + let repr = adt::represent_type(cx, ety); + let vals = map_list(&es[..]); + adt::trans_const(cx, &*repr, 0, &vals[..]) + }, + ast::ExprStruct(_, ref fs, ref base_opt) => { + let repr = adt::represent_type(cx, ety); + + let base_val = match *base_opt { Some(ref base) => Some(const_expr(cx, &**base, param_substs, fn_args)), None => None - }; - - expr::with_field_tys(cx.tcx(), ety, Some(e.id), |discr, field_tys| { - let cs = field_tys.iter().enumerate() - .map(|(ix, &field_ty)| { - match fs.iter().find(|f| field_ty.name == f.ident.node.name) { - Some(ref f) => const_expr(cx, &*f.expr, param_substs, fn_args).0, - None => { - match base_val { - Some((bv, _)) => { - adt::const_get_field(cx, &*repr, bv, - discr, ix) - } - None => { - cx.sess().span_bug(e.span, - "missing struct field") - } - } - } - } - }).collect::>(); - if ety.is_simd(cx.tcx()) { - C_vector(&cs[..]) - } else { - adt::trans_const(cx, &*repr, discr, &cs[..]) - } - }) - } - ast::ExprVec(ref es) => { + }; + + expr::with_field_tys(cx.tcx(), ety, Some(e.id), |discr, field_tys| { + let cs = field_tys.iter().enumerate() + .map(|(ix, &field_ty)| { + match (fs.iter().find(|f| field_ty.name == f.ident.node.name), base_val) { + (Some(ref f), _) => const_expr(cx, &*f.expr, param_substs, fn_args).0, + (_, Some((bv, _))) => adt::const_get_field(cx, &*repr, bv, discr, ix), + (_, None) => cx.sess().span_bug(e.span, "missing struct field"), + } + }).collect::>(); + if ety.is_simd(cx.tcx()) { + C_vector(&cs[..]) + } else { + adt::trans_const(cx, &*repr, discr, &cs[..]) + } + }) + }, + ast::ExprVec(ref es) => { let unit_ty = ety.sequence_element_type(cx.tcx()); let llunitty = type_of::type_of(cx, unit_ty); - let vs = es.iter().map(|e| const_expr(cx, &**e, param_substs, fn_args).0) - .collect::>(); + let vs = es.iter() + .map(|e| const_expr(cx, &**e, param_substs, fn_args).0) + .collect::>(); // If the vector contains enums, an LLVM array won't work. if vs.iter().any(|vi| val_ty(*vi) != llunitty) { C_struct(cx, &vs[..], false) } else { C_array(llunitty, &vs[..]) } - } - ast::ExprRepeat(ref elem, ref count) => { + }, + ast::ExprRepeat(ref elem, ref count) => { let unit_ty = ety.sequence_element_type(cx.tcx()); let llunitty = type_of::type_of(cx, unit_ty); let n = cx.tcx().eval_repeat_count(count); @@ -811,8 +786,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } else { C_array(llunitty, &vs[..]) } - } - ast::ExprPath(..) => { + }, + ast::ExprPath(..) => { let def = cx.tcx().def_map.borrow().get(&e.id).unwrap().full_def(); match def { def::DefLocal(id) => { @@ -853,69 +828,68 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, or variant def") } } - } - ast::ExprCall(ref callee, ref args) => { - let mut callee = &**callee; - loop { - callee = match callee.node { - ast::ExprParen(ref inner) => &**inner, - ast::ExprBlock(ref block) => match block.expr { - Some(ref tail) => &**tail, - None => break - }, - _ => break - }; - } - let def = cx.tcx().def_map.borrow()[&callee.id].full_def(); - let arg_vals = map_list(args); - match def { - def::DefFn(did, _) | def::DefMethod(did, _) => { - const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs) - } - def::DefStruct(_) => { - if ety.is_simd(cx.tcx()) { - C_vector(&arg_vals[..]) - } else { - let repr = adt::represent_type(cx, ety); - adt::trans_const(cx, &*repr, 0, &arg_vals[..]) - } - } - def::DefVariant(enum_did, variant_did, _) => { - let repr = adt::represent_type(cx, ety); - let vinfo = cx.tcx().enum_variant_with_id(enum_did, variant_did); - adt::trans_const(cx, - &*repr, - vinfo.disr_val, - &arg_vals[..]) - } - _ => cx.sess().span_bug(e.span, "expected a struct, variant, or const fn def") - } - } - ast::ExprMethodCall(_, _, ref args) => { - let arg_vals = map_list(args); - let method_call = ty::MethodCall::expr(e.id); + }, + ast::ExprCall(ref callee, ref args) => { + let mut callee = &**callee; + loop { + callee = match callee.node { + ast::ExprParen(ref inner) => &**inner, + ast::ExprBlock(ref block) => match block.expr { + Some(ref tail) => &**tail, + None => break, + }, + _ => break, + }; + } + let def = cx.tcx().def_map.borrow()[&callee.id].full_def(); + let arg_vals = map_list(args); + match def { + def::DefFn(did, _) | def::DefMethod(did, _) => { + const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs) + }, + def::DefStruct(_) => { + if ety.is_simd(cx.tcx()) { + C_vector(&arg_vals[..]) + } else { + let repr = adt::represent_type(cx, ety); + adt::trans_const(cx, &*repr, 0, &arg_vals[..]) + } + }, + def::DefVariant(enum_did, variant_did, _) => { + let repr = adt::represent_type(cx, ety); + let vinfo = cx.tcx().enum_variant_with_id(enum_did, variant_did); + adt::trans_const(cx, + &*repr, + vinfo.disr_val, + &arg_vals[..]) + }, + _ => cx.sess().span_bug(e.span, "expected a struct, variant, or const fn def"), + } + }, + ast::ExprMethodCall(_, _, ref args) => { + let arg_vals = map_list(args); + let method_call = ty::MethodCall::expr(e.id); let method_did = cx.tcx().tables.borrow().method_map[&method_call].def_id; - const_fn_call(cx, MethodCallKey(method_call), - method_did, &arg_vals, param_substs) - } - ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs, fn_args).0, - ast::ExprBlock(ref block) => { + const_fn_call(cx, MethodCallKey(method_call), + method_did, &arg_vals, param_substs) + }, + ast::ExprParen(ref e) => const_expr(cx, &**e, param_substs, fn_args).0, + ast::ExprBlock(ref block) => { match block.expr { Some(ref expr) => const_expr(cx, &**expr, param_substs, fn_args).0, - None => C_nil(cx) + None => C_nil(cx), } - } - ast::ExprClosure(_, ref decl, ref body) => { + }, + ast::ExprClosure(_, ref decl, ref body) => { closure::trans_closure_expr(closure::Dest::Ignore(cx), decl, body, e.id, param_substs); C_null(type_of::type_of(cx, ety)) - } - _ => cx.sess().span_bug(e.span, - "bad constant expression type in consts::const_expr") - } + }, + _ => cx.sess().span_bug(e.span, + "bad constant expression type in consts::const_expr"), } } From 155c8f9fa8d67badabd72bec16b9a9b3f49eb8a1 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 29 May 2015 12:36:02 -0400 Subject: [PATCH 12/23] Simplify --- src/etc/mklldeps.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index 7a925fa3f3367..d0f91ba683f9e 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -14,8 +14,7 @@ f = open(sys.argv[1], 'wb') -components = sys.argv[2].split(' ') -components = [i for i in components if i] # ignore extra whitespaces +components = sys.argv[2].split() # splits on whitespace enable_static = sys.argv[3] llconfig = sys.argv[4] From 158fcbbdd6db45058530abda35ce59d267ee489e Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 29 May 2015 12:36:13 -0400 Subject: [PATCH 13/23] `llconfig` is `llvm-config` --- src/etc/mklldeps.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index d0f91ba683f9e..1cc65406b2c00 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -16,7 +16,7 @@ components = sys.argv[2].split() # splits on whitespace enable_static = sys.argv[3] -llconfig = sys.argv[4] +llvm_config = sys.argv[4] f.write("""// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at @@ -38,7 +38,7 @@ def run(args): out, err = proc.communicate() if err: - print("failed to run llconfig: args = `{}`".format(args)) + print("failed to run llvm_config: args = `{}`".format(args)) print(err) sys.exit(1) return out @@ -46,7 +46,7 @@ def run(args): f.write("\n") # LLVM libs -args = [llconfig, '--libs', '--system-libs'] +args = [llvm_config, '--libs', '--system-libs'] args.extend(components) out = run(args) @@ -68,13 +68,13 @@ def run(args): f.write(")]\n") # LLVM ldflags -out = run([llconfig, '--ldflags']) +out = run([llvm_config, '--ldflags']) for lib in out.strip().split(' '): if lib[:2] == "-l": f.write("#[link(name = \"" + lib[2:] + "\")]\n") # C++ runtime library -out = run([llconfig, '--cxxflags']) +out = run([llvm_config, '--cxxflags']) if enable_static == '1': assert('stdlib=libc++' not in out) f.write("#[link(name = \"stdc++\", kind = \"static\")]\n") From 1491a8fa01f174a05e16c874fe717b99d103567d Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Sat, 30 May 2015 09:35:34 -0400 Subject: [PATCH 14/23] Remove unused variable --- mk/main.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/mk/main.mk b/mk/main.mk index 39261191fb74f..964ae626d0c8d 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -295,7 +295,6 @@ LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir) LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir) LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir) LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))" -LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS)) LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags) ifeq ($$(findstring freebsd,$(1)),freebsd) # On FreeBSD, it may search wrong headers (that are for pre-installed LLVM), From 78761d64a990a58c7c99688717e77ed0ea477e2a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 6 Jul 2015 18:46:03 +0300 Subject: [PATCH 15/23] don't use type_parameter_def during astconv astconv is called when converting the type-parameter, which leads to a crash. Fixes #26812. --- src/librustc_typeck/astconv.rs | 42 ++++++++++++++++++++-------- src/test/compile-fail/issue-26812.rs | 12 ++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/test/compile-fail/issue-26812.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 7f29af0d84b2d..046e83bf3fcbc 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1113,6 +1113,7 @@ fn report_ambiguous_associated_type(tcx: &ty::ctxt, // any ambiguity. fn find_bound_for_assoc_item<'tcx>(this: &AstConv<'tcx>, ty_param_node_id: ast::NodeId, + ty_param_name: Option, assoc_name: ast::Name, span: Span) -> Result, ErrorReported> @@ -1138,12 +1139,21 @@ fn find_bound_for_assoc_item<'tcx>(this: &AstConv<'tcx>, .filter(|b| this.trait_defines_associated_type_named(b.def_id(), assoc_name)) .collect(); - let ty_param_name = tcx.type_parameter_def(ty_param_node_id).name; - one_bound_for_assoc_type(tcx, - suitable_bounds, - &token::get_name(ty_param_name), - &token::get_name(assoc_name), - span) + if let Some(s) = ty_param_name { + // borrowck doesn't like this any other way + one_bound_for_assoc_type(tcx, + suitable_bounds, + &token::get_name(s), + &token::get_name(assoc_name), + span) + } else { + one_bound_for_assoc_type(tcx, + suitable_bounds, + "Self", + &token::get_name(assoc_name), + span) + + } } @@ -1240,12 +1250,20 @@ fn associated_path_def_to_ty<'tcx>(this: &AstConv<'tcx>, _ => unreachable!() } } - (&ty::TyParam(_), def::DefTyParam(..)) | - (&ty::TyParam(_), def::DefSelfTy(Some(_), None)) => { - // A type parameter or Self, we need to find the associated item from - // a bound. - let ty_param_node_id = ty_path_def.local_node_id(); - match find_bound_for_assoc_item(this, ty_param_node_id, assoc_name, span) { + (&ty::TyParam(_), def::DefSelfTy(Some(trait_did), None)) => { + assert_eq!(trait_did.krate, ast::LOCAL_CRATE); + match find_bound_for_assoc_item(this, trait_did.node, None, assoc_name, span) { + Ok(bound) => bound, + Err(ErrorReported) => return (tcx.types.err, ty_path_def), + } + } + (&ty::TyParam(_), def::DefTyParam(_, _, param_did, param_name)) => { + assert_eq!(param_did.krate, ast::LOCAL_CRATE); + match find_bound_for_assoc_item(this, + param_did.node, + Some(param_name), + assoc_name, + span) { Ok(bound) => bound, Err(ErrorReported) => return (tcx.types.err, ty_path_def), } diff --git a/src/test/compile-fail/issue-26812.rs b/src/test/compile-fail/issue-26812.rs new file mode 100644 index 0000000000000..c1ccfe269cdd3 --- /dev/null +++ b/src/test/compile-fail/issue-26812.rs @@ -0,0 +1,12 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn avg(_: T) {} //~ ERROR associated type `Item` not found for `T` +fn main() {} From 8cb1faaa907395d9bcf81919d7e94f3b7560dccf Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jul 2015 12:14:49 -0400 Subject: [PATCH 16/23] Link to test suite information from CONTRIBUTING.md Fixes #24802 --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bcb3219858c9..7bf144c713315 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,6 +108,10 @@ will run all the tests on every platform we support. If it all works out, [merge-queue]: http://buildbot.rust-lang.org/homu/queue/rust +Speaking of tests, Rust has a comprehensive test suite. More information about +it can be found +[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md). + ## Writing Documentation Documentation improvements are very welcome. The source of `doc.rust-lang.org` From fb6eeb6ce8980c23daec0361e61dfe406f535eb5 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jul 2015 12:27:32 -0400 Subject: [PATCH 17/23] Document _ in bindings Fixes #25786 --- src/doc/trpl/patterns.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/doc/trpl/patterns.md b/src/doc/trpl/patterns.md index 7a1f8bf21bf34..9603eec7aca71 100644 --- a/src/doc/trpl/patterns.md +++ b/src/doc/trpl/patterns.md @@ -282,6 +282,38 @@ This ‘destructuring’ behavior works on any compound data type, like [tuples]: primitive-types.html#tuples [enums]: enums.html +# Ignoring bindings + +You can use `_` in a pattern to disregard the value. For example, here’s a +`match` against a `Result`: + +```rust +# let some_value: Result = Err("There was an error"); +match some_value { + Ok(value) => println!("got a value: {}", value), + Err(_) => println!("an error occurred"), +} +``` + +In the first arm, we bind the value inside the `Ok` variant to `value`. But +in the `Err` arm, we use `_` to disregard the specific error, and just print +a general error message. + +`_` is valid in any pattern that creates a binding. This can be useful to +ignore parts of a larger structure: + +```rust +fn coordinate() -> (i32, i32, i32) { + // generate and return some sort of triple tuple +# (1, 2, 3) +} + +let (x, _, z) = coordinate(); +``` + +Here, we bind the first and last element of the tuple to `x` and `z`, but +ignore the middle element. + # Mix and Match Whew! That’s a lot of different ways to match things, and they can all be From 555b021c6e531fc375c62160a176dcc4fe77b798 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Fri, 26 Jun 2015 10:32:42 -0700 Subject: [PATCH 18/23] rustc_driver: Print stage info in --version --verbose --- src/librustc_driver/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index a9787987611f7..9a9ac2706a6ab 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -481,6 +481,21 @@ pub fn commit_date_str() -> Option<&'static str> { option_env!("CFG_VER_DATE") } +/// Returns a stage string, such as "stage0". +pub fn stage_str() -> Option<&'static str> { + if cfg!(stage0) { + Some("stage0") + } else if cfg!(stage1) { + Some("stage1") + } else if cfg!(stage2) { + Some("stage2") + } else if cfg!(stage3) { + Some("stage3") + } else { + None + } +} + /// Prints version information pub fn version(binary: &str, matches: &getopts::Matches) { let verbose = matches.opt_present("verbose"); @@ -493,6 +508,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(release_str())); + println!("stage: {}", unw(stage_str())); } } From ee43c5e2f0d838de2721b8c5a30aa25d882398d9 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Fri, 3 Jul 2015 12:56:51 -0400 Subject: [PATCH 19/23] FFI panic is UB I incorrectly stated that it's an abort. --- src/doc/trpl/ffi.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/doc/trpl/ffi.md b/src/doc/trpl/ffi.md index 442a1f062ef47..cbedf86371414 100644 --- a/src/doc/trpl/ffi.md +++ b/src/doc/trpl/ffi.md @@ -533,19 +533,10 @@ attribute turns off Rust's name mangling, so that it is easier to link to. # FFI and panics -It’s important to be mindful of `panic!`s when working with FFI. This code, -when called from C, will `abort`: - -```rust -#[no_mangle] -pub extern fn oh_no() -> ! { - panic!("Oops!"); -} -# fn main() {} -``` - -If you’re writing code that may panic, you should run it in another thread, -so that the panic doesn’t bubble up to C: +It’s important to be mindful of `panic!`s when working with FFI. A `panic!` +across an FFI boundary is undefined behavior. If you’re writing code that may +panic, you should run it in another thread, so that the panic doesn’t bubble up +to C: ```rust use std::thread; From c2f4f11443ba9e8eb0b69f245bcfc7eb985082fa Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Mon, 6 Jul 2015 22:01:20 +0200 Subject: [PATCH 20/23] reference: do not display the extra space --- src/doc/reference.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index a3e13acccae28..c5bffd0f0db44 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2876,7 +2876,6 @@ operand. ``` # let mut x = 0; # let y = 0; - x = y; ``` From dd78ffe828ac1ea09949bc9c5bc07082088ceba4 Mon Sep 17 00:00:00 2001 From: Tshepang Lekhonkhobe Date: Mon, 6 Jul 2015 22:10:21 +0200 Subject: [PATCH 21/23] reference: make 'Move and copied types' section more simple --- src/doc/reference.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index a3e13acccae28..199a0894b6ec9 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2509,9 +2509,8 @@ Here are some examples: #### Moved and copied types When a [local variable](#variables) is used as an -[rvalue](#lvalues,-rvalues-and-temporaries) the variable will either be moved -or copied, depending on its type. All values whose type implements `Copy` are -copied, all others are moved. +[rvalue](#lvalues,-rvalues-and-temporaries), the variable will be copied +if its type implements `Copy`. All others are moved. ### Literal expressions From e66ac43ea4ca489486c5c5dc59974577449fad44 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Mon, 6 Jul 2015 12:43:01 -0700 Subject: [PATCH 22/23] rustc_driver: Omit stage info for stage2+ --- src/librustc_driver/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 9a9ac2706a6ab..36438ccc784f8 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -487,10 +487,6 @@ pub fn stage_str() -> Option<&'static str> { Some("stage0") } else if cfg!(stage1) { Some("stage1") - } else if cfg!(stage2) { - Some("stage2") - } else if cfg!(stage3) { - Some("stage3") } else { None } @@ -508,7 +504,9 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(release_str())); - println!("stage: {}", unw(stage_str())); + if let Some(stage) = stage_str() { + println!("stage: {}", stage); + } } } From 720da310a92242290df5623bf3d5e2ea5f83e7f8 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 6 Jul 2015 14:46:21 -0400 Subject: [PATCH 23/23] Add note about special make targets --- CONTRIBUTING.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0bcb3219858c9..a2ac20deb2e5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,6 +83,21 @@ feature. We use the 'fork and pull' model described there. Please make pull requests against the `master` branch. +Compiling all of `make check` can take a while. When testing your pull request, +consider using one of the more specialized `make` targets to cut down on the +amount of time you have to wait. You need to have built the compiler at least +once before running these will work, but that’s only one full build rather than +one each time. + + $ make -j8 rustc-stage1 && make check-stage1 + +is one such example, which builds just `rustc`, and then runs the tests. If +you’re adding something to the standard library, try + + $ make -j8 check-stage1-std NO_REBUILD=1 + +This will not rebuild the compiler, but will run the tests. + All pull requests are reviewed by another person. We have a bot, @rust-highfive, that will automatically assign a random person to review your request.