diff --git a/configure b/configure index f4e1d41276aa9..dbca73415fec6 100755 --- a/configure +++ b/configure @@ -551,6 +551,7 @@ opt valgrind-rpass 1 "run rpass-valgrind tests with valgrind" opt docs 1 "build standard library documentation" opt compiler-docs 0 "build compiler documentation" opt optimize-tests 1 "build tests with optimizations" +opt debuginfo-tests 0 "build tests with debugger metadata" opt libcpp 1 "build with llvm with libc++ instead of libstdc++ when using clang" opt llvm-assertions 0 "build LLVM with assertions" opt debug-assertions 0 "build with debugging assertions" diff --git a/mk/tests.mk b/mk/tests.mk index 3c4818f65dade..f391d8555fc2b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -632,6 +632,13 @@ ifndef CFG_DISABLE_OPTIMIZE_TESTS CTEST_RUSTC_FLAGS += -O endif +# Analogously to the above, whether to pass `-g` when compiling tests +# is a separate choice from whether to pass `-g` when building the +# compiler and standard library themselves. +CTEST_RUSTC_FLAGS := $$(subst -g,,$$(CTEST_RUSTC_FLAGS)) +ifdef CFG_ENABLE_DEBUGINFO_TESTS +CTEST_RUSTC_FLAGS += -g +endif CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --compile-lib-path $$(HLIB$(1)_H_$(3)) \ diff --git a/src/doc/not_found.md b/src/doc/not_found.md index eae2bf1925a99..e740bf3c223ae 100644 --- a/src/doc/not_found.md +++ b/src/doc/not_found.md @@ -57,8 +57,12 @@ function populate_rust_search() { // #18540, use a single token + var a = document.createElement("a"); + a.href = "http://doc.rust-lang.org/core/?search=" + encodeURIComponent(lt); + a.textContent = lt; var search = document.getElementById('core-search'); - search.innerHTML = "" + lt + ""; + search.innerHTML = ""; + search.appendChild(a); } populate_site_search(); populate_rust_search(); diff --git a/src/doc/reference.md b/src/doc/reference.md index a71f8cf4250a6..19cbd6f90a581 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -103,7 +103,7 @@ Non-doc comments are interpreted as a form of whitespace. ## Whitespace -Whitespace is any non-empty string containing any the following characters: +Whitespace is any non-empty string containing only the following characters: - `U+0020` (space, `' '`) - `U+0009` (tab, `'\t'`) @@ -1897,8 +1897,8 @@ release builds. There are two kinds of configuration options, one that is either defined or not (`#[cfg(foo)]`), and the other that contains a string that can be checked -against (`#[cfg(bar = "baz")]` (currently only compiler-defined configuration -options can have the latter form). +against (`#[cfg(bar = "baz")]`). Currently, only compiler-defined configuration +options can have the latter form. ``` // The function is only included in the build when compiling for OSX diff --git a/src/doc/trpl/README.md b/src/doc/trpl/README.md index a892f67d571af..280665af78742 100644 --- a/src/doc/trpl/README.md +++ b/src/doc/trpl/README.md @@ -127,7 +127,7 @@ vector. When we try to compile this program, we get an error: ```text error: cannot borrow `x` as mutable because it is also borrowed as immutable - x.push(4); + x.push("foo"); ^ note: previous borrow of `x` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `x` until the borrow ends diff --git a/src/doc/trpl/benchmark-tests.md b/src/doc/trpl/benchmark-tests.md index 890a2f8ae7de7..887965375932b 100644 --- a/src/doc/trpl/benchmark-tests.md +++ b/src/doc/trpl/benchmark-tests.md @@ -13,7 +13,7 @@ pub fn add_two(a: i32) -> i32 { } #[cfg(test)] -mod test { +mod tests { use super::*; use test::Bencher; diff --git a/src/doc/trpl/macros.md b/src/doc/trpl/macros.md index 9d01f104ddaaf..d504fab206ddf 100644 --- a/src/doc/trpl/macros.md +++ b/src/doc/trpl/macros.md @@ -765,7 +765,7 @@ as `unimplemented!` until you’re ready to write them. # Procedural macros If Rust’s macro system can’t do what you need, you may want to write a -[compiler plugin](plugins.html) instead. Compared to `macro_rules!` +[compiler plugin](compiler-plugins.html) instead. Compared to `macro_rules!` macros, this is significantly more work, the interfaces are much less stable, and bugs can be much harder to track down. In exchange you get the flexibility of running arbitrary Rust code within the compiler. Syntax diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 9c6a293f421e7..ac43055a7c4a5 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -139,6 +139,21 @@ extern "rust-intrinsic" { pub fn atomic_fence_rel(); pub fn atomic_fence_acqrel(); + /// A compiler-only memory barrier. + /// + /// Memory accesses will never be reordered across this barrier by the compiler, + /// but no instructions will be emitted for it. This is appropriate for operations + /// on the same thread that may be preempted, such as when interacting with signal + /// handlers. + #[cfg(not(stage0))] // SNAP 857ef6e + pub fn atomic_singlethreadfence(); + #[cfg(not(stage0))] // SNAP 857ef6e + pub fn atomic_singlethreadfence_acq(); + #[cfg(not(stage0))] // SNAP 857ef6e + pub fn atomic_singlethreadfence_rel(); + #[cfg(not(stage0))] // SNAP 857ef6e + pub fn atomic_singlethreadfence_acqrel(); + /// Aborts the execution of the process. pub fn abort() -> !; @@ -255,12 +270,17 @@ extern "rust-intrinsic" { /// Returns `true` if a type is managed (will be allocated on the local heap) pub fn owns_managed() -> bool; - /// Calculates the offset from a pointer. The offset *must* be in-bounds of - /// the object, or one-byte-past-the-end. An arithmetic overflow is also - /// undefined behaviour. + /// Calculates the offset from a pointer. /// /// This is implemented as an intrinsic to avoid converting to and from an /// integer, since the conversion would throw away aliasing information. + /// + /// # Safety + /// + /// Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of an allocated object. If either pointer is out of + /// bounds or arithmetic overflow occurs then any further use of the + /// returned value will result in undefined behavior. pub fn offset(dst: *const T, offset: isize) -> *const T; /// Copies `count * size_of` bytes from `src` to `dst`. The source @@ -562,3 +582,20 @@ extern "rust-intrinsic" { /// cast to a `u64`; if `T` has no discriminant, returns 0. pub fn discriminant_value(v: &T) -> u64; } + +#[cfg(not(stage0))] +extern "rust-intrinsic" { + /// Performs an unchecked signed division, which results in undefined behavior, + /// in cases where y == 0, or x == int::MIN and y == -1 + pub fn unchecked_sdiv(x: T, y: T) -> T; + /// Performs an unchecked unsigned division, which results in undefined behavior, + /// in cases where y == 0 + pub fn unchecked_udiv(x: T, y: T) -> T; + + /// Returns the remainder of an unchecked signed division, which results in + /// undefined behavior, in cases where y == 0, or x == int::MIN and y == -1 + pub fn unchecked_urem(x: T, y: T) -> T; + /// Returns the remainder of an unchecked signed division, which results in + /// undefined behavior, in cases where y == 0 + pub fn unchecked_srem(x: T, y: T) -> T; +} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 44d5333ce1f46..b8638c5b09be2 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -745,7 +745,20 @@ macro_rules! uint_impl { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn trailing_zeros(self) -> u32 { - unsafe { $cttz(self as $ActualT) as u32 } + // As of LLVM 3.6 the codegen for the zero-safe cttz8 intrinsic + // emits two conditional moves on x86_64. By promoting the value to + // u16 and setting bit 8, we get better code without any conditional + // operations. + // FIXME: There's a LLVM patch (http://reviews.llvm.org/D9284) + // pending, remove this workaround once LLVM generates better code + // for cttz8. + unsafe { + if $BITS == 8 { + intrinsics::cttz16(self as u16 | 0x100) as u32 + } else { + $cttz(self as $ActualT) as u32 + } + } } /// Shifts the bits to the left by a specified amount, `n`, diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 00326ecd3e711..47c029f11b38f 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -284,9 +284,10 @@ impl *const T { /// /// # Safety /// - /// The offset must be in-bounds of the object, or one-byte-past-the-end. - /// Otherwise `offset` invokes Undefined Behaviour, regardless of whether - /// the pointer is used. + /// Both the starting and resulting pointer must be either in bounds or one + /// byte past the end of an allocated object. If either pointer is out of + /// bounds or arithmetic overflow occurs then + /// any further use of the returned value will result in undefined behavior. #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub unsafe fn offset(self, count: isize) -> *const T where T: Sized { diff --git a/src/libcoretest/option.rs b/src/libcoretest/option.rs index ad096630127cb..66945ad251f1e 100644 --- a/src/libcoretest/option.rs +++ b/src/libcoretest/option.rs @@ -219,7 +219,6 @@ fn test_ord() { assert!(big > None); } -/* FIXME(#20575) #[test] fn test_collect() { let v: Option> = (0..0).map(|_| Some(0)).collect(); @@ -241,28 +240,26 @@ fn test_collect() { assert!(v == None); } -*/ + #[test] fn test_cloned() { - let val1 = 1u32; - let mut val2 = 2u32; - let val1_ref = &val1; + let val = 1u32; + let val_ref = &val; let opt_none: Option<&'static u32> = None; - let opt_ref = Some(&val1); - let opt_ref_ref = Some(&val1_ref); - let opt_mut_ref = Some(&mut val2); + let opt_ref = Some(&val); + let opt_ref_ref = Some(&val_ref); // None works assert_eq!(opt_none.clone(), None); assert_eq!(opt_none.cloned(), None); // Immutable ref works - assert_eq!(opt_ref.clone(), Some(&val1)); + assert_eq!(opt_ref.clone(), Some(&val)); assert_eq!(opt_ref.cloned(), Some(1u32)); // Double Immutable ref works - assert_eq!(opt_ref_ref.clone(), Some(&val1_ref)); - assert_eq!(opt_ref_ref.clone().cloned(), Some(&val1)); + assert_eq!(opt_ref_ref.clone(), Some(&val_ref)); + assert_eq!(opt_ref_ref.clone().cloned(), Some(&val)); assert_eq!(opt_ref_ref.cloned().cloned(), Some(1u32)); } diff --git a/src/libcoretest/result.rs b/src/libcoretest/result.rs index ac8c2b953ae96..3fdb102875332 100644 --- a/src/libcoretest/result.rs +++ b/src/libcoretest/result.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub fn op1() -> Result { Ok(666) } -pub fn op2() -> Result { Err("sadface") } +fn op1() -> Result { Ok(666) } +fn op2() -> Result { Err("sadface") } #[test] -pub fn test_and() { +fn test_and() { assert_eq!(op1().and(Ok(667)).unwrap(), 667); assert_eq!(op1().and(Err::("bad")).unwrap_err(), "bad"); @@ -23,7 +23,7 @@ pub fn test_and() { } #[test] -pub fn test_and_then() { +fn test_and_then() { assert_eq!(op1().and_then(|i| Ok::(i + 1)).unwrap(), 667); assert_eq!(op1().and_then(|_| Err::("bad")).unwrap_err(), "bad"); @@ -35,7 +35,7 @@ pub fn test_and_then() { } #[test] -pub fn test_or() { +fn test_or() { assert_eq!(op1().or(Ok::<_, &'static str>(667)).unwrap(), 666); assert_eq!(op1().or(Err("bad")).unwrap(), 666); @@ -44,7 +44,7 @@ pub fn test_or() { } #[test] -pub fn test_or_else() { +fn test_or_else() { assert_eq!(op1().or_else(|_| Ok::(667)).unwrap(), 666); assert_eq!(op1().or_else(|e| Err::(e)).unwrap(), 666); @@ -54,18 +54,17 @@ pub fn test_or_else() { } #[test] -pub fn test_impl_map() { +fn test_impl_map() { assert!(Ok::(1).map(|x| x + 1) == Ok(2)); assert!(Err::(1).map(|x| x + 1) == Err(1)); } #[test] -pub fn test_impl_map_err() { +fn test_impl_map_err() { assert!(Ok::(1).map_err(|x| x + 1) == Ok(1)); assert!(Err::(1).map_err(|x| x + 1) == Err(2)); } -/* FIXME(#20575) #[test] fn test_collect() { let v: Result, ()> = (0..0).map(|_| Ok::(0)).collect(); @@ -86,10 +85,9 @@ fn test_collect() { let v: Result, isize> = functions.iter_mut().map(|f| (*f)()).collect(); assert!(v == Err(1)); } -*/ #[test] -pub fn test_fmt_default() { +fn test_fmt_default() { let ok: Result = Ok(100); let err: Result = Err("Err"); @@ -100,7 +98,7 @@ pub fn test_fmt_default() { } #[test] -pub fn test_unwrap_or() { +fn test_unwrap_or() { let ok: Result = Ok(100); let ok_err: Result = Err("Err"); @@ -109,7 +107,7 @@ pub fn test_unwrap_or() { } #[test] -pub fn test_unwrap_or_else() { +fn test_unwrap_or_else() { fn handler(msg: &'static str) -> isize { if msg == "I got this." { 50 diff --git a/src/librustc/README.md b/src/librustc/README.md index 31812e19aee86..2ef9dfd4da8da 100644 --- a/src/librustc/README.md +++ b/src/librustc/README.md @@ -71,7 +71,7 @@ The rustc crate itself consists of the following submodules - util: ubiquitous types and helper functions - lib: bindings to LLVM -The entry-point for the compiler is main() in the librustc_trans +The entry-point for the compiler is main() in the librustc_driver crate. The 3 central data structures: diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 8b43f9ada9a33..92be6ac6be54f 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -355,6 +355,22 @@ enum Method { GET, POST } ``` "##, +E0265: r##" +This error indicates that a static or constant references itself. +All statics and constants need to resolve to a value in an acyclic manner. + +For example, neither of the following can be sensibly compiled: + +``` +const X: u32 = X; +``` + +``` +const X: u32 = Y; +const Y: u32 = X; +``` +"##, + E0267: r##" This error indicates the use of loop keyword (break or continue) inside a closure but outside of any loop. Break and continue can be used as normal @@ -500,7 +516,6 @@ register_diagnostics! { E0262, // illegal lifetime parameter name E0263, // lifetime name declared twice in same scope E0264, // unknown external lang item - E0265, // recursive constant E0266, // expected item E0269, // not all control paths return a value E0270, // computation may converge in a function marked as diverging diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 767f17681b4c2..aeb08464ff566 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -25,21 +25,23 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/nightly/")] +#![feature(associated_consts)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(fs_canonicalize)] #![feature(hash)] +#![feature(into_cow)] #![feature(libc)] +#![feature(path_ext)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] +#![feature(slice_patterns)] #![feature(staged_api)] #![feature(std_misc)] -#![feature(path_ext)] #![feature(str_char)] -#![feature(into_cow)] -#![feature(slice_patterns)] #![cfg_attr(test, feature(test))] #![allow(trivial_casts)] @@ -138,7 +140,6 @@ pub mod plugin; pub mod lint; pub mod util { - pub use rustc_back::fs; pub use rustc_back::sha2; pub mod common; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 241d8fd00252c..12112fd45ebe9 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -21,8 +21,10 @@ use metadata::decoder; use metadata::loader; use metadata::loader::CratePaths; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::rc::Rc; +use std::fs; + use syntax::ast; use syntax::abi; use syntax::attr; @@ -32,7 +34,6 @@ use syntax::parse; use syntax::parse::token::InternedString; use syntax::parse::token; use syntax::visit; -use util::fs; use log; pub struct CrateReader<'a> { @@ -322,7 +323,7 @@ impl<'a> CrateReader<'a> { let source = self.sess.cstore.get_used_crate_source(cnum).unwrap(); if let Some(locs) = self.sess.opts.externs.get(name) { let found = locs.iter().any(|l| { - let l = fs::realpath(&Path::new(&l[..])).ok(); + let l = fs::canonicalize(l).ok(); source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || source.rlib.as_ref().map(|p| &p.0) == l.as_ref() }); diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 1567f4b99475c..7d8cf5b22a901 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -18,7 +18,6 @@ use std::fs; use std::io::prelude::*; use std::path::{Path, PathBuf}; -use util::fs as myfs; use session::search_paths::{SearchPaths, PathKind}; #[derive(Copy, Clone)] @@ -191,7 +190,7 @@ pub fn get_or_default_sysroot() -> PathBuf { // Follow symlinks. If the resolved path is relative, make it absolute. fn canonicalize(path: Option) -> Option { path.and_then(|path| { - match myfs::realpath(&path) { + match fs::canonicalize(&path) { Ok(canon) => Some(canon), Err(e) => panic!("failed to get realpath: {}", e), } diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index bbb2452ca29ee..062a156637a35 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -225,12 +225,12 @@ use metadata::encoder; use metadata::filesearch::{FileSearch, FileMatches, FileDoesntMatch}; use syntax::codemap::Span; use syntax::diagnostic::SpanHandler; -use util::fs; use util::common; use rustc_back::target::Target; use std::cmp; use std::collections::HashMap; +use std::fs; use std::io::prelude::*; use std::io; use std::path::{Path, PathBuf}; @@ -430,9 +430,9 @@ impl<'a> Context<'a> { .or_insert_with(|| (HashMap::new(), HashMap::new())); let (ref mut rlibs, ref mut dylibs) = *slot; if rlib { - rlibs.insert(fs::realpath(path).unwrap(), kind); + rlibs.insert(fs::canonicalize(path).unwrap(), kind); } else { - dylibs.insert(fs::realpath(path).unwrap(), kind); + dylibs.insert(fs::canonicalize(path).unwrap(), kind); } FileMatches @@ -660,10 +660,10 @@ impl<'a> Context<'a> { // there's at most one rlib and at most one dylib. for loc in locs { if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(fs::realpath(&loc).unwrap(), + rlibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag); } else { - dylibs.insert(fs::realpath(&loc).unwrap(), + dylibs.insert(fs::canonicalize(&loc).unwrap(), PathKind::ExternFlag); } } diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 80326229618c2..5b0eea6bcc516 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -46,33 +46,35 @@ bitflags! { #[derive(RustcEncodable, RustcDecodable)] flags ConstQualif: u8 { // Const rvalue which can be placed behind a reference. - const PURE_CONST = 0b000000, + const PURE_CONST = 0, // Inner mutability (can not be placed behind a reference) or behind // &mut in a non-global expression. Can be copied from static memory. - const MUTABLE_MEM = 0b000001, + const MUTABLE_MEM = 1 << 0, // Constant value with a type that implements Drop. Can be copied // from static memory, similar to MUTABLE_MEM. - const NEEDS_DROP = 0b000010, + const NEEDS_DROP = 1 << 1, // Even if the value can be placed in static memory, copying it from // there is more expensive than in-place instantiation, and/or it may // be too large. This applies to [T; N] and everything containing it. // N.B.: references need to clear this flag to not end up on the stack. - const PREFER_IN_PLACE = 0b000100, + const PREFER_IN_PLACE = 1 << 2, // May use more than 0 bytes of memory, doesn't impact the constness // directly, but is not allowed to be borrowed mutably in a constant. - const NON_ZERO_SIZED = 0b001000, + const NON_ZERO_SIZED = 1 << 3, // Actually borrowed, has to always be in static memory. Does not // propagate, and requires the expression to behave like a 'static // lvalue. The set of expressions with this flag is the minimum // that have to be promoted. - const HAS_STATIC_BORROWS = 0b010000, + const HAS_STATIC_BORROWS = 1 << 4, // Invalid const for miscellaneous reasons (e.g. not implemented). - const NOT_CONST = 0b100000, + const NOT_CONST = 1 << 5, // Borrowing the expression won't produce &'static T if any of these // bits are set, though the value could be copied from static memory // if `NOT_CONST` isn't set. - const NON_STATIC_BORROWS = MUTABLE_MEM.bits | NEEDS_DROP.bits | NOT_CONST.bits + const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits | + ConstQualif::NEEDS_DROP.bits | + ConstQualif::NOT_CONST.bits } } @@ -102,7 +104,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { { let (old_mode, old_qualif) = (self.mode, self.qualif); self.mode = mode; - self.qualif = PURE_CONST; + self.qualif = ConstQualif::PURE_CONST; let r = f(self); self.mode = old_mode; self.qualif = old_qualif; @@ -126,7 +128,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { Entry::Occupied(entry) => return *entry.get(), Entry::Vacant(entry) => { // Prevent infinite recursion on re-entry. - entry.insert(PURE_CONST); + entry.insert(ConstQualif::PURE_CONST); } } self.with_mode(mode, |this| { @@ -271,7 +273,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { fn visit_expr(&mut self, ex: &ast::Expr) { let mut outer = self.qualif; - self.qualif = PURE_CONST; + self.qualif = ConstQualif::PURE_CONST; let node_ty = ty::node_id_to_type(self.tcx, ex.id); check_expr(self, ex, node_ty); @@ -287,7 +289,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { self.visit_expr(&**callee); // The callee's size doesn't count in the call. let added = self.qualif - inner; - self.qualif = inner | (added - NON_ZERO_SIZED); + self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED); } ast::ExprRepeat(ref element, _) => { self.visit_expr(&**element); @@ -298,7 +300,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { }; // [element; 0] is always zero-sized. if count == 0 { - self.qualif = self.qualif - (NON_ZERO_SIZED | PREFER_IN_PLACE); + self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE); } } ast::ExprMatch(ref discr, ref arms, _) => { @@ -325,7 +327,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { let div_or_rem = op.node == ast::BiDiv || op.node == ast::BiRem; match node_ty.sty { ty::ty_uint(_) | ty::ty_int(_) if div_or_rem => { - if !self.qualif.intersects(NOT_CONST) { + if !self.qualif.intersects(ConstQualif::NOT_CONST) { match const_eval::eval_const_expr_partial(self.tcx, ex, None) { Ok(_) => {} Err(msg) => { @@ -348,11 +350,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { // Constants cannot be borrowed if they contain interior mutability as // it means that our "silent insertion of statics" could change // initializer values (very bad). - // If the type doesn't have interior mutability, then `MUTABLE_MEM` has + // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has // propagated from another error, so erroring again would be just noise. let tc = ty::type_contents(self.tcx, node_ty); - if self.qualif.intersects(MUTABLE_MEM) && tc.interior_unsafe() { - outer = outer | NOT_CONST; + if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() { + outer = outer | ConstQualif::NOT_CONST; if self.mode != Mode::Var { self.tcx.sess.span_err(ex.span, "cannot borrow a constant which contains \ @@ -361,32 +363,32 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { } // If the reference has to be 'static, avoid in-place initialization // as that will end up pointing to the stack instead. - if !self.qualif.intersects(NON_STATIC_BORROWS) { - self.qualif = self.qualif - PREFER_IN_PLACE; - self.add_qualif(HAS_STATIC_BORROWS); + if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { + self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE; + self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); } } Some(ast::MutMutable) => { // `&mut expr` means expr could be mutated, unless it's zero-sized. - if self.qualif.intersects(NON_ZERO_SIZED) { + if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) { if self.mode == Mode::Var { - outer = outer | NOT_CONST; - self.add_qualif(MUTABLE_MEM); + outer = outer | ConstQualif::NOT_CONST; + self.add_qualif(ConstQualif::MUTABLE_MEM); } else { span_err!(self.tcx.sess, ex.span, E0017, "references in {}s may only refer \ to immutable values", self.msg()) } } - if !self.qualif.intersects(NON_STATIC_BORROWS) { - self.add_qualif(HAS_STATIC_BORROWS); + if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) { + self.add_qualif(ConstQualif::HAS_STATIC_BORROWS); } } None => {} } self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif); // Don't propagate certain flags. - self.qualif = outer | (self.qualif - HAS_STATIC_BORROWS); + self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS); } } @@ -401,7 +403,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, match node_ty.sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) if ty::has_dtor(v.tcx, did) => { - v.add_qualif(NEEDS_DROP); + v.add_qualif(ConstQualif::NEEDS_DROP); if v.mode != Mode::Var { v.tcx.sess.span_err(e.span, &format!("{}s are not allowed to have destructors", @@ -416,7 +418,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ast::ExprUnary(..) | ast::ExprBinary(..) | ast::ExprIndex(..) if v.tcx.method_map.borrow().contains_key(&method_call) => { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0011, "user-defined operators are not allowed in {}s", v.msg()); @@ -424,7 +426,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } ast::ExprBox(..) | ast::ExprUnary(ast::UnUniq, _) => { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0010, "allocations are not allowed in {}s", v.msg()); @@ -434,7 +436,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, match ty::node_id_to_type(v.tcx, ptr.id).sty { ty::ty_ptr(_) => { // This shouldn't be allowed in constants at all. - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); } _ => {} } @@ -447,7 +449,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ty::type_is_unsafe_ptr(toty) || (ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty)); if !is_legal_cast { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0012, "can not cast to `{}` in {}s", @@ -455,7 +457,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } } if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0018, "can not cast a pointer to an integer in {}s", v.msg()); @@ -467,17 +469,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, match def { Some(def::DefVariant(_, _, _)) => { // Count the discriminator or function pointer. - v.add_qualif(NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NON_ZERO_SIZED); } Some(def::DefStruct(_)) => { if let ty::ty_bare_fn(..) = node_ty.sty { // Count the function pointer. - v.add_qualif(NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NON_ZERO_SIZED); } } Some(def::DefFn(..)) | Some(def::DefMethod(..)) => { // Count the function pointer. - v.add_qualif(NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NON_ZERO_SIZED); } Some(def::DefStatic(..)) => { match v.mode { @@ -487,7 +489,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, "constants cannot refer to other statics, \ insert an intermediate constant instead"); } - Mode::Var => v.add_qualif(NOT_CONST) + Mode::Var => v.add_qualif(ConstQualif::NOT_CONST) } } Some(def::DefConst(did)) | @@ -503,7 +505,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } } def => { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { debug!("(checking const) found bad def: {:?}", def); span_err!(v.tcx.sess, e.span, E0014, @@ -530,10 +532,10 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, Some(def::DefStruct(..)) => {} Some(def::DefVariant(..)) => { // Count the discriminator. - v.add_qualif(NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NON_ZERO_SIZED); } _ => { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0015, "function calls in {}s are limited to \ @@ -545,7 +547,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ast::ExprBlock(ref block) => { // Check all statements in the block let mut block_span_err = |span| { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, span, E0016, "blocks in {}s are limited to items and \ @@ -574,17 +576,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ast::ExprStruct(..) => { let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id()); if did == v.tcx.lang_items.unsafe_cell_type() { - v.add_qualif(MUTABLE_MEM); + v.add_qualif(ConstQualif::MUTABLE_MEM); } } ast::ExprLit(_) | ast::ExprAddrOf(..) => { - v.add_qualif(NON_ZERO_SIZED); + v.add_qualif(ConstQualif::NON_ZERO_SIZED); } ast::ExprRepeat(..) => { - v.add_qualif(PREFER_IN_PLACE); + v.add_qualif(ConstQualif::PREFER_IN_PLACE); } ast::ExprClosure(..) => { @@ -593,7 +595,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, if ty::with_freevars(v.tcx, e.id, |fv| !fv.is_empty()) { assert!(v.mode == Mode::Var, "global closures can't capture anything"); - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); } } @@ -631,7 +633,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ast::ExprAssignOp(..) | ast::ExprInlineAsm(_) | ast::ExprMac(_) => { - v.add_qualif(NOT_CONST); + v.add_qualif(ConstQualif::NOT_CONST); if v.mode != Mode::Var { span_err!(v.tcx.sess, e.span, E0019, "{} contains unimplemented expression type", v.msg()); @@ -644,7 +646,7 @@ pub fn check_crate(tcx: &ty::ctxt) { visit::walk_crate(&mut CheckCrateVisitor { tcx: tcx, mode: Mode::Var, - qualif: NOT_CONST, + qualif: ConstQualif::NOT_CONST, rvalue_borrows: NodeMap() }, tcx.map.krate()); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 587194bafada4..c4ab89e3b4da0 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -838,20 +838,20 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { expr_ty: Ty<'tcx>) -> cmt<'tcx> { let qualif = self.tcx().const_qualif_map.borrow().get(&id).cloned() - .unwrap_or(check_const::NOT_CONST); + .unwrap_or(check_const::ConstQualif::NOT_CONST); // Only promote `[T; 0]` before an RFC for rvalue promotions // is accepted. let qualif = match expr_ty.sty { ty::ty_vec(_, Some(0)) => qualif, - _ => check_const::NOT_CONST + _ => check_const::ConstQualif::NOT_CONST }; // Compute maximum lifetime of this rvalue. This is 'static if // we can promote to a constant, otherwise equal to enclosing temp // lifetime. - let re = match qualif & check_const::NON_STATIC_BORROWS { - check_const::PURE_CONST => ty::ReStatic, + let re = match qualif & check_const::ConstQualif::NON_STATIC_BORROWS { + check_const::ConstQualif::PURE_CONST => ty::ReStatic, _ => self.temporary_scope(id), }; let ret = self.cat_rvalue(id, span, re, expr_ty); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index f172815a4d143..33ba21bc7b154 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -848,16 +848,18 @@ impl<'tcx> ctxt<'tcx> { // recursing over the type itself. bitflags! { flags TypeFlags: u32 { - const NO_TYPE_FLAGS = 0b0, - const HAS_PARAMS = 0b1, - const HAS_SELF = 0b10, - const HAS_TY_INFER = 0b100, - const HAS_RE_INFER = 0b1000, - const HAS_RE_LATE_BOUND = 0b10000, - const HAS_REGIONS = 0b100000, - const HAS_TY_ERR = 0b1000000, - const HAS_PROJECTION = 0b10000000, - const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits, + const NO_TYPE_FLAGS = 0, + const HAS_PARAMS = 1 << 0, + const HAS_SELF = 1 << 1, + const HAS_TY_INFER = 1 << 2, + const HAS_RE_INFER = 1 << 3, + const HAS_RE_LATE_BOUND = 1 << 4, + const HAS_REGIONS = 1 << 5, + const HAS_TY_ERR = 1 << 6, + const HAS_PROJECTION = 1 << 7, + const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | + TypeFlags::HAS_SELF.bits | + TypeFlags::HAS_REGIONS.bits, } } @@ -890,8 +892,8 @@ macro_rules! sty_debug_print { ty::ty_err => /* unimportant */ continue, $(ty::$variant(..) => &mut $variant,)* }; - let region = t.flags.intersects(ty::HAS_RE_INFER); - let ty = t.flags.intersects(ty::HAS_TY_INFER); + let region = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER); + let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER); variant.total += 1; total.total += 1; @@ -993,23 +995,23 @@ impl<'tcx> Borrow> for InternedTy<'tcx> { } pub fn type_has_params(ty: Ty) -> bool { - ty.flags.intersects(HAS_PARAMS) + ty.flags.intersects(TypeFlags::HAS_PARAMS) } pub fn type_has_self(ty: Ty) -> bool { - ty.flags.intersects(HAS_SELF) + ty.flags.intersects(TypeFlags::HAS_SELF) } pub fn type_has_ty_infer(ty: Ty) -> bool { - ty.flags.intersects(HAS_TY_INFER) + ty.flags.intersects(TypeFlags::HAS_TY_INFER) } pub fn type_needs_infer(ty: Ty) -> bool { - ty.flags.intersects(HAS_TY_INFER | HAS_RE_INFER) + ty.flags.intersects(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_RE_INFER) } pub fn type_has_projection(ty: Ty) -> bool { - ty.flags.intersects(HAS_PROJECTION) + ty.flags.intersects(TypeFlags::HAS_PROJECTION) } pub fn type_has_late_bound_regions(ty: Ty) -> bool { - ty.flags.intersects(HAS_RE_LATE_BOUND) + ty.flags.intersects(TypeFlags::HAS_RE_LATE_BOUND) } /// An "escaping region" is a bound region whose binder is not part of `t`. @@ -2810,7 +2812,7 @@ struct FlagComputation { impl FlagComputation { fn new() -> FlagComputation { - FlagComputation { flags: NO_TYPE_FLAGS, depth: 0 } + FlagComputation { flags: TypeFlags::NO_TYPE_FLAGS, depth: 0 } } fn for_sty(st: &sty) -> FlagComputation { @@ -2855,20 +2857,20 @@ impl FlagComputation { // You might think that we could just return ty_err for // any type containing ty_err as a component, and get - // rid of the HAS_TY_ERR flag -- likewise for ty_bot (with + // rid of the TypeFlags::HAS_TY_ERR flag -- likewise for ty_bot (with // the exception of function types that return bot). // But doing so caused sporadic memory corruption, and // neither I (tjc) nor nmatsakis could figure out why, // so we're doing it this way. &ty_err => { - self.add_flags(HAS_TY_ERR) + self.add_flags(TypeFlags::HAS_TY_ERR) } &ty_param(ref p) => { if p.space == subst::SelfSpace { - self.add_flags(HAS_SELF); + self.add_flags(TypeFlags::HAS_SELF); } else { - self.add_flags(HAS_PARAMS); + self.add_flags(TypeFlags::HAS_PARAMS); } } @@ -2877,7 +2879,7 @@ impl FlagComputation { } &ty_infer(_) => { - self.add_flags(HAS_TY_INFER) + self.add_flags(TypeFlags::HAS_TY_INFER) } &ty_enum(_, substs) | &ty_struct(_, substs) => { @@ -2885,7 +2887,7 @@ impl FlagComputation { } &ty_projection(ref data) => { - self.add_flags(HAS_PROJECTION); + self.add_flags(TypeFlags::HAS_PROJECTION); self.add_projection_ty(data); } @@ -2949,11 +2951,11 @@ impl FlagComputation { } fn add_region(&mut self, r: Region) { - self.add_flags(HAS_REGIONS); + self.add_flags(TypeFlags::HAS_REGIONS); match r { - ty::ReInfer(_) => { self.add_flags(HAS_RE_INFER); } + ty::ReInfer(_) => { self.add_flags(TypeFlags::HAS_RE_INFER); } ty::ReLateBound(debruijn, _) => { - self.add_flags(HAS_RE_LATE_BOUND); + self.add_flags(TypeFlags::HAS_RE_LATE_BOUND); self.add_depth(debruijn.depth); } _ => { } @@ -3307,11 +3309,11 @@ pub fn type_is_nil(ty: Ty) -> bool { } pub fn type_is_error(ty: Ty) -> bool { - ty.flags.intersects(HAS_TY_ERR) + ty.flags.intersects(TypeFlags::HAS_TY_ERR) } pub fn type_needs_subst(ty: Ty) -> bool { - ty.flags.intersects(NEEDS_SUBST) + ty.flags.intersects(TypeFlags::NEEDS_SUBST) } pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool { diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 072761f2754b3..b999929c4af9e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -755,11 +755,14 @@ mod opt { pub fn multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) } pub fn flag(a: S, b: S, c: S) -> R { stable(getopts::optflag(a, b, c)) } pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) } + pub fn flagmulti(a: S, b: S, c: S) -> R { stable(getopts::optflagmulti(a, b, c)) } + pub fn opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) } pub fn multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) } pub fn flag_u(a: S, b: S, c: S) -> R { unstable(getopts::optflag(a, b, c)) } pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) } + pub fn flagmulti_u(a: S, b: S, c: S) -> R { unstable(getopts::optflagmulti(a, b, c)) } } /// Returns the "short" subset of the rustc command line options, @@ -786,8 +789,8 @@ pub fn rustc_short_optgroups() -> Vec { opt::multi("", "print", "Comma separated list of compiler information to \ print on stdout", "[crate-name|file-names|sysroot]"), - opt::flag("g", "", "Equivalent to -C debuginfo=2"), - opt::flag("O", "", "Equivalent to -C opt-level=2"), + opt::flagmulti("g", "", "Equivalent to -C debuginfo=2"), + opt::flagmulti("O", "", "Equivalent to -C opt-level=2"), opt::opt("o", "", "Write output to ", "FILENAME"), opt::opt("", "out-dir", "Write output to compiler-chosen filename \ in ", "DIR"), diff --git a/src/librustc_back/fs.rs b/src/librustc_back/fs.rs deleted file mode 100644 index 37e3efb4d83fe..0000000000000 --- a/src/librustc_back/fs.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::io; -use std::path::{Path, PathBuf}; - -#[cfg(windows)] -pub fn realpath(original: &Path) -> io::Result { - Ok(original.to_path_buf()) -} - -#[cfg(unix)] -pub fn realpath(original: &Path) -> io::Result { - use libc; - use std::ffi::{OsString, CString}; - use std::os::unix::prelude::*; - - extern { - fn realpath(pathname: *const libc::c_char, resolved: *mut libc::c_char) - -> *mut libc::c_char; - } - - let path = try!(CString::new(original.as_os_str().as_bytes())); - let mut buf = vec![0u8; 16 * 1024]; - unsafe { - let r = realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); - if r.is_null() { - return Err(io::Error::last_os_error()) - } - } - let p = buf.iter().position(|i| *i == 0).unwrap(); - buf.truncate(p); - Ok(PathBuf::from(OsString::from_vec(buf))) -} - -#[cfg(all(not(windows), test))] -mod tests { - use tempdir::TempDir; - use std::fs::{self, File}; - use super::realpath; - - #[test] - fn realpath_works() { - let tmpdir = TempDir::new("rustc-fs").unwrap(); - let tmpdir = realpath(tmpdir.path()).unwrap(); - let file = tmpdir.join("test"); - let dir = tmpdir.join("test2"); - let link = dir.join("link"); - let linkdir = tmpdir.join("test3"); - - File::create(&file).unwrap(); - fs::create_dir(&dir).unwrap(); - fs::soft_link(&file, &link).unwrap(); - fs::soft_link(&dir, &linkdir).unwrap(); - - assert_eq!(realpath(&tmpdir).unwrap(), tmpdir); - assert_eq!(realpath(&file).unwrap(), file); - assert_eq!(realpath(&link).unwrap(), file); - assert_eq!(realpath(&linkdir).unwrap(), dir); - assert_eq!(realpath(&linkdir.join("link")).unwrap(), file); - } - - #[test] - fn realpath_works_tricky() { - let tmpdir = TempDir::new("rustc-fs").unwrap(); - let tmpdir = realpath(tmpdir.path()).unwrap(); - - let a = tmpdir.join("a"); - let b = a.join("b"); - let c = b.join("c"); - let d = a.join("d"); - let e = d.join("e"); - let f = a.join("f"); - - fs::create_dir_all(&b).unwrap(); - fs::create_dir_all(&d).unwrap(); - File::create(&f).unwrap(); - fs::soft_link("../d/e", &c).unwrap(); - fs::soft_link("../f", &e).unwrap(); - - assert_eq!(realpath(&c).unwrap(), f); - assert_eq!(realpath(&e).unwrap(), f); - } -} diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 22dea4757ed66..3e55f7f8045b5 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -41,6 +41,7 @@ #![feature(path_ext)] #![feature(step_by)] #![feature(libc)] +#![feature(fs_canonicalize)] #![cfg_attr(test, feature(test, rand))] extern crate syntax; @@ -53,7 +54,6 @@ pub mod abi; pub mod archive; pub mod tempdir; pub mod arm; -pub mod fs; pub mod mips; pub mod mipsel; pub mod rpath; diff --git a/src/librustc_back/rpath.rs b/src/librustc_back/rpath.rs index 1daeb1cb223e2..6674d3135a0bd 100644 --- a/src/librustc_back/rpath.rs +++ b/src/librustc_back/rpath.rs @@ -10,8 +10,8 @@ use std::collections::HashSet; use std::env; -use std::io; use std::path::{Path, PathBuf}; +use std::fs; use syntax::ast; pub struct RPathConfig<'a> { @@ -20,7 +20,6 @@ pub struct RPathConfig<'a> { pub is_like_osx: bool, pub has_rpath: bool, pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf, - pub realpath: &'a mut FnMut(&Path) -> io::Result, } pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { @@ -95,11 +94,11 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String }; let cwd = env::current_dir().unwrap(); - let mut lib = (config.realpath)(&cwd.join(lib)).unwrap(); + let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); lib.pop(); let mut output = cwd.join(&config.out_filename); output.pop(); - let output = (config.realpath)(&output).unwrap(); + let output = fs::canonicalize(&output).unwrap_or(output); let relative = path_relative_from(&lib, &output) .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib)); // FIXME (#9639): This needs to handle non-utf8 paths @@ -231,7 +230,6 @@ mod tests { is_like_osx: true, out_filename: PathBuf::from("bin/rustc"), get_install_prefix_lib_path: &mut || panic!(), - realpath: &mut |p| Ok(p.to_path_buf()), }; let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); @@ -243,7 +241,6 @@ mod tests { get_install_prefix_lib_path: &mut || panic!(), has_rpath: true, is_like_osx: false, - realpath: &mut |p| Ok(p.to_path_buf()), }; let res = get_rpath_relative_to_output(config, Path::new("lib/libstd.so")); diff --git a/src/librustc_back/target/aarch64_linux_android.rs b/src/librustc_back/target/aarch64_linux_android.rs index fcc901fc81f57..67194e7ac5c7c 100644 --- a/src/librustc_back/target/aarch64_linux_android.rs +++ b/src/librustc_back/target/aarch64_linux_android.rs @@ -11,10 +11,6 @@ use target::Target; pub fn target() -> Target { - let mut base = super::linux_base::opts(); - base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string()); - base.is_like_android = true; - base.position_independent_executables = true; Target { data_layout: "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\ @@ -25,6 +21,6 @@ pub fn target() -> Target { arch: "aarch64".to_string(), target_os: "android".to_string(), target_env: "".to_string(), - options: base, + options: super::android_base::opts(), } } diff --git a/src/librustc_back/target/android_base.rs b/src/librustc_back/target/android_base.rs new file mode 100644 index 0000000000000..2883ffd6e9f22 --- /dev/null +++ b/src/librustc_back/target/android_base.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::TargetOptions; + +pub fn opts() -> TargetOptions { + let mut base = super::linux_base::opts(); + // Many of the symbols defined in compiler-rt are also defined in libgcc. + // Android's linker doesn't like that by default. + base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string()); + base.is_like_android = true; + base.position_independent_executables = true; + base +} diff --git a/src/librustc_back/target/arm_linux_androideabi.rs b/src/librustc_back/target/arm_linux_androideabi.rs index 57712d18cbae2..cbaa2b205b055 100644 --- a/src/librustc_back/target/arm_linux_androideabi.rs +++ b/src/librustc_back/target/arm_linux_androideabi.rs @@ -11,21 +11,13 @@ use target::Target; pub fn target() -> Target { - let mut base = super::linux_base::opts(); + let mut base = super::android_base::opts(); base.features = "+v7".to_string(); - // Many of the symbols defined in compiler-rt are also defined in libgcc. Android - // linker doesn't like that by default. - base.pre_link_args.push("-Wl,--allow-multiple-definition".to_string()); - base.is_like_android = true; - // FIXME #17437 (and #17448): Android doesn't support position dependent executables anymore. - base.position_independent_executables = false; Target { - data_layout: "e-p:32:32:32\ - -i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64\ - -f32:32:32-f64:64:64\ - -v64:64:64-v128:64:128\ - -a:0:64-n32".to_string(), + data_layout: "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ + f32:32:32-f64:64:64-v64:64:64-v128:64:128-a:0:64-\ + n32".to_string(), llvm_target: "arm-linux-androideabi".to_string(), target_endian: "little".to_string(), target_pointer_width: "32".to_string(), diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 3a79ae3e7c01b..c5f1882fa1dd7 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -46,18 +46,19 @@ //! specified by the target, rather than replace. use serialize::json::Json; -use syntax::{diagnostic, abi}; use std::default::Default; use std::io::prelude::*; +use syntax::{diagnostic, abi}; -mod windows_base; -mod linux_base; +mod android_base; mod apple_base; mod apple_ios_base; -mod freebsd_base; -mod dragonfly_base; mod bitrig_base; +mod dragonfly_base; +mod freebsd_base; +mod linux_base; mod openbsd_base; +mod windows_base; /// Everything `rustc` knows about how to compile for a specific target. /// diff --git a/src/librustc_bitflags/lib.rs b/src/librustc_bitflags/lib.rs index 93a2a5d125778..6d23cad26cb37 100644 --- a/src/librustc_bitflags/lib.rs +++ b/src/librustc_bitflags/lib.rs @@ -12,6 +12,7 @@ // Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364) #![cfg_attr(stage0, feature(custom_attribute))] #![crate_name = "rustc_bitflags"] +#![feature(associated_consts)] #![feature(staged_api)] #![staged_api] #![crate_type = "rlib"] @@ -34,6 +35,7 @@ /// /// ```{.rust} /// # #![feature(rustc_private)] +/// # #![feature(associated_consts)] /// #[macro_use] extern crate rustc_bitflags; /// /// bitflags! { @@ -41,19 +43,19 @@ /// const FLAG_A = 0b00000001, /// const FLAG_B = 0b00000010, /// const FLAG_C = 0b00000100, -/// const FLAG_ABC = FLAG_A.bits -/// | FLAG_B.bits -/// | FLAG_C.bits, +/// const FLAG_ABC = Flags::FLAG_A.bits +/// | Flags::FLAG_B.bits +/// | Flags::FLAG_C.bits, /// } /// } /// /// fn main() { -/// let e1 = FLAG_A | FLAG_C; -/// let e2 = FLAG_B | FLAG_C; -/// assert!((e1 | e2) == FLAG_ABC); // union -/// assert!((e1 & e2) == FLAG_C); // intersection -/// assert!((e1 - e2) == FLAG_A); // set difference -/// assert!(!e2 == FLAG_A); // set complement +/// let e1 = Flags::FLAG_A | Flags::FLAG_C; +/// let e2 = Flags::FLAG_B | Flags::FLAG_C; +/// assert!((e1 | e2) == Flags::FLAG_ABC); // union +/// assert!((e1 & e2) == Flags::FLAG_C); // intersection +/// assert!((e1 - e2) == Flags::FLAG_A); // set difference +/// assert!(!e2 == Flags::FLAG_A); // set complement /// } /// ``` /// @@ -86,7 +88,7 @@ /// } /// /// fn main() { -/// let mut flags = FLAG_A | FLAG_B; +/// let mut flags = Flags::FLAG_A | Flags::FLAG_B; /// flags.clear(); /// assert!(flags.is_empty()); /// assert_eq!(format!("{:?}", flags), "hi!"); @@ -144,9 +146,9 @@ macro_rules! bitflags { bits: $T, } - $($(#[$Flag_attr])* pub const $Flag: $BitFlags = $BitFlags { bits: $value };)+ - impl $BitFlags { + $($(#[$Flag_attr])* pub const $Flag: $BitFlags = $BitFlags { bits: $value };)+ + /// Returns an empty set of flags. #[inline] pub fn empty() -> $BitFlags { @@ -314,9 +316,9 @@ mod tests { #[doc = "* cmr bed"] #[doc = "* strcat table"] #[doc = " wait what?"] - const FlagABC = FlagA.bits - | FlagB.bits - | FlagC.bits, + const FlagABC = Flags::FlagA.bits + | Flags::FlagB.bits + | Flags::FlagC.bits, } } @@ -329,32 +331,32 @@ mod tests { #[test] fn test_bits(){ assert_eq!(Flags::empty().bits(), 0b00000000); - assert_eq!(FlagA.bits(), 0b00000001); - assert_eq!(FlagABC.bits(), 0b00000111); + assert_eq!(Flags::FlagA.bits(), 0b00000001); + assert_eq!(Flags::FlagABC.bits(), 0b00000111); assert_eq!(AnotherSetOfFlags::empty().bits(), 0b00); - assert_eq!(AnotherFlag.bits(), !0); + assert_eq!(AnotherSetOfFlags::AnotherFlag.bits(), !0); } #[test] fn test_from_bits() { assert!(Flags::from_bits(0) == Some(Flags::empty())); - assert!(Flags::from_bits(0b1) == Some(FlagA)); - assert!(Flags::from_bits(0b10) == Some(FlagB)); - assert!(Flags::from_bits(0b11) == Some(FlagA | FlagB)); + assert!(Flags::from_bits(0b1) == Some(Flags::FlagA)); + assert!(Flags::from_bits(0b10) == Some(Flags::FlagB)); + assert!(Flags::from_bits(0b11) == Some(Flags::FlagA | Flags::FlagB)); assert!(Flags::from_bits(0b1000) == None); - assert!(AnotherSetOfFlags::from_bits(!0) == Some(AnotherFlag)); + assert!(AnotherSetOfFlags::from_bits(!0) == Some(AnotherSetOfFlags::AnotherFlag)); } #[test] fn test_from_bits_truncate() { assert!(Flags::from_bits_truncate(0) == Flags::empty()); - assert!(Flags::from_bits_truncate(0b1) == FlagA); - assert!(Flags::from_bits_truncate(0b10) == FlagB); - assert!(Flags::from_bits_truncate(0b11) == (FlagA | FlagB)); + assert!(Flags::from_bits_truncate(0b1) == Flags::FlagA); + assert!(Flags::from_bits_truncate(0b10) == Flags::FlagB); + assert!(Flags::from_bits_truncate(0b11) == (Flags::FlagA | Flags::FlagB)); assert!(Flags::from_bits_truncate(0b1000) == Flags::empty()); - assert!(Flags::from_bits_truncate(0b1001) == FlagA); + assert!(Flags::from_bits_truncate(0b1001) == Flags::FlagA); assert!(AnotherSetOfFlags::from_bits_truncate(0) == AnotherSetOfFlags::empty()); } @@ -362,19 +364,19 @@ mod tests { #[test] fn test_is_empty(){ assert!(Flags::empty().is_empty()); - assert!(!FlagA.is_empty()); - assert!(!FlagABC.is_empty()); + assert!(!Flags::FlagA.is_empty()); + assert!(!Flags::FlagABC.is_empty()); - assert!(!AnotherFlag.is_empty()); + assert!(!AnotherSetOfFlags::AnotherFlag.is_empty()); } #[test] fn test_is_all() { assert!(Flags::all().is_all()); - assert!(!FlagA.is_all()); - assert!(FlagABC.is_all()); + assert!(!Flags::FlagA.is_all()); + assert!(Flags::FlagABC.is_all()); - assert!(AnotherFlag.is_all()); + assert!(AnotherSetOfFlags::AnotherFlag.is_all()); } #[test] @@ -383,77 +385,77 @@ mod tests { let e2 = Flags::empty(); assert!(!e1.intersects(e2)); - assert!(AnotherFlag.intersects(AnotherFlag)); + assert!(AnotherSetOfFlags::AnotherFlag.intersects(AnotherSetOfFlags::AnotherFlag)); } #[test] fn test_empty_does_not_intersect_with_full() { let e1 = Flags::empty(); - let e2 = FlagABC; + let e2 = Flags::FlagABC; assert!(!e1.intersects(e2)); } #[test] fn test_disjoint_intersects() { - let e1 = FlagA; - let e2 = FlagB; + let e1 = Flags::FlagA; + let e2 = Flags::FlagB; assert!(!e1.intersects(e2)); } #[test] fn test_overlapping_intersects() { - let e1 = FlagA; - let e2 = FlagA | FlagB; + let e1 = Flags::FlagA; + let e2 = Flags::FlagA | Flags::FlagB; assert!(e1.intersects(e2)); } #[test] fn test_contains() { - let e1 = FlagA; - let e2 = FlagA | FlagB; + let e1 = Flags::FlagA; + let e2 = Flags::FlagA | Flags::FlagB; assert!(!e1.contains(e2)); assert!(e2.contains(e1)); - assert!(FlagABC.contains(e2)); + assert!(Flags::FlagABC.contains(e2)); - assert!(AnotherFlag.contains(AnotherFlag)); + assert!(AnotherSetOfFlags::AnotherFlag.contains(AnotherSetOfFlags::AnotherFlag)); } #[test] fn test_insert(){ - let mut e1 = FlagA; - let e2 = FlagA | FlagB; + let mut e1 = Flags::FlagA; + let e2 = Flags::FlagA | Flags::FlagB; e1.insert(e2); assert!(e1 == e2); let mut e3 = AnotherSetOfFlags::empty(); - e3.insert(AnotherFlag); - assert!(e3 == AnotherFlag); + e3.insert(AnotherSetOfFlags::AnotherFlag); + assert!(e3 == AnotherSetOfFlags::AnotherFlag); } #[test] fn test_remove(){ - let mut e1 = FlagA | FlagB; - let e2 = FlagA | FlagC; + let mut e1 = Flags::FlagA | Flags::FlagB; + let e2 = Flags::FlagA | Flags::FlagC; e1.remove(e2); - assert!(e1 == FlagB); + assert!(e1 == Flags::FlagB); - let mut e3 = AnotherFlag; - e3.remove(AnotherFlag); + let mut e3 = AnotherSetOfFlags::AnotherFlag; + e3.remove(AnotherSetOfFlags::AnotherFlag); assert!(e3 == AnotherSetOfFlags::empty()); } #[test] fn test_operators() { - let e1 = FlagA | FlagC; - let e2 = FlagB | FlagC; - assert!((e1 | e2) == FlagABC); // union - assert!((e1 & e2) == FlagC); // intersection - assert!((e1 - e2) == FlagA); // set difference - assert!(!e2 == FlagA); // set complement - assert!(e1 ^ e2 == FlagA | FlagB); // toggle + let e1 = Flags::FlagA | Flags::FlagC; + let e2 = Flags::FlagB | Flags::FlagC; + assert!((e1 | e2) == Flags::FlagABC); // union + assert!((e1 & e2) == Flags::FlagC); // intersection + assert!((e1 - e2) == Flags::FlagA); // set difference + assert!(!e2 == Flags::FlagA); // set complement + assert!(e1 ^ e2 == Flags::FlagA | Flags::FlagB); // toggle let mut e3 = e1; e3.toggle(e2); - assert!(e3 == FlagA | FlagB); + assert!(e3 == Flags::FlagA | Flags::FlagB); let mut m4 = AnotherSetOfFlags::empty(); m4.toggle(AnotherSetOfFlags::empty()); @@ -466,11 +468,11 @@ mod tests { let mut b = Flags::empty(); assert!(!(a < b) && !(b < a)); - b = FlagB; + b = Flags::FlagB; assert!(a < b); - a = FlagC; + a = Flags::FlagC; assert!(!(a < b) && b < a); - b = FlagC | FlagB; + b = Flags::FlagC | Flags::FlagB; assert!(a < b); } @@ -480,10 +482,10 @@ mod tests { let mut b = Flags::empty(); assert!(a <= b && a >= b); - a = FlagA; + a = Flags::FlagA; assert!(a > b && a >= b); assert!(b < a && b <= a); - b = FlagB; + b = Flags::FlagB; assert!(b > a && b >= a); assert!(a < b && a <= b); } @@ -494,7 +496,7 @@ mod tests { let mut y = Flags::empty(); assert!(hash::hash::(&x) == hash::hash::(&y)); x = Flags::all(); - y = FlagABC; + y = Flags::FlagABC; assert!(hash::hash::(&x) == hash::hash::(&y)); } } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 1d5c5fb86cbd5..cc2c9b735ea44 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -2142,3 +2142,57 @@ impl LintPass for UnstableFeatures { } } } + +/// Lints for attempts to impl Drop on types that have `#[repr(C)]` +/// attribute (see issue #24585). +#[derive(Copy, Clone)] +pub struct DropWithReprExtern; + +declare_lint! { + DROP_WITH_REPR_EXTERN, + Warn, + "use of #[repr(C)] on a type that implements Drop" +} + +impl LintPass for DropWithReprExtern { + fn get_lints(&self) -> LintArray { + lint_array!(DROP_WITH_REPR_EXTERN) + } + fn check_crate(&mut self, ctx: &Context, _: &ast::Crate) { + for dtor_did in ctx.tcx.destructors.borrow().iter() { + let (drop_impl_did, dtor_self_type) = + if dtor_did.krate == ast::LOCAL_CRATE { + let impl_did = ctx.tcx.map.get_parent_did(dtor_did.node); + let ty = ty::lookup_item_type(ctx.tcx, impl_did).ty; + (impl_did, ty) + } else { + continue; + }; + + match dtor_self_type.sty { + ty::ty_enum(self_type_did, _) | + ty::ty_struct(self_type_did, _) | + ty::ty_closure(self_type_did, _) => { + let hints = ty::lookup_repr_hints(ctx.tcx, self_type_did); + if hints.iter().any(|attr| *attr == attr::ReprExtern) && + ty::ty_dtor(ctx.tcx, self_type_did).has_drop_flag() { + let drop_impl_span = ctx.tcx.map.def_id_span(drop_impl_did, + codemap::DUMMY_SP); + let self_defn_span = ctx.tcx.map.def_id_span(self_type_did, + codemap::DUMMY_SP); + ctx.span_lint(DROP_WITH_REPR_EXTERN, + drop_impl_span, + "implementing Drop adds hidden state to types, \ + possibly conflicting with `#[repr(C)]`"); + // FIXME #19668: could be span_lint_note instead of manual guard. + if ctx.current_level(DROP_WITH_REPR_EXTERN) != Level::Allow { + ctx.sess().span_note(self_defn_span, + "the `#[repr(C)]` attribute is attached here"); + } + } + } + _ => {} + } + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 544fe7598192c..970f9c634a2ca 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -108,6 +108,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnconditionalRecursion, InvalidNoMangleItems, PluginAsLibrary, + DropWithReprExtern, ); add_builtin_with_new!(sess, diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 22f915af00f5f..db88072150a60 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -25,6 +25,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/nightly/")] +#![feature(associated_consts)] #![feature(box_syntax)] #![feature(collections)] #![feature(libc)] @@ -42,6 +43,7 @@ pub use self::RealPredicate::*; pub use self::TypeKind::*; pub use self::AtomicBinOp::*; pub use self::AtomicOrdering::*; +pub use self::SynchronizationScope::*; pub use self::FileType::*; pub use self::MetadataType::*; pub use self::AsmDialect::*; @@ -123,32 +125,32 @@ pub enum DiagnosticSeverity { bitflags! { flags Attribute : u32 { - const ZExtAttribute = 1 << 0, - const SExtAttribute = 1 << 1, - const NoReturnAttribute = 1 << 2, - const InRegAttribute = 1 << 3, - const StructRetAttribute = 1 << 4, - const NoUnwindAttribute = 1 << 5, - const NoAliasAttribute = 1 << 6, - const ByValAttribute = 1 << 7, - const NestAttribute = 1 << 8, - const ReadNoneAttribute = 1 << 9, - const ReadOnlyAttribute = 1 << 10, - const NoInlineAttribute = 1 << 11, - const AlwaysInlineAttribute = 1 << 12, + const ZExtAttribute = 1 << 0, + const SExtAttribute = 1 << 1, + const NoReturnAttribute = 1 << 2, + const InRegAttribute = 1 << 3, + const StructRetAttribute = 1 << 4, + const NoUnwindAttribute = 1 << 5, + const NoAliasAttribute = 1 << 6, + const ByValAttribute = 1 << 7, + const NestAttribute = 1 << 8, + const ReadNoneAttribute = 1 << 9, + const ReadOnlyAttribute = 1 << 10, + const NoInlineAttribute = 1 << 11, + const AlwaysInlineAttribute = 1 << 12, const OptimizeForSizeAttribute = 1 << 13, - const StackProtectAttribute = 1 << 14, + const StackProtectAttribute = 1 << 14, const StackProtectReqAttribute = 1 << 15, - const AlignmentAttribute = 31 << 16, - const NoCaptureAttribute = 1 << 21, - const NoRedZoneAttribute = 1 << 22, + const AlignmentAttribute = 1 << 16, + const NoCaptureAttribute = 1 << 21, + const NoRedZoneAttribute = 1 << 22, const NoImplicitFloatAttribute = 1 << 23, - const NakedAttribute = 1 << 24, - const InlineHintAttribute = 1 << 25, - const StackAttribute = 7 << 26, - const ReturnsTwiceAttribute = 1 << 29, - const UWTableAttribute = 1 << 30, - const NonLazyBindAttribute = 1 << 31, + const NakedAttribute = 1 << 24, + const InlineHintAttribute = 1 << 25, + const StackAttribute = 7 << 26, + const ReturnsTwiceAttribute = 1 << 29, + const UWTableAttribute = 1 << 30, + const NonLazyBindAttribute = 1 << 31, } } @@ -360,6 +362,13 @@ pub enum AtomicOrdering { SequentiallyConsistent = 7 } +#[repr(C)] +#[derive(Copy, Clone)] +pub enum SynchronizationScope { + SingleThread = 0, + CrossThread = 1 +} + // Consts for the LLVMCodeGenFileType type (in include/llvm/c/TargetMachine.h) #[repr(C)] #[derive(Copy, Clone)] @@ -1533,7 +1542,9 @@ extern { SingleThreaded: Bool) -> ValueRef; - pub fn LLVMBuildAtomicFence(B: BuilderRef, Order: AtomicOrdering); + pub fn LLVMBuildAtomicFence(B: BuilderRef, + Order: AtomicOrdering, + Scope: SynchronizationScope); /* Selected entries from the downcasts. */ diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 0d25700d2b7f1..4ea18968d43ac 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -13,7 +13,7 @@ //! Here we build the "reduced graph": the graph of the module tree without //! any imports resolved. -use {DefModifiers, PUBLIC, IMPORTABLE}; +use DefModifiers; use resolve_imports::ImportDirective; use resolve_imports::ImportDirectiveSubclass::{self, SingleImport, GlobImport}; use resolve_imports::ImportResolution; @@ -262,7 +262,11 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { let name = item.ident.name; let sp = item.span; let is_public = item.vis == ast::Public; - let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; + let modifiers = if is_public { + DefModifiers::PUBLIC + } else { + DefModifiers::empty() + } | DefModifiers::IMPORTABLE; match item.node { ItemUse(ref view_path) => { @@ -533,20 +537,20 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { ast::ConstTraitItem(..) => { let def = DefAssociatedConst(local_def(trait_item.id), FromTrait(local_def(item.id))); - // NB: not IMPORTABLE - name_bindings.define_value(def, trait_item.span, PUBLIC); + // NB: not DefModifiers::IMPORTABLE + name_bindings.define_value(def, trait_item.span, DefModifiers::PUBLIC); } ast::MethodTraitItem(..) => { let def = DefMethod(local_def(trait_item.id), FromTrait(local_def(item.id))); - // NB: not IMPORTABLE - name_bindings.define_value(def, trait_item.span, PUBLIC); + // NB: not DefModifiers::IMPORTABLE + name_bindings.define_value(def, trait_item.span, DefModifiers::PUBLIC); } ast::TypeTraitItem(..) => { let def = DefAssociatedTy(local_def(item.id), local_def(trait_item.id)); - // NB: not IMPORTABLE - name_bindings.define_type(def, trait_item.span, PUBLIC); + // NB: not DefModifiers::IMPORTABLE + name_bindings.define_type(def, trait_item.span, DefModifiers::PUBLIC); } } @@ -584,10 +588,10 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { // used child.define_value(DefVariant(item_id, local_def(variant.node.id), is_exported), - variant.span, PUBLIC | IMPORTABLE); + variant.span, DefModifiers::PUBLIC | DefModifiers::IMPORTABLE); child.define_type(DefVariant(item_id, local_def(variant.node.id), is_exported), - variant.span, PUBLIC | IMPORTABLE); + variant.span, DefModifiers::PUBLIC | DefModifiers::IMPORTABLE); } /// Constructs the reduced graph for one foreign item. @@ -596,7 +600,11 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { parent: &Rc) { let name = foreign_item.ident.name; let is_public = foreign_item.vis == ast::Public; - let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; + let modifiers = if is_public { + DefModifiers::PUBLIC + } else { + DefModifiers::empty() + } | DefModifiers::IMPORTABLE; let name_bindings = self.add_child(name, parent, ForbidDuplicateValues, foreign_item.span); @@ -644,7 +652,11 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { external crate) building external def {}, priv {:?}", final_ident, vis); let is_public = vis == ast::Public; - let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; + let modifiers = if is_public { + DefModifiers::PUBLIC + } else { + DefModifiers::empty() + } | DefModifiers::IMPORTABLE; let is_exported = is_public && match new_parent.def_id.get() { None => true, Some(did) => self.external_exports.contains(&did) @@ -695,7 +707,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { final_ident); // variants are always treated as importable to allow them to be // glob used - let modifiers = PUBLIC | IMPORTABLE; + let modifiers = DefModifiers::PUBLIC | DefModifiers::IMPORTABLE; if is_struct { child_name_bindings.define_type(def, DUMMY_SP, modifiers); // Not adding fields for variants as they are not accessed with a self receiver @@ -715,11 +727,12 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> { crate) building value (fn/static) {}", final_ident); // impl methods have already been defined with the correct importability modifier let mut modifiers = match *child_name_bindings.value_def.borrow() { - Some(ref def) => (modifiers & !IMPORTABLE) | (def.modifiers & IMPORTABLE), + Some(ref def) => (modifiers & !DefModifiers::IMPORTABLE) | + (def.modifiers & DefModifiers::IMPORTABLE), None => modifiers }; if new_parent.kind.get() != NormalModuleKind { - modifiers = modifiers & !IMPORTABLE; + modifiers = modifiers & !DefModifiers::IMPORTABLE; } child_name_bindings.define_value(def, DUMMY_SP, modifiers); } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 61eab4ce9b285..d8495fb989bfe 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -20,6 +20,7 @@ html_root_url = "http://doc.rust-lang.org/nightly/")] #![feature(alloc)] +#![feature(associated_consts)] #![feature(collections)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -477,8 +478,8 @@ impl fmt::Debug for Module { bitflags! { #[derive(Debug)] flags DefModifiers: u8 { - const PUBLIC = 0b0000_0001, - const IMPORTABLE = 0b0000_0010, + const PUBLIC = 1 << 0, + const IMPORTABLE = 1 << 1, } } @@ -524,7 +525,11 @@ impl NameBindings { is_public: bool, sp: Span) { // Merges the module with the existing type def or creates a new one. - let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; + let modifiers = if is_public { + DefModifiers::PUBLIC + } else { + DefModifiers::empty() + } | DefModifiers::IMPORTABLE; let module_ = Rc::new(Module::new(parent_link, def_id, kind, @@ -559,7 +564,11 @@ impl NameBindings { external: bool, is_public: bool, _sp: Span) { - let modifiers = if is_public { PUBLIC } else { DefModifiers::empty() } | IMPORTABLE; + let modifiers = if is_public { + DefModifiers::PUBLIC + } else { + DefModifiers::empty() + } | DefModifiers::IMPORTABLE; let type_def = self.type_def.borrow().clone(); match type_def { None => { @@ -659,7 +668,7 @@ impl NameBindings { } fn defined_in_public_namespace(&self, namespace: Namespace) -> bool { - self.defined_in_namespace_with(namespace, PUBLIC) + self.defined_in_namespace_with(namespace, DefModifiers::PUBLIC) } fn defined_in_namespace_with(&self, namespace: Namespace, modifiers: DefModifiers) -> bool { @@ -730,11 +739,11 @@ impl NameBindings { match namespace { TypeNS => { let type_def = self.type_def.borrow(); - type_def.as_ref().unwrap().modifiers.contains(PUBLIC) + type_def.as_ref().unwrap().modifiers.contains(DefModifiers::PUBLIC) } ValueNS => { let value_def = self.value_def.borrow(); - value_def.as_ref().unwrap().modifiers.contains(PUBLIC) + value_def.as_ref().unwrap().modifiers.contains(DefModifiers::PUBLIC) } } } @@ -921,7 +930,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn create_name_bindings_from_module(module: Rc) -> NameBindings { NameBindings { type_def: RefCell::new(Some(TypeNsDef { - modifiers: IMPORTABLE, + modifiers: DefModifiers::IMPORTABLE, module_def: Some(module), type_def: None, type_span: None diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 4b488981bfbc6..350f69d30c4e4 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -10,7 +10,7 @@ use self::ImportDirectiveSubclass::*; -use {PUBLIC, IMPORTABLE}; +use DefModifiers; use Module; use Namespace::{self, TypeNS, ValueNS}; use NameBindings; @@ -848,7 +848,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { // Merge the child item into the import resolution. { let mut merge_child_item = |namespace| { - if name_bindings.defined_in_namespace_with(namespace, IMPORTABLE | PUBLIC) { + let modifier = DefModifiers::IMPORTABLE | DefModifiers::PUBLIC; + + if name_bindings.defined_in_namespace_with(namespace, modifier) { let namespace_name = match namespace { TypeNS => "type", ValueNS => "value", @@ -914,7 +916,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { import_span: Span, name: Name, namespace: Namespace) { - if !name_bindings.defined_in_namespace_with(namespace, IMPORTABLE) { + if !name_bindings.defined_in_namespace_with(namespace, DefModifiers::IMPORTABLE) { let msg = format!("`{}` is not directly importable", token::get_name(name)); span_err!(self.resolver.session, import_span, E0253, "{}", &msg[..]); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index c72072f069650..92c9549b37727 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -32,7 +32,7 @@ use std::ffi::OsString; use std::fs::{self, PathExt}; use std::io::{self, Read, Write}; use std::mem; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use std::process::Command; use std::str; use flate; @@ -872,7 +872,7 @@ fn link_args(cmd: &mut Command, // target descriptor let t = &sess.target.target; - cmd.arg("-L").arg(&lib_path); + cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(&lib_path)); cmd.arg("-o").arg(out_filename).arg(obj_filename); @@ -924,8 +924,9 @@ fn link_args(cmd: &mut Command, // stripped away as much as it could. This has not been seen to impact // link times negatively. // - // -dead_strip can't be part of the pre_link_args because it's also used for partial - // linking when using multiple codegen units (-r). So we insert it here. + // -dead_strip can't be part of the pre_link_args because it's also used + // for partial linking when using multiple codegen units (-r). So we + // insert it here. cmd.arg("-Wl,-dead_strip"); } @@ -1051,7 +1052,6 @@ fn link_args(cmd: &mut Command, has_rpath: sess.target.target.options.has_rpath, is_like_osx: sess.target.target.options.is_like_osx, get_install_prefix_lib_path: &mut get_install_prefix_lib_path, - realpath: &mut ::util::fs::realpath }; cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } @@ -1266,7 +1266,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, } }); } else { - cmd.arg(cratepath); + cmd.arg(&fix_windows_verbatim_for_gcc(cratepath)); } } @@ -1279,7 +1279,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // Just need to tell the linker about where the library lives and // what its name is if let Some(dir) = cratepath.parent() { - cmd.arg("-L").arg(dir); + cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(dir)); } let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); cmd.arg(&format!("-l{}", unlib(&sess.target, filestem))); @@ -1333,3 +1333,29 @@ fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { } } } + +// Unfortunately, on windows, gcc cannot accept paths of the form `\\?\C:\...` +// (a verbatim path). This form of path is generally pretty rare, but the +// implementation of `fs::canonicalize` currently generates paths of this form, +// meaning that we're going to be passing quite a few of these down to gcc. +// +// For now we just strip the "verbatim prefix" of `\\?\` from the path. This +// will probably lose information in some cases, but there's not a whole lot +// more we can do with a buggy gcc... +fn fix_windows_verbatim_for_gcc(p: &Path) -> PathBuf { + if !cfg!(windows) { + return p.to_path_buf() + } + let mut components = p.components(); + let prefix = match components.next() { + Some(path::Component::Prefix(p)) => p, + _ => return p.to_path_buf(), + }; + let disk = match prefix.kind() { + path::Prefix::VerbatimDisk(disk) => disk, + _ => return p.to_path_buf(), + }; + let mut base = OsString::from(format!("{}:", disk as char)); + base.push(components.as_path()); + PathBuf::from(base) +} diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 39cfac42011ab..d80086da20315 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -295,6 +295,8 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { return; } + debug!("process_method: {}:{}", id, token::get_name(name)); + let mut scope_id; // The qualname for a method is the trait name or name of the struct in an impl in // which the method is declared in, followed by the method's name. @@ -704,7 +706,7 @@ impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { self.process_generic_params(type_parameters, item.span, "", item.id); for impl_item in impl_items { - visit::walk_impl_item(self, impl_item); + self.visit_impl_item(impl_item); } } @@ -1258,7 +1260,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { match impl_item.node { ast::ConstImplItem(ref ty, ref expr) => { self.process_const(impl_item.id, &impl_item.ident, - impl_item.span, &*ty, &*expr); + impl_item.span, &ty, &expr); } ast::MethodImplItem(ref sig, ref body) => { self.process_method(sig, Some(body), impl_item.id, diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index cecf35001c4eb..001de615fb1eb 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -440,6 +440,22 @@ fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>, None }, + // Perhaps one of the upvars of this struct is non-zero + // Let's recurse and find out! + ty::ty_closure(def_id, substs) => { + let typer = NormalizingClosureTyper::new(tcx); + let upvars = typer.closure_upvars(def_id, substs).unwrap(); + let upvar_types = upvars.iter().map(|u| u.ty).collect::>(); + + for (j, &ty) in upvar_types.iter().enumerate() { + if let Some(mut fpath) = find_discr_field_candidate(tcx, ty, path.clone()) { + fpath.push(j); + return Some(fpath); + } + } + None + }, + // Can we use one of the fields in this tuple? ty::ty_tup(ref tys) => { for (j, &ty) in tys.iter().enumerate() { diff --git a/src/librustc_trans/trans/attributes.rs b/src/librustc_trans/trans/attributes.rs index 2615490a9fbc5..b44ccec0127bd 100644 --- a/src/librustc_trans/trans/attributes.rs +++ b/src/librustc_trans/trans/attributes.rs @@ -39,13 +39,13 @@ pub fn split_stack(val: ValueRef, set: bool) { pub fn inline(val: ValueRef, inline: InlineAttr) { use self::InlineAttr::*; match inline { - Hint => llvm::SetFunctionAttribute(val, llvm::InlineHintAttribute), - Always => llvm::SetFunctionAttribute(val, llvm::AlwaysInlineAttribute), - Never => llvm::SetFunctionAttribute(val, llvm::NoInlineAttribute), + Hint => llvm::SetFunctionAttribute(val, llvm::Attribute::InlineHintAttribute), + Always => llvm::SetFunctionAttribute(val, llvm::Attribute::AlwaysInlineAttribute), + Never => llvm::SetFunctionAttribute(val, llvm::Attribute::NoInlineAttribute), None => { - let attr = llvm::InlineHintAttribute | - llvm::AlwaysInlineAttribute | - llvm::NoInlineAttribute; + let attr = llvm::Attribute::InlineHintAttribute | + llvm::Attribute::AlwaysInlineAttribute | + llvm::Attribute::NoInlineAttribute; unsafe { llvm::LLVMRemoveFunctionAttr(val, attr.bits() as c_ulonglong) } @@ -57,10 +57,13 @@ pub fn inline(val: ValueRef, inline: InlineAttr) { #[inline] pub fn emit_uwtable(val: ValueRef, emit: bool) { if emit { - llvm::SetFunctionAttribute(val, llvm::UWTableAttribute); + llvm::SetFunctionAttribute(val, llvm::Attribute::UWTableAttribute); } else { unsafe { - llvm::LLVMRemoveFunctionAttr(val, llvm::UWTableAttribute.bits() as c_ulonglong); + llvm::LLVMRemoveFunctionAttr( + val, + llvm::Attribute::UWTableAttribute.bits() as c_ulonglong, + ); } } } @@ -71,10 +74,13 @@ pub fn emit_uwtable(val: ValueRef, emit: bool) { pub fn unwind(val: ValueRef, can_unwind: bool) { if can_unwind { unsafe { - llvm::LLVMRemoveFunctionAttr(val, llvm::NoUnwindAttribute.bits() as c_ulonglong); + llvm::LLVMRemoveFunctionAttr( + val, + llvm::Attribute::NoUnwindAttribute.bits() as c_ulonglong, + ); } } else { - llvm::SetFunctionAttribute(val, llvm::NoUnwindAttribute); + llvm::SetFunctionAttribute(val, llvm::Attribute::NoUnwindAttribute); } } @@ -83,10 +89,13 @@ pub fn unwind(val: ValueRef, can_unwind: bool) { #[allow(dead_code)] // possibly useful function pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { if optimize { - llvm::SetFunctionAttribute(val, llvm::OptimizeForSizeAttribute); + llvm::SetFunctionAttribute(val, llvm::Attribute::OptimizeForSizeAttribute); } else { unsafe { - llvm::LLVMRemoveFunctionAttr(val, llvm::OptimizeForSizeAttribute.bits() as c_ulonglong); + llvm::LLVMRemoveFunctionAttr( + val, + llvm::Attribute::OptimizeForSizeAttribute.bits() as c_ulonglong, + ); } } } @@ -107,7 +116,7 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe llvm::ColdAttribute as u64) } } else if attr.check_name("allocator") { - llvm::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn); + llvm::Attribute::NoAliasAttribute.apply_llfn(llvm::ReturnIndex as c_uint, llfn); } } } @@ -176,9 +185,9 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx // The outptr can be noalias and nocapture because it's entirely // invisible to the program. We also know it's nonnull as well // as how many bytes we can dereference - attrs.arg(1, llvm::StructRetAttribute) - .arg(1, llvm::NoAliasAttribute) - .arg(1, llvm::NoCaptureAttribute) + attrs.arg(1, llvm::Attribute::StructRetAttribute) + .arg(1, llvm::Attribute::NoAliasAttribute) + .arg(1, llvm::Attribute::NoCaptureAttribute) .arg(1, llvm::DereferenceableAttribute(llret_sz)); // Add one more since there's an outptr @@ -190,7 +199,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx // `~` pointer return values never alias because ownership // is transferred ty::ty_uniq(it) if common::type_is_sized(ccx.tcx(), it) => { - attrs.ret(llvm::NoAliasAttribute); + attrs.ret(llvm::Attribute::NoAliasAttribute); } _ => {} } @@ -207,7 +216,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx } if let ty::ty_bool = ret_ty.sty { - attrs.ret(llvm::ZExtAttribute); + attrs.ret(llvm::Attribute::ZExtAttribute); } } } @@ -221,20 +230,20 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx // For non-immediate arguments the callee gets its own copy of // the value on the stack, so there are no aliases. It's also // program-invisible so can't possibly capture - attrs.arg(idx, llvm::NoAliasAttribute) - .arg(idx, llvm::NoCaptureAttribute) + attrs.arg(idx, llvm::Attribute::NoAliasAttribute) + .arg(idx, llvm::Attribute::NoCaptureAttribute) .arg(idx, llvm::DereferenceableAttribute(llarg_sz)); } ty::ty_bool => { - attrs.arg(idx, llvm::ZExtAttribute); + attrs.arg(idx, llvm::Attribute::ZExtAttribute); } // `~` pointer parameters never alias because ownership is transferred ty::ty_uniq(inner) => { let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, inner)); - attrs.arg(idx, llvm::NoAliasAttribute) + attrs.arg(idx, llvm::Attribute::NoAliasAttribute) .arg(idx, llvm::DereferenceableAttribute(llsz)); } @@ -247,15 +256,15 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx !ty::type_contents(ccx.tcx(), mt.ty).interior_unsafe() => { let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); - attrs.arg(idx, llvm::NoAliasAttribute) + attrs.arg(idx, llvm::Attribute::NoAliasAttribute) .arg(idx, llvm::DereferenceableAttribute(llsz)); if mt.mutbl == ast::MutImmutable { - attrs.arg(idx, llvm::ReadOnlyAttribute); + attrs.arg(idx, llvm::Attribute::ReadOnlyAttribute); } if let ReLateBound(_, BrAnon(_)) = *b { - attrs.arg(idx, llvm::NoCaptureAttribute); + attrs.arg(idx, llvm::Attribute::NoCaptureAttribute); } } @@ -263,7 +272,7 @@ pub fn from_fn_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_type: ty::Ty<'tcx // reference to escape this function (returned or stored beyond the call by a closure). ty::ty_rptr(&ReLateBound(_, BrAnon(_)), mt) => { let llsz = machine::llsize_of_real(ccx, type_of::type_of(ccx, mt.ty)); - attrs.arg(idx, llvm::NoCaptureAttribute) + attrs.arg(idx, llvm::Attribute::NoCaptureAttribute) .arg(idx, llvm::DereferenceableAttribute(llsz)); } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index a049b9a4e49ff..4879975dde695 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2182,7 +2182,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext, unsafe { llvm::LLVMPositionBuilderAtEnd(bld, llbb); - debuginfo::insert_reference_to_gdb_debug_scripts_section_global(ccx); + debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx); let (start_fn, args) = if use_start_lang_item { let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) { diff --git a/src/librustc_trans/trans/build.rs b/src/librustc_trans/trans/build.rs index 32d73e50e6b6b..d6ac412a4faea 100644 --- a/src/librustc_trans/trans/build.rs +++ b/src/librustc_trans/trans/build.rs @@ -12,7 +12,7 @@ #![allow(non_snake_case)] use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; +use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder}; use llvm::{Opcode, IntPredicate, RealPredicate}; use llvm::{ValueRef, BasicBlockRef}; use trans::common::*; @@ -965,9 +965,9 @@ pub fn CallWithConv(cx: Block, B(cx).call_with_conv(fn_, args, conv, attributes) } -pub fn AtomicFence(cx: Block, order: AtomicOrdering) { +pub fn AtomicFence(cx: Block, order: AtomicOrdering, scope: SynchronizationScope) { if cx.unreachable.get() { return; } - B(cx).atomic_fence(order) + B(cx).atomic_fence(order, scope) } pub fn Select(cx: Block, if_: ValueRef, then: ValueRef, else_: ValueRef) -> ValueRef { diff --git a/src/librustc_trans/trans/builder.rs b/src/librustc_trans/trans/builder.rs index 3febd41bdce2e..497e0ae422c1f 100644 --- a/src/librustc_trans/trans/builder.rs +++ b/src/librustc_trans/trans/builder.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] // FFI wrappers use llvm; -use llvm::{CallConv, AtomicBinOp, AtomicOrdering, AsmDialect, AttrBuilder}; +use llvm::{CallConv, AtomicBinOp, AtomicOrdering, SynchronizationScope, AsmDialect, AttrBuilder}; use llvm::{Opcode, IntPredicate, RealPredicate, False}; use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; use trans::base; @@ -989,9 +989,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn atomic_fence(&self, order: AtomicOrdering) { + pub fn atomic_fence(&self, order: AtomicOrdering, scope: SynchronizationScope) { unsafe { - llvm::LLVMBuildAtomicFence(self.llbuilder, order); + llvm::LLVMBuildAtomicFence(self.llbuilder, order, scope); } } } diff --git a/src/librustc_trans/trans/cabi_aarch64.rs b/src/librustc_trans/trans/cabi_aarch64.rs index 57dd222338828..2eef678673987 100644 --- a/src/librustc_trans/trans/cabi_aarch64.rs +++ b/src/librustc_trans/trans/cabi_aarch64.rs @@ -10,8 +10,7 @@ #![allow(non_upper_case_globals)] -use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; -use llvm::{StructRetAttribute, ZExtAttribute}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute}; use trans::cabi::{FnType, ArgType}; use trans::context::CrateContext; use trans::type_::Type; @@ -164,7 +163,7 @@ fn is_homogenous_aggregate_ty(ty: Type) -> Option<(Type, u64)> { fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; return ArgType::direct(ty, None, None, attr); } if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) { @@ -186,12 +185,12 @@ fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { }; return ArgType::direct(ty, Some(llty), None, None); } - ArgType::indirect(ty, Some(StructRetAttribute)) + ArgType::indirect(ty, Some(Attribute::StructRetAttribute)) } fn classify_arg_ty(ccx: &CrateContext, ty: Type) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; return ArgType::direct(ty, None, None, attr); } if let Some((base_ty, members)) = is_homogenous_aggregate_ty(ty) { diff --git a/src/librustc_trans/trans/cabi_arm.rs b/src/librustc_trans/trans/cabi_arm.rs index 941c065e3d5d4..689b3b3ad37ee 100644 --- a/src/librustc_trans/trans/cabi_arm.rs +++ b/src/librustc_trans/trans/cabi_arm.rs @@ -10,8 +10,7 @@ #![allow(non_upper_case_globals)] -use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; -use llvm::{StructRetAttribute, ZExtAttribute}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute}; use trans::cabi::{FnType, ArgType}; use trans::context::CrateContext; use trans::type_::Type; @@ -132,7 +131,7 @@ fn ty_size(ty: Type, align_fn: TyAlignFn) -> usize { fn classify_ret_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; return ArgType::direct(ty, None, None, attr); } let size = ty_size(ty, align_fn); @@ -146,12 +145,12 @@ fn classify_ret_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType }; return ArgType::direct(ty, Some(llty), None, None); } - ArgType::indirect(ty, Some(StructRetAttribute)) + ArgType::indirect(ty, Some(Attribute::StructRetAttribute)) } fn classify_arg_ty(ccx: &CrateContext, ty: Type, align_fn: TyAlignFn) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; return ArgType::direct(ty, None, None, attr); } let align = align_fn(ty); diff --git a/src/librustc_trans/trans/cabi_mips.rs b/src/librustc_trans/trans/cabi_mips.rs index 2d7fdd2f2eba4..2e899f72979d7 100644 --- a/src/librustc_trans/trans/cabi_mips.rs +++ b/src/librustc_trans/trans/cabi_mips.rs @@ -13,8 +13,7 @@ use libc::c_uint; use std::cmp; use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector}; -use llvm::{StructRetAttribute, ZExtAttribute}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Vector, Attribute}; use trans::cabi::{ArgType, FnType}; use trans::context::CrateContext; use trans::type_::Type; @@ -89,10 +88,10 @@ fn ty_size(ty: Type) -> usize { fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(ty, None, None, attr) } else { - ArgType::indirect(ty, Some(StructRetAttribute)) + ArgType::indirect(ty, Some(Attribute::StructRetAttribute)) } } @@ -106,7 +105,7 @@ fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut usize) -> ArgType *offset += align_up_to(size, align * 8) / 8; if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(ty, None, None, attr) } else { ArgType::direct( diff --git a/src/librustc_trans/trans/cabi_powerpc.rs b/src/librustc_trans/trans/cabi_powerpc.rs index 8c30d4fcc2b1f..eae2378a2c518 100644 --- a/src/librustc_trans/trans/cabi_powerpc.rs +++ b/src/librustc_trans/trans/cabi_powerpc.rs @@ -10,8 +10,7 @@ use libc::c_uint; use llvm; -use llvm::{Integer, Pointer, Float, Double, Struct, Array}; -use llvm::{StructRetAttribute, ZExtAttribute}; +use llvm::{Integer, Pointer, Float, Double, Struct, Array, Attribute}; use trans::cabi::{FnType, ArgType}; use trans::context::CrateContext; use trans::type_::Type; @@ -85,10 +84,10 @@ fn ty_size(ty: Type) -> usize { fn classify_ret_ty(ccx: &CrateContext, ty: Type) -> ArgType { if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(ty, None, None, attr) } else { - ArgType::indirect(ty, Some(StructRetAttribute)) + ArgType::indirect(ty, Some(Attribute::StructRetAttribute)) } } @@ -102,7 +101,7 @@ fn classify_arg_ty(ccx: &CrateContext, ty: Type, offset: &mut usize) -> ArgType *offset += align_up_to(size, align * 8) / 8; if is_reg_ty(ty) { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(ty, None, None, attr) } else { ArgType::direct( diff --git a/src/librustc_trans/trans/cabi_x86.rs b/src/librustc_trans/trans/cabi_x86.rs index 028d20f308474..d9c265d94a793 100644 --- a/src/librustc_trans/trans/cabi_x86.rs +++ b/src/librustc_trans/trans/cabi_x86.rs @@ -52,11 +52,11 @@ pub fn compute_abi_info(ccx: &CrateContext, ret_ty = ArgType::direct(rty, Some(t), None, None); } RetPointer => { - ret_ty = ArgType::indirect(rty, Some(StructRetAttribute)); + ret_ty = ArgType::indirect(rty, Some(Attribute::StructRetAttribute)); } } } else { - let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ret_ty = ArgType::direct(rty, None, None, attr); } @@ -67,11 +67,11 @@ pub fn compute_abi_info(ccx: &CrateContext, if size == 0 { ArgType::ignore(t) } else { - ArgType::indirect(t, Some(ByValAttribute)) + ArgType::indirect(t, Some(Attribute::ByValAttribute)) } } _ => { - let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if t == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(t, None, None, attr) } }; diff --git a/src/librustc_trans/trans/cabi_x86_64.rs b/src/librustc_trans/trans/cabi_x86_64.rs index 8d946e2743bdf..d954a861a7267 100644 --- a/src/librustc_trans/trans/cabi_x86_64.rs +++ b/src/librustc_trans/trans/cabi_x86_64.rs @@ -16,7 +16,6 @@ use self::RegClass::*; use llvm::{Integer, Pointer, Float, Double}; use llvm::{Struct, Array, Attribute, Vector}; -use llvm::{StructRetAttribute, ByValAttribute, ZExtAttribute}; use trans::cabi::{ArgType, FnType}; use trans::context::CrateContext; use trans::type_::Type; @@ -407,19 +406,19 @@ pub fn compute_abi_info(ccx: &CrateContext, None) } } else { - let attr = if ty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if ty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(ty, None, None, attr) } } let mut arg_tys = Vec::new(); for t in atys { - let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), ByValAttribute); + let ty = x86_64_ty(ccx, *t, |cls| cls.is_pass_byval(), Attribute::ByValAttribute); arg_tys.push(ty); } let ret_ty = if ret_def { - x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), StructRetAttribute) + x86_64_ty(ccx, rty, |cls| cls.is_ret_bysret(), Attribute::StructRetAttribute) } else { ArgType::direct(Type::void(ccx), None, None, None) }; diff --git a/src/librustc_trans/trans/cabi_x86_win64.rs b/src/librustc_trans/trans/cabi_x86_win64.rs index 9b34c3bf26200..7808b9d27feea 100644 --- a/src/librustc_trans/trans/cabi_x86_win64.rs +++ b/src/librustc_trans/trans/cabi_x86_win64.rs @@ -31,10 +31,10 @@ pub fn compute_abi_info(ccx: &CrateContext, 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), - _ => ArgType::indirect(rty, Some(StructRetAttribute)) + _ => ArgType::indirect(rty, Some(Attribute::StructRetAttribute)) }; } else { - let attr = if rty == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if rty == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ret_ty = ArgType::direct(rty, None, None, attr); } @@ -46,11 +46,11 @@ pub fn compute_abi_info(ccx: &CrateContext, 2 => ArgType::direct(rty, Some(Type::i16(ccx)), None, None), 4 => ArgType::direct(rty, Some(Type::i32(ccx)), None, None), 8 => ArgType::direct(rty, Some(Type::i64(ccx)), None, None), - _ => ArgType::indirect(t, Some(ByValAttribute)) + _ => ArgType::indirect(t, Some(Attribute::ByValAttribute)) } } _ => { - let attr = if t == Type::i1(ccx) { Some(ZExtAttribute) } else { None }; + let attr = if t == Type::i1(ccx) { Some(Attribute::ZExtAttribute) } else { None }; ArgType::direct(t, None, None, attr) } }; diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 3ec73ff8eb9de..637325881436d 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -438,10 +438,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { /// `ty`. The scheduled code handles extracting the discriminant /// and dropping the contents associated with that variant /// *without* executing any associated drop implementation. - fn schedule_drop_enum_contents(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: Ty<'tcx>) { + fn schedule_drop_adt_contents(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: Ty<'tcx>) { // `if` below could be "!contents_needs_drop"; skipping drop // is just an optimization, so sound to be conservative. if !self.type_needs_drop(ty) { return; } @@ -455,7 +455,7 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { skip_dtor: true, }; - debug!("schedule_drop_enum_contents({:?}, val={}, ty={}) fill_on_drop={} skip_dtor={}", + debug!("schedule_drop_adt_contents({:?}, val={}, ty={}) fill_on_drop={} skip_dtor={}", cleanup_scope, self.ccx.tn().val_to_string(val), ty.repr(self.ccx.tcx()), @@ -1240,10 +1240,10 @@ pub trait CleanupMethods<'blk, 'tcx> { cleanup_scope: ScopeId, val: ValueRef, ty: Ty<'tcx>); - fn schedule_drop_enum_contents(&self, - cleanup_scope: ScopeId, - val: ValueRef, - ty: Ty<'tcx>); + fn schedule_drop_adt_contents(&self, + cleanup_scope: ScopeId, + val: ValueRef, + ty: Ty<'tcx>); fn schedule_drop_immediate(&self, cleanup_scope: ScopeId, val: ValueRef, diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 9932899ed8f0f..3aaf4addd8962 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -186,7 +186,7 @@ fn get_const_val(ccx: &CrateContext, ref_expr: &ast::Expr) -> ValueRef { let expr = get_const_expr(ccx, def_id, ref_expr); let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty()); - get_const_expr_as_global(ccx, expr, check_const::PURE_CONST, empty_substs) + get_const_expr_as_global(ccx, expr, check_const::ConstQualif::PURE_CONST, empty_substs) } pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -215,7 +215,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, Some(&val) => return val, None => {} } - let val = if qualif.intersects(check_const::NON_STATIC_BORROWS) { + let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) { // Avoid autorefs as they would create global instead of stack // references, even when only the latter are correct. let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs, diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 1506e5b266981..41ef566f2fd7f 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -735,6 +735,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { } } +/// Declare any llvm intrinsics that you might need fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { macro_rules! ifn { ($name:expr, fn() -> $ret:expr) => ( diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs new file mode 100644 index 0000000000000..9af22b788b77b --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs @@ -0,0 +1,514 @@ +// 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 super::metadata::file_metadata; +use super::utils::DIB; + +use llvm; +use llvm::debuginfo::{DIScope, DISubprogram}; +use trans::common::CrateContext; +use middle::pat_util; +use util::nodemap::NodeMap; + +use libc::c_uint; +use syntax::codemap::{Span, Pos}; +use syntax::{ast, codemap, ast_util}; + +// This procedure builds the *scope map* for a given function, which maps any +// given ast::NodeId in the function's AST to the correct DIScope metadata instance. +// +// This builder procedure walks the AST in execution order and keeps track of +// what belongs to which scope, creating DIScope DIEs along the way, and +// introducing *artificial* lexical scope descriptors where necessary. These +// artificial scopes allow GDB to correctly handle name shadowing. +pub fn create_scope_map(cx: &CrateContext, + args: &[ast::Arg], + fn_entry_block: &ast::Block, + fn_metadata: DISubprogram, + fn_ast_id: ast::NodeId) + -> NodeMap { + let mut scope_map = NodeMap(); + + let def_map = &cx.tcx().def_map; + + let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None }); + scope_map.insert(fn_ast_id, fn_metadata); + + // Push argument identifiers onto the stack so arguments integrate nicely + // with variable shadowing. + for arg in args { + pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { + scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, + name: Some(path1.node.name) }); + scope_map.insert(node_id, fn_metadata); + }) + } + + // Clang creates a separate scope for function bodies, so let's do this too. + with_new_scope(cx, + fn_entry_block.span, + &mut scope_stack, + &mut scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, fn_entry_block, scope_stack, scope_map); + }); + + return scope_map; +} + +// local helper functions for walking the AST. +fn with_new_scope(cx: &CrateContext, + scope_span: Span, + scope_stack: &mut Vec , + scope_map: &mut NodeMap, + inner_walk: F) where + F: FnOnce(&CrateContext, &mut Vec, &mut NodeMap), +{ + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); + let file_metadata = file_metadata(cx, &loc.file.name); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None }); + + inner_walk(cx, scope_stack, scope_map); + + // pop artificial scopes + while scope_stack.last().unwrap().name.is_some() { + scope_stack.pop(); + } + + if scope_stack.last().unwrap().scope_metadata != scope_metadata { + cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); + } + + scope_stack.pop(); +} + +struct ScopeStackEntry { + scope_metadata: DIScope, + name: Option +} + +fn walk_block(cx: &CrateContext, + block: &ast::Block, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); + + // The interesting things here are statements and the concluding expression. + for statement in &block.stmts { + scope_map.insert(ast_util::stmt_id(&**statement), + scope_stack.last().unwrap().scope_metadata); + + match statement.node { + ast::StmtDecl(ref decl, _) => + walk_decl(cx, &**decl, scope_stack, scope_map), + ast::StmtExpr(ref exp, _) | + ast::StmtSemi(ref exp, _) => + walk_expr(cx, &**exp, scope_stack, scope_map), + ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). + } + } + + if let Some(ref exp) = block.expr { + walk_expr(cx, &**exp, scope_stack, scope_map); + } +} + +fn walk_decl(cx: &CrateContext, + decl: &ast::Decl, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + match *decl { + codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { + scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); + + walk_pattern(cx, &*local.pat, scope_stack, scope_map); + + if let Some(ref exp) = local.init { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + _ => () + } +} + +fn walk_pattern(cx: &CrateContext, + pat: &ast::Pat, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + let def_map = &cx.tcx().def_map; + + // Unfortunately, we cannot just use pat_util::pat_bindings() or + // ast_util::walk_pat() here because we have to visit *all* nodes in + // order to put them into the scope map. The above functions don't do that. + match pat.node { + ast::PatIdent(_, ref path1, ref sub_pat_opt) => { + + // Check if this is a binding. If so we need to put it on the + // scope stack and maybe introduce an artificial scope + if pat_util::pat_is_binding(def_map, &*pat) { + + let name = path1.node.name; + + // LLVM does not properly generate 'DW_AT_start_scope' fields + // for variable DIEs. For this reason we have to introduce + // an artificial scope at bindings whenever a variable with + // the same name is declared in *any* parent scope. + // + // Otherwise the following error occurs: + // + // let x = 10; + // + // do_something(); // 'gdb print x' correctly prints 10 + // + // { + // do_something(); // 'gdb print x' prints 0, because it + // // already reads the uninitialized 'x' + // // from the next line... + // let x = 100; + // do_something(); // 'gdb print x' correctly prints 100 + // } + + // Is there already a binding with that name? + // N.B.: this comparison must be UNhygienic... because + // gdb knows nothing about the context, so any two + // variables with the same name will cause the problem. + let need_new_scope = scope_stack + .iter() + .any(|entry| entry.name == Some(name)); + + if need_new_scope { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); + let file_metadata = file_metadata(cx, &loc.file.name); + let parent_scope = scope_stack.last().unwrap().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { + scope_metadata: scope_metadata, + name: Some(name) + }); + + } else { + // Push a new entry anyway so the name can be found + let prev_metadata = scope_stack.last().unwrap().scope_metadata; + scope_stack.push(ScopeStackEntry { + scope_metadata: prev_metadata, + name: Some(name) + }); + } + } + + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + if let Some(ref sub_pat) = *sub_pat_opt { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatWild(_) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + + ast::PatEnum(_, ref sub_pats_opt) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + if let Some(ref sub_pats) = *sub_pats_opt { + for p in sub_pats { + walk_pattern(cx, &**p, scope_stack, scope_map); + } + } + } + + ast::PatQPath(..) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + } + + ast::PatStruct(_, ref field_pats, _) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for &codemap::Spanned { + node: ast::FieldPat { pat: ref sub_pat, .. }, + .. + } in field_pats.iter() { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatTup(ref sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + ast::PatLit(ref exp) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + ast::PatRange(ref exp1, ref exp2) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + walk_expr(cx, &**exp1, scope_stack, scope_map); + walk_expr(cx, &**exp2, scope_stack, scope_map); + } + + ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + + for sub_pat in front_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + if let Some(ref sub_pat) = *middle_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + + for sub_pat in back_sub_pats { + walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + } + } + + ast::PatMac(_) => { + cx.sess().span_bug(pat.span, "debuginfo::create_scope_map() - \ + Found unexpanded macro."); + } + } +} + +fn walk_expr(cx: &CrateContext, + exp: &ast::Expr, + scope_stack: &mut Vec , + scope_map: &mut NodeMap) { + + scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); + + match exp.node { + ast::ExprLit(_) | + ast::ExprBreak(_) | + ast::ExprAgain(_) | + ast::ExprPath(..) => {} + + ast::ExprCast(ref sub_exp, _) | + ast::ExprAddrOf(_, ref sub_exp) | + ast::ExprField(ref sub_exp, _) | + ast::ExprTupField(ref sub_exp, _) | + ast::ExprParen(ref sub_exp) => + walk_expr(cx, &**sub_exp, scope_stack, scope_map), + + ast::ExprBox(ref place, ref sub_expr) => { + place.as_ref().map( + |e| walk_expr(cx, &**e, scope_stack, scope_map)); + walk_expr(cx, &**sub_expr, scope_stack, scope_map); + } + + ast::ExprRet(ref exp_opt) => match *exp_opt { + Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), + None => () + }, + + ast::ExprUnary(_, ref sub_exp) => { + walk_expr(cx, &**sub_exp, scope_stack, scope_map); + } + + ast::ExprAssignOp(_, ref lhs, ref rhs) | + ast::ExprIndex(ref lhs, ref rhs) | + ast::ExprBinary(_, ref lhs, ref rhs) => { + walk_expr(cx, &**lhs, scope_stack, scope_map); + walk_expr(cx, &**rhs, scope_stack, scope_map); + } + + ast::ExprRange(ref start, ref end) => { + start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); + end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); + } + + ast::ExprVec(ref init_expressions) | + ast::ExprTup(ref init_expressions) => { + for ie in init_expressions { + walk_expr(cx, &**ie, scope_stack, scope_map); + } + } + + ast::ExprAssign(ref sub_exp1, ref sub_exp2) | + ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { + walk_expr(cx, &**sub_exp1, scope_stack, scope_map); + walk_expr(cx, &**sub_exp2, scope_stack, scope_map); + } + + ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + then_block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**then_block, scope_stack, scope_map); + }); + + match *opt_else_exp { + Some(ref else_exp) => + walk_expr(cx, &**else_exp, scope_stack, scope_map), + _ => () + } + } + + ast::ExprIfLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded if-let."); + } + + ast::ExprWhile(ref cond_exp, ref loop_body, _) => { + walk_expr(cx, &**cond_exp, scope_stack, scope_map); + + with_new_scope(cx, + loop_body.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**loop_body, scope_stack, scope_map); + }) + } + + ast::ExprWhileLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded while-let."); + } + + ast::ExprForLoop(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded for loop."); + } + + ast::ExprMac(_) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded macro."); + } + + ast::ExprLoop(ref block, _) | + ast::ExprBlock(ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprClosure(_, ref decl, ref block) => { + with_new_scope(cx, + block.span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for &ast::Arg { pat: ref pattern, .. } in &decl.inputs { + walk_pattern(cx, &**pattern, scope_stack, scope_map); + } + + walk_block(cx, &**block, scope_stack, scope_map); + }) + } + + ast::ExprCall(ref fn_exp, ref args) => { + walk_expr(cx, &**fn_exp, scope_stack, scope_map); + + for arg_exp in args { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMethodCall(_, _, ref args) => { + for arg_exp in args { + walk_expr(cx, &**arg_exp, scope_stack, scope_map); + } + } + + ast::ExprMatch(ref discriminant_exp, ref arms, _) => { + walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); + + // For each arm we have to first walk the pattern as these might + // introduce new artificial scopes. It should be sufficient to + // walk only one pattern per arm, as they all must contain the + // same binding names. + + for arm_ref in arms { + let arm_span = arm_ref.pats[0].span; + + with_new_scope(cx, + arm_span, + scope_stack, + scope_map, + |cx, scope_stack, scope_map| { + for pat in &arm_ref.pats { + walk_pattern(cx, &**pat, scope_stack, scope_map); + } + + if let Some(ref guard_exp) = arm_ref.guard { + walk_expr(cx, &**guard_exp, scope_stack, scope_map) + } + + walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); + }) + } + } + + ast::ExprStruct(_, ref fields, ref base_exp) => { + for &ast::Field { expr: ref exp, .. } in fields { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + match *base_exp { + Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), + None => () + } + } + + ast::ExprInlineAsm(ast::InlineAsm { ref inputs, + ref outputs, + .. }) => { + // inputs, outputs: Vec<(String, P)> + for &(_, ref exp) in inputs { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + + for &(_, ref exp, _) in outputs { + walk_expr(cx, &**exp, scope_stack, scope_map); + } + } + } +} \ No newline at end of file diff --git a/src/librustc_trans/trans/debuginfo/doc.rs b/src/librustc_trans/trans/debuginfo/doc.rs new file mode 100644 index 0000000000000..a91619b2f845a --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/doc.rs @@ -0,0 +1,189 @@ +// 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. + +//! # Debug Info Module +//! +//! This module serves the purpose of generating debug symbols. We use LLVM's +//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html) +//! features for generating the debug information. The general principle is +//! this: +//! +//! Given the right metadata in the LLVM IR, the LLVM code generator is able to +//! create DWARF debug symbols for the given code. The +//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured +//! much like DWARF *debugging information entries* (DIE), representing type +//! information such as datatype layout, function signatures, block layout, +//! variable location and scope information, etc. It is the purpose of this +//! module to generate correct metadata and insert it into the LLVM IR. +//! +//! As the exact format of metadata trees may change between different LLVM +//! versions, we now use LLVM +//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) +//! to create metadata where possible. This will hopefully ease the adaption of +//! this module to future LLVM versions. +//! +//! The public API of the module is a set of functions that will insert the +//! correct metadata into the LLVM IR when called with the right parameters. +//! The module is thus driven from an outside client with functions like +//! `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`. +//! +//! Internally the module will try to reuse already created metadata by +//! utilizing a cache. The way to get a shared metadata node when needed is +//! thus to just call the corresponding function in this module: +//! +//! let file_metadata = file_metadata(crate_context, path); +//! +//! The function will take care of probing the cache for an existing node for +//! that exact file path. +//! +//! All private state used by the module is stored within either the +//! CrateDebugContext struct (owned by the CrateContext) or the +//! FunctionDebugContext (owned by the FunctionContext). +//! +//! This file consists of three conceptual sections: +//! 1. The public interface of the module +//! 2. Module-internal metadata creation functions +//! 3. Minor utility functions +//! +//! +//! ## Recursive Types +//! +//! Some kinds of types, such as structs and enums can be recursive. That means +//! that the type definition of some type X refers to some other type which in +//! turn (transitively) refers to X. This introduces cycles into the type +//! referral graph. A naive algorithm doing an on-demand, depth-first traversal +//! of this graph when describing types, can get trapped in an endless loop +//! when it reaches such a cycle. +//! +//! For example, the following simple type for a singly-linked list... +//! +//! ``` +//! struct List { +//! value: int, +//! tail: Option>, +//! } +//! ``` +//! +//! will generate the following callstack with a naive DFS algorithm: +//! +//! ``` +//! describe(t = List) +//! describe(t = int) +//! describe(t = Option>) +//! describe(t = Box) +//! describe(t = List) // at the beginning again... +//! ... +//! ``` +//! +//! To break cycles like these, we use "forward declarations". That is, when +//! the algorithm encounters a possibly recursive type (any struct or enum), it +//! immediately creates a type description node and inserts it into the cache +//! *before* describing the members of the type. This type description is just +//! a stub (as type members are not described and added to it yet) but it +//! allows the algorithm to already refer to the type. After the stub is +//! inserted into the cache, the algorithm continues as before. If it now +//! encounters a recursive reference, it will hit the cache and does not try to +//! describe the type anew. +//! +//! This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, +//! which represents a kind of continuation, storing all state needed to +//! continue traversal at the type members after the type has been registered +//! with the cache. (This implementation approach might be a tad over- +//! engineered and may change in the future) +//! +//! +//! ## Source Locations and Line Information +//! +//! In addition to data type descriptions the debugging information must also +//! allow to map machine code locations back to source code locations in order +//! to be useful. This functionality is also handled in this module. The +//! following functions allow to control source mappings: +//! +//! + set_source_location() +//! + clear_source_location() +//! + start_emitting_source_locations() +//! +//! `set_source_location()` allows to set the current source location. All IR +//! instructions created after a call to this function will be linked to the +//! given source location, until another location is specified with +//! `set_source_location()` or the source location is cleared with +//! `clear_source_location()`. In the later case, subsequent IR instruction +//! will not be linked to any source location. As you can see, this is a +//! stateful API (mimicking the one in LLVM), so be careful with source +//! locations set by previous calls. It's probably best to not rely on any +//! specific state being present at a given point in code. +//! +//! One topic that deserves some extra attention is *function prologues*. At +//! the beginning of a function's machine code there are typically a few +//! instructions for loading argument values into allocas and checking if +//! there's enough stack space for the function to execute. This *prologue* is +//! not visible in the source code and LLVM puts a special PROLOGUE END marker +//! into the line table at the first non-prologue instruction of the function. +//! In order to find out where the prologue ends, LLVM looks for the first +//! instruction in the function body that is linked to a source location. So, +//! when generating prologue instructions we have to make sure that we don't +//! emit source location information until the 'real' function body begins. For +//! this reason, source location emission is disabled by default for any new +//! function being translated and is only activated after a call to the third +//! function from the list above, `start_emitting_source_locations()`. This +//! function should be called right before regularly starting to translate the +//! top-level block of the given function. +//! +//! There is one exception to the above rule: `llvm.dbg.declare` instruction +//! must be linked to the source location of the variable being declared. For +//! function parameters these `llvm.dbg.declare` instructions typically occur +//! in the middle of the prologue, however, they are ignored by LLVM's prologue +//! detection. The `create_argument_metadata()` and related functions take care +//! of linking the `llvm.dbg.declare` instructions to the correct source +//! locations even while source location emission is still disabled, so there +//! is no need to do anything special with source location handling here. +//! +//! ## Unique Type Identification +//! +//! In order for link-time optimization to work properly, LLVM needs a unique +//! type identifier that tells it across compilation units which types are the +//! same as others. This type identifier is created by +//! TypeMap::get_unique_type_id_of_type() using the following algorithm: +//! +//! (1) Primitive types have their name as ID +//! (2) Structs, enums and traits have a multipart identifier +//! +//! (1) The first part is the SVH (strict version hash) of the crate they +//! wereoriginally defined in +//! +//! (2) The second part is the ast::NodeId of the definition in their +//! originalcrate +//! +//! (3) The final part is a concatenation of the type IDs of their concrete +//! typearguments if they are generic types. +//! +//! (3) Tuple-, pointer and function types are structurally identified, which +//! means that they are equivalent if their component types are equivalent +//! (i.e. (int, int) is the same regardless in which crate it is used). +//! +//! This algorithm also provides a stable ID for types that are defined in one +//! crate but instantiated from metadata within another crate. We just have to +//! take care to always map crate and node IDs back to the original crate +//! context. +//! +//! As a side-effect these unique type IDs also help to solve a problem arising +//! from lifetime parameters. Since lifetime parameters are completely omitted +//! in debuginfo, more than one `Ty` instance may map to the same debuginfo +//! type metadata, that is, some struct `Struct<'a>` may have N instantiations +//! with different concrete substitutions for `'a`, and thus there will be N +//! `Ty` instances for the type `Struct<'a>` even though it is not generic +//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as +//! cheap identifier for type metadata---we have done this in the past, but it +//! led to unnecessary metadata duplication in the best case and LLVM +//! assertions in the worst. However, the unique type ID as described above +//! *can* be used as identifier. Since it is comparatively expensive to +//! construct, though, `ty::type_id()` is still used additionally as an +//! optimization for cases where the exact same type has been seen before +//! (which is most of the time). diff --git a/src/librustc_trans/trans/debuginfo/gdb.rs b/src/librustc_trans/trans/debuginfo/gdb.rs new file mode 100644 index 0000000000000..a6f1199d0ffe7 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/gdb.rs @@ -0,0 +1,94 @@ +// 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. + +// .debug_gdb_scripts binary section. + +use llvm; +use llvm::ValueRef; + +use trans::common::{C_bytes, CrateContext}; +use trans::declare; +use trans::type_::Type; +use middle::ty::ClosureTyper; +use session::config::NoDebugInfo; + +use std::ffi::CString; +use std::ptr; +use syntax::attr; + + +/// Inserts a side-effect free instruction sequence that makes sure that the +/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. +pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { + if needs_gdb_debug_scripts_section(ccx) { + let empty = CString::new("").unwrap(); + let gdb_debug_scripts_section_global = + get_or_insert_gdb_debug_scripts_section_global(ccx); + unsafe { + let volative_load_instruction = + llvm::LLVMBuildLoad(ccx.raw_builder(), + gdb_debug_scripts_section_global, + empty.as_ptr()); + llvm::LLVMSetVolatile(volative_load_instruction, llvm::True); + } + } +} + +/// Allocates the global variable responsible for the .debug_gdb_scripts binary +/// section. +pub fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext) + -> llvm::ValueRef { + let section_var_name = "__rustc_debug_gdb_scripts_section__"; + + let section_var = unsafe { + llvm::LLVMGetNamedGlobal(ccx.llmod(), + section_var_name.as_ptr() as *const _) + }; + + if section_var == ptr::null_mut() { + let section_name = b".debug_gdb_scripts\0"; + let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; + + unsafe { + let llvm_type = Type::array(&Type::i8(ccx), + section_contents.len() as u64); + + let section_var = declare::define_global(ccx, section_var_name, + llvm_type).unwrap_or_else(||{ + ccx.sess().bug(&format!("symbol `{}` is already defined", section_var_name)) + }); + llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(section_var, C_bytes(ccx, section_contents)); + llvm::LLVMSetGlobalConstant(section_var, llvm::True); + llvm::LLVMSetUnnamedAddr(section_var, llvm::True); + llvm::SetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); + // This should make sure that the whole section is not larger than + // the string it contains. Otherwise we get a warning from GDB. + llvm::LLVMSetAlignment(section_var, 1); + section_var + } + } else { + section_var + } +} + +pub fn needs_gdb_debug_scripts_section(ccx: &CrateContext) -> bool { + let omit_gdb_pretty_printer_section = + attr::contains_name(&ccx.tcx() + .map + .krate() + .attrs, + "omit_gdb_pretty_printer_section"); + + !omit_gdb_pretty_printer_section && + !ccx.sess().target.target.options.is_like_osx && + !ccx.sess().target.target.options.is_like_windows && + ccx.sess().opts.debuginfo != NoDebugInfo +} diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo/metadata.rs similarity index 51% rename from src/librustc_trans/trans/debuginfo.rs rename to src/librustc_trans/trans/debuginfo/metadata.rs index 516ff443dacb9..9ff69e7f9dd29 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo/metadata.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,225 +8,46 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! # Debug Info Module -//! -//! This module serves the purpose of generating debug symbols. We use LLVM's -//! [source level debugging](http://llvm.org/docs/SourceLevelDebugging.html) -//! features for generating the debug information. The general principle is this: -//! -//! Given the right metadata in the LLVM IR, the LLVM code generator is able to -//! create DWARF debug symbols for the given code. The -//! [metadata](http://llvm.org/docs/LangRef.html#metadata-type) is structured much -//! like DWARF *debugging information entries* (DIE), representing type information -//! such as datatype layout, function signatures, block layout, variable location -//! and scope information, etc. It is the purpose of this module to generate correct -//! metadata and insert it into the LLVM IR. -//! -//! As the exact format of metadata trees may change between different LLVM -//! versions, we now use LLVM -//! [DIBuilder](http://llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) to -//! create metadata where possible. This will hopefully ease the adaption of this -//! module to future LLVM versions. -//! -//! The public API of the module is a set of functions that will insert the correct -//! metadata into the LLVM IR when called with the right parameters. The module is -//! thus driven from an outside client with functions like -//! `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`. -//! -//! Internally the module will try to reuse already created metadata by utilizing a -//! cache. The way to get a shared metadata node when needed is thus to just call -//! the corresponding function in this module: -//! -//! let file_metadata = file_metadata(crate_context, path); -//! -//! The function will take care of probing the cache for an existing node for that -//! exact file path. -//! -//! All private state used by the module is stored within either the -//! CrateDebugContext struct (owned by the CrateContext) or the FunctionDebugContext -//! (owned by the FunctionContext). -//! -//! This file consists of three conceptual sections: -//! 1. The public interface of the module -//! 2. Module-internal metadata creation functions -//! 3. Minor utility functions -//! -//! -//! ## Recursive Types -//! -//! Some kinds of types, such as structs and enums can be recursive. That means that -//! the type definition of some type X refers to some other type which in turn -//! (transitively) refers to X. This introduces cycles into the type referral graph. -//! A naive algorithm doing an on-demand, depth-first traversal of this graph when -//! describing types, can get trapped in an endless loop when it reaches such a -//! cycle. -//! -//! For example, the following simple type for a singly-linked list... -//! -//! ``` -//! struct List { -//! value: int, -//! tail: Option>, -//! } -//! ``` -//! -//! will generate the following callstack with a naive DFS algorithm: -//! -//! ``` -//! describe(t = List) -//! describe(t = int) -//! describe(t = Option>) -//! describe(t = Box) -//! describe(t = List) // at the beginning again... -//! ... -//! ``` -//! -//! To break cycles like these, we use "forward declarations". That is, when the -//! algorithm encounters a possibly recursive type (any struct or enum), it -//! immediately creates a type description node and inserts it into the cache -//! *before* describing the members of the type. This type description is just a -//! stub (as type members are not described and added to it yet) but it allows the -//! algorithm to already refer to the type. After the stub is inserted into the -//! cache, the algorithm continues as before. If it now encounters a recursive -//! reference, it will hit the cache and does not try to describe the type anew. -//! -//! This behaviour is encapsulated in the 'RecursiveTypeDescription' enum, which -//! represents a kind of continuation, storing all state needed to continue -//! traversal at the type members after the type has been registered with the cache. -//! (This implementation approach might be a tad over-engineered and may change in -//! the future) -//! -//! -//! ## Source Locations and Line Information -//! -//! In addition to data type descriptions the debugging information must also allow -//! to map machine code locations back to source code locations in order to be useful. -//! This functionality is also handled in this module. The following functions allow -//! to control source mappings: -//! -//! + set_source_location() -//! + clear_source_location() -//! + start_emitting_source_locations() -//! -//! `set_source_location()` allows to set the current source location. All IR -//! instructions created after a call to this function will be linked to the given -//! source location, until another location is specified with -//! `set_source_location()` or the source location is cleared with -//! `clear_source_location()`. In the later case, subsequent IR instruction will not -//! be linked to any source location. As you can see, this is a stateful API -//! (mimicking the one in LLVM), so be careful with source locations set by previous -//! calls. It's probably best to not rely on any specific state being present at a -//! given point in code. -//! -//! One topic that deserves some extra attention is *function prologues*. At the -//! beginning of a function's machine code there are typically a few instructions -//! for loading argument values into allocas and checking if there's enough stack -//! space for the function to execute. This *prologue* is not visible in the source -//! code and LLVM puts a special PROLOGUE END marker into the line table at the -//! first non-prologue instruction of the function. In order to find out where the -//! prologue ends, LLVM looks for the first instruction in the function body that is -//! linked to a source location. So, when generating prologue instructions we have -//! to make sure that we don't emit source location information until the 'real' -//! function body begins. For this reason, source location emission is disabled by -//! default for any new function being translated and is only activated after a call -//! to the third function from the list above, `start_emitting_source_locations()`. -//! This function should be called right before regularly starting to translate the -//! top-level block of the given function. -//! -//! There is one exception to the above rule: `llvm.dbg.declare` instruction must be -//! linked to the source location of the variable being declared. For function -//! parameters these `llvm.dbg.declare` instructions typically occur in the middle -//! of the prologue, however, they are ignored by LLVM's prologue detection. The -//! `create_argument_metadata()` and related functions take care of linking the -//! `llvm.dbg.declare` instructions to the correct source locations even while -//! source location emission is still disabled, so there is no need to do anything -//! special with source location handling here. -//! -//! ## Unique Type Identification -//! -//! In order for link-time optimization to work properly, LLVM needs a unique type -//! identifier that tells it across compilation units which types are the same as -//! others. This type identifier is created by TypeMap::get_unique_type_id_of_type() -//! using the following algorithm: -//! -//! (1) Primitive types have their name as ID -//! (2) Structs, enums and traits have a multipart identifier -//! -//! (1) The first part is the SVH (strict version hash) of the crate they were -//! originally defined in -//! -//! (2) The second part is the ast::NodeId of the definition in their original -//! crate -//! -//! (3) The final part is a concatenation of the type IDs of their concrete type -//! arguments if they are generic types. -//! -//! (3) Tuple-, pointer and function types are structurally identified, which means -//! that they are equivalent if their component types are equivalent (i.e. (int, -//! int) is the same regardless in which crate it is used). -//! -//! This algorithm also provides a stable ID for types that are defined in one crate -//! but instantiated from metadata within another crate. We just have to take care -//! to always map crate and node IDs back to the original crate context. -//! -//! As a side-effect these unique type IDs also help to solve a problem arising from -//! lifetime parameters. Since lifetime parameters are completely omitted in -//! debuginfo, more than one `Ty` instance may map to the same debuginfo type -//! metadata, that is, some struct `Struct<'a>` may have N instantiations with -//! different concrete substitutions for `'a`, and thus there will be N `Ty` -//! instances for the type `Struct<'a>` even though it is not generic otherwise. -//! Unfortunately this means that we cannot use `ty::type_id()` as cheap identifier -//! for type metadata---we have done this in the past, but it led to unnecessary -//! metadata duplication in the best case and LLVM assertions in the worst. However, -//! the unique type ID as described above *can* be used as identifier. Since it is -//! comparatively expensive to construct, though, `ty::type_id()` is still used -//! additionally as an optimization for cases where the exact same type has been -//! seen before (which is most of the time). -use self::VariableAccess::*; -use self::VariableKind::*; +use self::RecursiveTypeDescription::*; use self::MemberOffset::*; use self::MemberDescriptionFactory::*; -use self::RecursiveTypeDescription::*; use self::EnumDiscriminantInfo::*; -use self::InternalDebugLocation::*; -use llvm; -use llvm::{ModuleRef, ContextRef, ValueRef}; -use llvm::debuginfo::*; +use super::utils::{debug_context, DIB, span_start, bytes_to_bits, size_and_align_of, + get_namespace_and_span_for_item, create_DIArray, + fn_should_be_ignored, is_node_local_to_unit}; +use super::namespace::namespace_for_item; +use super::type_names::{compute_debuginfo_type_name, push_debuginfo_type_name}; +use super::{declare_local, VariableKind, VariableAccess}; + +use llvm::{self, ValueRef}; +use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType}; + use metadata::csearch; +use middle::pat_util; use middle::subst::{self, Substs}; -use trans::{self, adt, machine, type_of}; -use trans::common::{self, NodeIdAndSpan, CrateContext, FunctionContext, Block, C_bytes, - NormalizingClosureTyper}; -use trans::declare; +use trans::{type_of, adt, machine, monomorphize}; +use trans::common::{self, CrateContext, FunctionContext, NormalizingClosureTyper, Block}; use trans::_match::{BindingInfo, TrByCopy, TrByMove, TrByRef}; -use trans::monomorphize; use trans::type_::Type; use middle::ty::{self, Ty, ClosureTyper}; -use middle::pat_util; -use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; -use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet}; +use session::config::{self, FullDebugInfo}; +use util::nodemap::FnvHashMap; use util::ppaux; use util::common::path2cstr; use libc::{c_uint, c_longlong}; -use std::cell::{Cell, RefCell}; use std::ffi::CString; use std::path::Path; use std::ptr; -use std::rc::{Rc, Weak}; +use std::rc::Rc; use syntax::util::interner::Interner; -use syntax::codemap::{Span, Pos}; -use syntax::{ast, codemap, ast_util, ast_map, attr}; +use syntax::codemap::Span; +use syntax::{ast, codemap, ast_util, ast_map}; use syntax::parse::token::{self, special_idents}; -const DW_LANG_RUST: c_uint = 0x9000; - -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; +const DW_LANG_RUST: c_uint = 0x9000; #[allow(non_upper_case_globals)] const DW_ATE_boolean: c_uint = 0x02; #[allow(non_upper_case_globals)] @@ -238,8 +59,8 @@ const DW_ATE_unsigned: c_uint = 0x07; #[allow(non_upper_case_globals)] const DW_ATE_unsigned_char: c_uint = 0x08; -const UNKNOWN_LINE_NUMBER: c_uint = 0; -const UNKNOWN_COLUMN_NUMBER: c_uint = 0; +pub const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; // ptr::null() doesn't work :( const UNKNOWN_FILE_METADATA: DIFile = (0 as DIFile); @@ -247,18 +68,14 @@ const UNKNOWN_SCOPE_METADATA: DIScope = (0 as DIScope); const FLAGS_NONE: c_uint = 0; -//=----------------------------------------------------------------------------- -// Public Interface of debuginfo module -//=----------------------------------------------------------------------------- - #[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] -struct UniqueTypeId(ast::Name); +pub struct UniqueTypeId(ast::Name); // The TypeMap is where the CrateDebugContext holds the type metadata nodes // created so far. The metadata nodes are indexed by UniqueTypeId, and, for // faster lookup, also by Ty. The TypeMap is responsible for creating // UniqueTypeIds. -struct TypeMap<'tcx> { +pub struct TypeMap<'tcx> { // The UniqueTypeIds created so far unique_id_interner: Interner>, // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. @@ -270,8 +87,7 @@ struct TypeMap<'tcx> { } impl<'tcx> TypeMap<'tcx> { - - fn new() -> TypeMap<'tcx> { + pub fn new() -> TypeMap<'tcx> { TypeMap { unique_id_interner: Interner::new(), type_to_metadata: FnvHashMap(), @@ -609,6 +425,92 @@ impl<'tcx> TypeMap<'tcx> { } } +// A description of some recursive type. It can either be already finished (as +// with FinalMetadata) or it is not yet finished, but contains all information +// needed to generate the missing parts of the description. See the +// documentation section on Recursive Types at the top of this file for more +// information. +enum RecursiveTypeDescription<'tcx> { + UnfinishedMetadata { + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + llvm_type: Type, + member_description_factory: MemberDescriptionFactory<'tcx>, + }, + FinalMetadata(DICompositeType) +} + +fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( + cx: &CrateContext<'a, 'tcx>, + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + llvm_type: Type, + member_description_factory: MemberDescriptionFactory<'tcx>) + -> RecursiveTypeDescription<'tcx> { + + // Insert the stub into the TypeMap in order to allow for recursive references + let mut type_map = debug_context(cx).type_map.borrow_mut(); + type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata_stub); + type_map.register_type_with_metadata(cx, unfinished_type, metadata_stub); + + UnfinishedMetadata { + unfinished_type: unfinished_type, + unique_type_id: unique_type_id, + metadata_stub: metadata_stub, + llvm_type: llvm_type, + member_description_factory: member_description_factory, + } +} + +impl<'tcx> RecursiveTypeDescription<'tcx> { + // Finishes up the description of the type in question (mostly by providing + // descriptions of the fields of the given type) and returns the final type + // metadata. + fn finalize<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> MetadataCreationResult { + match *self { + FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + llvm_type, + ref member_description_factory, + .. + } => { + // Make sure that we have a forward declaration of the type in + // the TypeMap so that recursive references are possible. This + // will always be the case if the RecursiveTypeDescription has + // been properly created through the + // create_and_register_recursive_type_forward_declaration() + // function. + { + let type_map = debug_context(cx).type_map.borrow(); + if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || + type_map.find_metadata_for_type(unfinished_type).is_none() { + cx.sess().bug(&format!("Forward declaration of potentially recursive type \ + '{}' was not found in TypeMap!", + ppaux::ty_to_string(cx.tcx(), unfinished_type)) + ); + } + } + + // ... then create the member descriptions ... + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + // ... and attach them to the stub to complete it. + set_members_of_composite_type(cx, + metadata_stub, + llvm_type, + &member_descriptions[..]); + return MetadataCreationResult::new(metadata_stub, true); + } + } + } +} + // Returns from the enclosing function if the type metadata with the given // unique id can be found in the type map macro_rules! return_if_metadata_created_in_meantime { @@ -622,1002 +524,479 @@ macro_rules! return_if_metadata_created_in_meantime { ) } +fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + unique_type_id: UniqueTypeId, + element_type: Ty<'tcx>, + len: Option, + span: Span) + -> MetadataCreationResult { + let element_type_metadata = type_metadata(cx, element_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); -/// A context object for maintaining all state needed by the debuginfo module. -pub struct CrateDebugContext<'tcx> { - llcontext: ContextRef, - builder: DIBuilderRef, - current_debug_location: Cell, - created_files: RefCell>, - created_enum_disr_types: RefCell>, + let element_llvm_type = type_of::type_of(cx, element_type); + let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); - type_map: RefCell>, - namespace_map: RefCell, Rc>>, + let (array_size_in_bytes, upper_bound) = match len { + Some(len) => (element_type_size * len, len as c_longlong), + None => (0, -1) + }; - // This collection is used to assert that composite types (structs, enums, - // ...) have their members only set once: - composite_types_completed: RefCell>, -} + let subrange = unsafe { + llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) + }; -impl<'tcx> CrateDebugContext<'tcx> { - pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { - debug!("CrateDebugContext::new"); - let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) }; - // DIBuilder inherits context from the module, so we'd better use the same one - let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - return CrateDebugContext { - llcontext: llcontext, - builder: builder, - current_debug_location: Cell::new(UnknownLocation), - created_files: RefCell::new(FnvHashMap()), - created_enum_disr_types: RefCell::new(DefIdMap()), - type_map: RefCell::new(TypeMap::new()), - namespace_map: RefCell::new(FnvHashMap()), - composite_types_completed: RefCell::new(FnvHashSet()), - }; - } -} + let subscripts = create_DIArray(DIB(cx), &[subrange]); + let metadata = unsafe { + llvm::LLVMDIBuilderCreateArrayType( + DIB(cx), + bytes_to_bits(array_size_in_bytes), + bytes_to_bits(element_type_align), + element_type_metadata, + subscripts) + }; -pub enum FunctionDebugContext { - RegularContext(Box), - DebugInfoDisabled, - FunctionWithoutDebugInfo, + return MetadataCreationResult::new(metadata, false); } -impl FunctionDebugContext { - fn get_ref<'a>(&'a self, - cx: &CrateContext, - span: Span) - -> &'a FunctionDebugContextData { - match *self { - FunctionDebugContext::RegularContext(box ref data) => data, - FunctionDebugContext::DebugInfoDisabled => { - cx.sess().span_bug(span, - FunctionDebugContext::debuginfo_disabled_message()); - } - FunctionDebugContext::FunctionWithoutDebugInfo => { - cx.sess().span_bug(span, - FunctionDebugContext::should_be_ignored_message()); - } - } - } +fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + vec_type: Ty<'tcx>, + element_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> MetadataCreationResult { + let data_ptr_type = ty::mk_ptr(cx.tcx(), ty::mt { + ty: element_type, + mutbl: ast::MutImmutable + }); - fn debuginfo_disabled_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" - } + let element_type_metadata = type_metadata(cx, data_ptr_type, span); - fn should_be_ignored_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext for function that should be \ - ignored by debug info!" - } -} + return_if_metadata_created_in_meantime!(cx, unique_type_id); -struct FunctionDebugContextData { - scope_map: RefCell>, - fn_metadata: DISubprogram, - argument_counter: Cell, - source_locations_enabled: Cell, - source_location_override: Cell, -} + let slice_llvm_type = type_of::type_of(cx, vec_type); + let slice_type_name = compute_debuginfo_type_name(cx, vec_type, true); -enum VariableAccess<'a> { - // The llptr given is an alloca containing the variable's value - DirectVariable { alloca: ValueRef }, - // The llptr given is an alloca containing the start of some pointer chain - // leading to the variable's content. - IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } -} + let member_llvm_types = slice_llvm_type.field_types(); + assert!(slice_layout_is_correct(cx, + &member_llvm_types[..], + element_type)); + let member_descriptions = [ + MemberDescription { + name: "data_ptr".to_string(), + llvm_type: member_llvm_types[0], + type_metadata: element_type_metadata, + offset: ComputedMemberOffset, + flags: FLAGS_NONE + }, + MemberDescription { + name: "length".to_string(), + llvm_type: member_llvm_types[1], + type_metadata: type_metadata(cx, cx.tcx().types.usize, span), + offset: ComputedMemberOffset, + flags: FLAGS_NONE + }, + ]; -enum VariableKind { - ArgumentVariable(usize /*index*/), - LocalVariable, - CapturedVariable, -} + assert!(member_descriptions.len() == member_llvm_types.len()); -/// Create any deferred debug metadata nodes -pub fn finalize(cx: &CrateContext) { - if cx.dbg_cx().is_none() { - return; - } + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, &loc.file.name); - debug!("finalize"); - let _ = compile_unit_metadata(cx); + let metadata = composite_type_metadata(cx, + slice_llvm_type, + &slice_type_name[..], + unique_type_id, + &member_descriptions, + UNKNOWN_SCOPE_METADATA, + file_metadata, + span); + return MetadataCreationResult::new(metadata, false); - if needs_gdb_debug_scripts_section(cx) { - // Add a .debug_gdb_scripts section to this compile-unit. This will - // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, - // which activates the Rust pretty printers for binary this section is - // contained in. - get_or_insert_gdb_debug_scripts_section_global(cx); + fn slice_layout_is_correct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + member_llvm_types: &[Type], + element_type: Ty<'tcx>) + -> bool { + member_llvm_types.len() == 2 && + member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && + member_llvm_types[1] == cx.int_type() } +} - unsafe { - llvm::LLVMDIBuilderFinalize(DIB(cx)); - llvm::LLVMDIBuilderDispose(DIB(cx)); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than OS X currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for OS X to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - if cx.sess().target.target.options.is_like_osx || - cx.sess().target.target.options.is_like_android { - llvm::LLVMRustAddModuleFlag(cx.llmod(), - "Dwarf Version\0".as_ptr() as *const _, - 2) - } +fn subroutine_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + unique_type_id: UniqueTypeId, + signature: &ty::PolyFnSig<'tcx>, + span: Span) + -> MetadataCreationResult +{ + let signature = ty::erase_late_bound_regions(cx.tcx(), signature); - // Prevent bitcode readers from deleting the debug info. - let ptr = "Debug Info Version\0".as_ptr(); - llvm::LLVMRustAddModuleFlag(cx.llmod(), ptr as *const _, - llvm::LLVMRustDebugMetadataVersion); - }; -} + let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs.len() + 1); -/// Creates debug information for the given global variable. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_global_var_metadata(cx: &CrateContext, - node_id: ast::NodeId, - global: ValueRef) { - if cx.dbg_cx().is_none() { - return; - } + // return type + signature_metadata.push(match signature.output { + ty::FnConverging(ret_ty) => match ret_ty.sty { + ty::ty_tup(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, ret_ty, span) + }, + ty::FnDiverging => diverging_type_metadata(cx) + }); - // Don't create debuginfo for globals inlined from other crates. The other - // crate should already contain debuginfo for it. More importantly, the - // global might not even exist in un-inlined form anywhere which would lead - // to a linker errors. - if cx.external_srcs().borrow().contains_key(&node_id) { - return; + // regular arguments + for &argument_type in &signature.inputs { + signature_metadata.push(type_metadata(cx, argument_type, span)); } - let var_item = cx.tcx().map.get(node_id); + return_if_metadata_created_in_meantime!(cx, unique_type_id); - let (name, span) = match var_item { - ast_map::NodeItem(item) => { - match item.node { - ast::ItemStatic(..) => (item.ident.name, item.span), - ast::ItemConst(..) => (item.ident.name, item.span), - _ => { - cx.sess() - .span_bug(item.span, - &format!("debuginfo::\ - create_global_var_metadata() - - Captured var-id refers to \ - unexpected ast_item variant: {:?}", - var_item)) - } - } + return MetadataCreationResult::new( + unsafe { + llvm::LLVMDIBuilderCreateSubroutineType( + DIB(cx), + UNKNOWN_FILE_METADATA, + create_DIArray(DIB(cx), &signature_metadata[..])) }, - _ => cx.sess().bug(&format!("debuginfo::create_global_var_metadata() \ - - Captured var-id refers to unexpected \ - ast_map variant: {:?}", - var_item)) - }; + false); +} - let (file_metadata, line_number) = if span != codemap::DUMMY_SP { - let loc = span_start(cx, span); - (file_metadata(cx, &loc.file.name), loc.line as c_uint) - } else { - (UNKNOWN_FILE_METADATA, UNKNOWN_LINE_NUMBER) +// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- +// defined concept. For the case of an actual trait pointer (i.e., Box, +// &Trait), trait_object_type should be the whole thing (e.g, Box) and +// trait_type should be the actual trait (e.g., Trait). Where the trait is part +// of a DST struct, there is no trait_object_type and the results of this +// function will be a little bit weird. +fn trait_pointer_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + trait_type: Ty<'tcx>, + trait_object_type: Option>, + unique_type_id: UniqueTypeId) + -> DIType { + // The implementation provided here is a stub. It makes sure that the trait + // type is assigned the correct name, size, namespace, and source location. + // But it does not describe the trait's methods. + + let def_id = match trait_type.sty { + ty::ty_trait(ref data) => data.principal_def_id(), + _ => { + let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type); + cx.sess().bug(&format!("debuginfo: Unexpected trait-object type in \ + trait_pointer_metadata(): {}", + &pp_type_name[..])); + } }; - let is_local_to_unit = is_node_local_to_unit(cx, node_id); - let variable_type = ty::node_id_to_type(cx.tcx(), node_id); - let type_metadata = type_metadata(cx, variable_type, span); - let namespace_node = namespace_for_item(cx, ast_util::local_def(node_id)); - let var_name = token::get_name(name).to_string(); - let linkage_name = - namespace_node.mangled_name_of_contained_item(&var_name[..]); - let var_scope = namespace_node.scope; + let trait_object_type = trait_object_type.unwrap_or(trait_type); + let trait_type_name = + compute_debuginfo_type_name(cx, trait_object_type, false); - let var_name = CString::new(var_name).unwrap(); - let linkage_name = CString::new(linkage_name).unwrap(); - unsafe { - llvm::LLVMDIBuilderCreateStaticVariable(DIB(cx), - var_scope, - var_name.as_ptr(), - linkage_name.as_ptr(), - file_metadata, - line_number, - type_metadata, - is_local_to_unit, - global, - ptr::null_mut()); - } -} + let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); -/// Creates debug information for the given local variable. -/// -/// This function assumes that there's a datum for each pattern component of the -/// local in `bcx.fcx.lllocals`. -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_local_var_metadata(bcx: Block, local: &ast::Local) { - if bcx.unreachable.get() || - fn_should_be_ignored(bcx.fcx) || - bcx.sess().opts.debuginfo != FullDebugInfo { - return; - } + let trait_llvm_type = type_of::type_of(cx, trait_object_type); - let cx = bcx.ccx(); - let def_map = &cx.tcx().def_map; - let locals = bcx.fcx.lllocals.borrow(); + composite_type_metadata(cx, + trait_llvm_type, + &trait_type_name[..], + unique_type_id, + &[], + containing_scope, + UNKNOWN_FILE_METADATA, + codemap::DUMMY_SP) +} - pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, var_ident| { - let datum = match locals.get(&node_id) { - Some(datum) => datum, +pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>, + usage_site_span: Span) + -> DIType { + // Get the unique type id of this type. + let unique_type_id = { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + // First, try to find the type in TypeMap. If we have seen it before, we + // can exit early here. + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + return metadata; + }, None => { - bcx.sess().span_bug(span, - &format!("no entry in lllocals table for {}", - node_id)); + // The Ty is not in the TypeMap but maybe we have already seen + // an equivalent type (e.g. only differing in region arguments). + // In order to find out, generate the unique type id and look + // that up. + let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); + match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => { + // There is already an equivalent type in the TypeMap. + // Register this Ty as an alias in the cache and + // return the cached metadata. + type_map.register_type_with_metadata(cx, t, metadata); + return metadata; + }, + None => { + // There really is no type metadata for this type, so + // proceed by creating it. + unique_type_id + } + } } - }; - - if unsafe { llvm::LLVMIsAAllocaInst(datum.val) } == ptr::null_mut() { - cx.sess().span_bug(span, "debuginfo::create_local_var_metadata() - \ - Referenced variable location is not an alloca!"); } + }; - let scope_metadata = scope_metadata(bcx.fcx, node_id, span); - - declare_local(bcx, - var_ident.node.name, - datum.ty, - scope_metadata, - DirectVariable { alloca: datum.val }, - LocalVariable, - span); - }) -} - -/// Creates debug information for a variable captured in a closure. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_captured_var_metadata<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - node_id: ast::NodeId, - env_pointer: ValueRef, - env_index: usize, - captured_by_ref: bool, - span: Span) { - if bcx.unreachable.get() || - fn_should_be_ignored(bcx.fcx) || - bcx.sess().opts.debuginfo != FullDebugInfo { - return; - } - - let cx = bcx.ccx(); - - let ast_item = cx.tcx().map.find(node_id); + debug!("type_metadata: {:?}", t); - let variable_name = match ast_item { - None => { - cx.sess().span_bug(span, "debuginfo::create_captured_var_metadata: node not found"); + let sty = &t.sty; + let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { + ty::ty_bool | + ty::ty_char | + ty::ty_int(_) | + ty::ty_uint(_) | + ty::ty_float(_) => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) } - Some(ast_map::NodeLocal(pat)) | Some(ast_map::NodeArg(pat)) => { - match pat.node { - ast::PatIdent(_, ref path1, _) => { - path1.node.name + ty::ty_tup(ref elements) if elements.is_empty() => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::ty_enum(def_id, _) => { + prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx) + } + ty::ty_vec(typ, len) => { + fixed_vec_metadata(cx, unique_type_id, typ, len.map(|x| x as u64), usage_site_span) + } + ty::ty_str => { + fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, None, usage_site_span) + } + ty::ty_trait(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, t, None, unique_type_id), + false) + } + ty::ty_uniq(ty) | ty::ty_ptr(ty::mt{ty, ..}) | ty::ty_rptr(_, ty::mt{ty, ..}) => { + match ty.sty { + ty::ty_vec(typ, None) => { + vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span) + } + ty::ty_str => { + vec_slice_metadata(cx, t, cx.tcx().types.u8, unique_type_id, usage_site_span) + } + ty::ty_trait(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, ty, Some(t), unique_type_id), + false) } _ => { - cx.sess() - .span_bug(span, - &format!( - "debuginfo::create_captured_var_metadata() - \ - Captured var-id refers to unexpected \ - ast_map variant: {:?}", - ast_item)); + let pointee_metadata = type_metadata(cx, ty, usage_site_span); + + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return metadata, + None => { /* proceed normally */ } + }; + + MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), + false) } } } + ty::ty_bare_fn(_, ref barefnty) => { + subroutine_type_metadata(cx, unique_type_id, &barefnty.sig, usage_site_span) + } + ty::ty_closure(def_id, substs) => { + let typer = NormalizingClosureTyper::new(cx.tcx()); + let sig = typer.closure_type(def_id, substs).sig; + subroutine_type_metadata(cx, unique_type_id, &sig, usage_site_span) + } + ty::ty_struct(def_id, substs) => { + prepare_struct_metadata(cx, + t, + def_id, + substs, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::ty_tup(ref elements) => { + prepare_tuple_metadata(cx, + t, + &elements[..], + unique_type_id, + usage_site_span).finalize(cx) + } _ => { - cx.sess() - .span_bug(span, - &format!("debuginfo::create_captured_var_metadata() - \ - Captured var-id refers to unexpected \ - ast_map variant: {:?}", - ast_item)); + cx.sess().bug(&format!("debuginfo: unexpected type in type_metadata: {:?}", + sty)) } }; - let variable_type = common::node_id_type(bcx, node_id); - let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata; + { + let mut type_map = debug_context(cx).type_map.borrow_mut(); - // env_pointer is the alloca containing the pointer to the environment, - // so it's type is **EnvironmentType. In order to find out the type of - // the environment we have to "dereference" two times. - let llvm_env_data_type = common::val_ty(env_pointer).element_type() - .element_type(); - let byte_offset_of_var_in_env = machine::llelement_offset(cx, - llvm_env_data_type, - env_index); - - let address_operations = unsafe { - [llvm::LLVMDIBuilderCreateOpDeref(), - llvm::LLVMDIBuilderCreateOpPlus(), - byte_offset_of_var_in_env as i64, - llvm::LLVMDIBuilderCreateOpDeref()] - }; - - let address_op_count = if captured_by_ref { - address_operations.len() - } else { - address_operations.len() - 1 - }; - - let variable_access = IndirectVariable { - alloca: env_pointer, - address_operations: &address_operations[..address_op_count] - }; - - declare_local(bcx, - variable_name, - variable_type, - scope_metadata, - variable_access, - CapturedVariable, - span); -} - -/// Creates debug information for a local variable introduced in the head of a -/// match-statement arm. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_match_binding_metadata<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - variable_name: ast::Name, - binding: BindingInfo<'tcx>) { - if bcx.unreachable.get() || - fn_should_be_ignored(bcx.fcx) || - bcx.sess().opts.debuginfo != FullDebugInfo { - return; - } + if already_stored_in_typemap { + // Also make sure that we already have a TypeMap entry entry for the unique type id. + let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => metadata, + None => { + let unique_type_id_str = + type_map.get_unique_type_id_as_string(unique_type_id); + let error_message = format!("Expected type metadata for unique \ + type id '{}' to already be in \ + the debuginfo::TypeMap but it \ + was not. (Ty = {})", + &unique_type_id_str[..], + ppaux::ty_to_string(cx.tcx(), t)); + cx.sess().span_bug(usage_site_span, &error_message[..]); + } + }; - let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span); - let aops = unsafe { - [llvm::LLVMDIBuilderCreateOpDeref()] - }; - // Regardless of the actual type (`T`) we're always passed the stack slot (alloca) - // for the binding. For ByRef bindings that's a `T*` but for ByMove bindings we - // actually have `T**`. So to get the actual variable we need to dereference once - // more. For ByCopy we just use the stack slot we created for the binding. - let var_access = match binding.trmode { - TrByCopy(llbinding) => DirectVariable { - alloca: llbinding - }, - TrByMove => IndirectVariable { - alloca: binding.llmatch, - address_operations: &aops - }, - TrByRef => DirectVariable { - alloca: binding.llmatch + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + if metadata != metadata_for_uid { + let unique_type_id_str = + type_map.get_unique_type_id_as_string(unique_type_id); + let error_message = format!("Mismatch between Ty and \ + UniqueTypeId maps in \ + debuginfo::TypeMap. \ + UniqueTypeId={}, Ty={}", + &unique_type_id_str[..], + ppaux::ty_to_string(cx.tcx(), t)); + cx.sess().span_bug(usage_site_span, &error_message[..]); + } + } + None => { + type_map.register_type_with_metadata(cx, t, metadata); + } + } + } else { + type_map.register_type_with_metadata(cx, t, metadata); + type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata); } - }; + } - declare_local(bcx, - variable_name, - binding.ty, - scope_metadata, - var_access, - LocalVariable, - binding.span); + metadata } -/// Creates debug information for the given function argument. -/// -/// This function assumes that there's a datum for each pattern component of the -/// argument in `bcx.fcx.lllocals`. -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_argument_metadata(bcx: Block, arg: &ast::Arg) { - if bcx.unreachable.get() || - fn_should_be_ignored(bcx.fcx) || - bcx.sess().opts.debuginfo != FullDebugInfo { - return; +pub fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile { + match debug_context(cx).created_files.borrow().get(full_path) { + Some(file_metadata) => return *file_metadata, + None => () } - let def_map = &bcx.tcx().def_map; - let scope_metadata = bcx - .fcx - .debug_context - .get_ref(bcx.ccx(), arg.pat.span) - .fn_metadata; - let locals = bcx.fcx.lllocals.borrow(); - - pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, var_ident| { - let datum = match locals.get(&node_id) { - Some(v) => v, - None => { - bcx.sess().span_bug(span, - &format!("no entry in lllocals table for {}", - node_id)); - } - }; - - if unsafe { llvm::LLVMIsAAllocaInst(datum.val) } == ptr::null_mut() { - bcx.sess().span_bug(span, "debuginfo::create_argument_metadata() - \ - Referenced variable location is not an alloca!"); - } + debug!("file_metadata: {}", full_path); - let argument_index = { - let counter = &bcx - .fcx - .debug_context - .get_ref(bcx.ccx(), span) - .argument_counter; - let argument_index = counter.get(); - counter.set(argument_index + 1); - argument_index + // FIXME (#9639): This needs to handle non-utf8 paths + let work_dir = cx.sess().working_dir.to_str().unwrap(); + let file_name = + if full_path.starts_with(work_dir) { + &full_path[work_dir.len() + 1..full_path.len()] + } else { + full_path }; - declare_local(bcx, - var_ident.node.name, - datum.ty, - scope_metadata, - DirectVariable { alloca: datum.val }, - ArgumentVariable(argument_index), - span); - }) -} - -pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - node_id: ast::NodeId, - node_span: Span, - is_block: bool) - -> NodeIdAndSpan { - // A debug location needs two things: - // (1) A span (of which only the beginning will actually be used) - // (2) An AST node-id which will be used to look up the lexical scope - // for the location in the functions scope-map - // - // This function will calculate the debug location for compiler-generated - // cleanup calls that are executed when control-flow leaves the - // scope identified by `node_id`. - // - // For everything but block-like things we can simply take id and span of - // the given expression, meaning that from a debugger's view cleanup code is - // executed at the same source location as the statement/expr itself. - // - // Blocks are a special case. Here we want the cleanup to be linked to the - // closing curly brace of the block. The *scope* the cleanup is executed in - // is up to debate: It could either still be *within* the block being - // cleaned up, meaning that locals from the block are still visible in the - // debugger. - // Or it could be in the scope that the block is contained in, so any locals - // from within the block are already considered out-of-scope and thus not - // accessible in the debugger anymore. - // - // The current implementation opts for the second option: cleanup of a block - // already happens in the parent scope of the block. The main reason for - // this decision is that scoping becomes controlflow dependent when variable - // shadowing is involved and it's impossible to decide statically which - // scope is actually left when the cleanup code is executed. - // In practice it shouldn't make much of a difference. - - let mut cleanup_span = node_span; - - if is_block { - // Not all blocks actually have curly braces (e.g. simple closure - // bodies), in which case we also just want to return the span of the - // whole expression. - let code_snippet = cx.sess().codemap().span_to_snippet(node_span); - if let Ok(code_snippet) = code_snippet { - let bytes = code_snippet.as_bytes(); - - if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" { - cleanup_span = Span { - lo: node_span.hi - codemap::BytePos(1), - hi: node_span.hi, - expn_id: node_span.expn_id - }; - } - } - } + let file_name = CString::new(file_name).unwrap(); + let work_dir = CString::new(work_dir).unwrap(); + let file_metadata = unsafe { + llvm::LLVMDIBuilderCreateFile(DIB(cx), file_name.as_ptr(), + work_dir.as_ptr()) + }; - NodeIdAndSpan { - id: node_id, - span: cleanup_span - } + let mut created_files = debug_context(cx).created_files.borrow_mut(); + created_files.insert(full_path.to_string(), file_metadata); + return file_metadata; } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum DebugLoc { - At(ast::NodeId, Span), - None -} +/// Finds the scope metadata node for the given AST node. +pub fn scope_metadata(fcx: &FunctionContext, + node_id: ast::NodeId, + error_reporting_span: Span) + -> DIScope { + let scope_map = &fcx.debug_context + .get_ref(fcx.ccx, error_reporting_span) + .scope_map; + match scope_map.borrow().get(&node_id).cloned() { + Some(scope_metadata) => scope_metadata, + None => { + let node = fcx.ccx.tcx().map.get(node_id); -impl DebugLoc { - pub fn apply(&self, fcx: &FunctionContext) { - match *self { - DebugLoc::At(node_id, span) => { - set_source_location(fcx, node_id, span); - } - DebugLoc::None => { - clear_source_location(fcx); - } + fcx.ccx.sess().span_bug(error_reporting_span, + &format!("debuginfo: Could not find scope info for node {:?}", + node)); } } } -pub trait ToDebugLoc { - fn debug_loc(&self) -> DebugLoc; -} - -impl ToDebugLoc for ast::Expr { - fn debug_loc(&self) -> DebugLoc { - DebugLoc::At(self.id, self.span) - } -} - -impl ToDebugLoc for NodeIdAndSpan { - fn debug_loc(&self) -> DebugLoc { - DebugLoc::At(self.id, self.span) - } -} - -impl ToDebugLoc for Option { - fn debug_loc(&self) -> DebugLoc { - match *self { - Some(NodeIdAndSpan { id, span }) => DebugLoc::At(id, span), - None => DebugLoc::None - } +fn diverging_type_metadata(cx: &CrateContext) -> DIType { + unsafe { + llvm::LLVMDIBuilderCreateBasicType( + DIB(cx), + "!\0".as_ptr() as *const _, + bytes_to_bits(0), + bytes_to_bits(0), + DW_ATE_unsigned) } } -/// Sets the current debug location at the beginning of the span. -/// -/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id -/// parameter is used to reliably find the correct visibility scope for the code -/// position. -pub fn set_source_location(fcx: &FunctionContext, - node_id: ast::NodeId, - span: Span) { - match fcx.debug_context { - FunctionDebugContext::DebugInfoDisabled => return, - FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, UnknownLocation); - return; - } - FunctionDebugContext::RegularContext(box ref function_debug_context) => { - if function_debug_context.source_location_override.get() { - // Just ignore any attempts to set a new debug location while - // the override is active. - return; - } +fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>) -> DIType { - let cx = fcx.ccx; + debug!("basic_type_metadata: {:?}", t); - debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); + let (name, encoding) = match t.sty { + ty::ty_tup(ref elements) if elements.is_empty() => + ("()".to_string(), DW_ATE_unsigned), + ty::ty_bool => ("bool".to_string(), DW_ATE_boolean), + ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char), + ty::ty_int(int_ty) => match int_ty { + ast::TyIs => ("isize".to_string(), DW_ATE_signed), + ast::TyI8 => ("i8".to_string(), DW_ATE_signed), + ast::TyI16 => ("i16".to_string(), DW_ATE_signed), + ast::TyI32 => ("i32".to_string(), DW_ATE_signed), + ast::TyI64 => ("i64".to_string(), DW_ATE_signed) + }, + ty::ty_uint(uint_ty) => match uint_ty { + ast::TyUs => ("usize".to_string(), DW_ATE_unsigned), + ast::TyU8 => ("u8".to_string(), DW_ATE_unsigned), + ast::TyU16 => ("u16".to_string(), DW_ATE_unsigned), + ast::TyU32 => ("u32".to_string(), DW_ATE_unsigned), + ast::TyU64 => ("u64".to_string(), DW_ATE_unsigned) + }, + ty::ty_float(float_ty) => match float_ty { + ast::TyF32 => ("f32".to_string(), DW_ATE_float), + ast::TyF64 => ("f64".to_string(), DW_ATE_float), + }, + _ => cx.sess().bug("debuginfo::basic_type_metadata - t is invalid type") + }; - if function_debug_context.source_locations_enabled.get() { - let loc = span_start(cx, span); - let scope = scope_metadata(fcx, node_id, span); + let llvm_type = type_of::type_of(cx, t); + let (size, align) = size_and_align_of(cx, llvm_type); + let name = CString::new(name).unwrap(); + let ty_metadata = unsafe { + llvm::LLVMDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr(), + bytes_to_bits(size), + bytes_to_bits(align), + encoding) + }; - set_debug_location(cx, InternalDebugLocation::new(scope, - loc.line, - loc.col.to_usize())); - } else { - set_debug_location(cx, UnknownLocation); - } - } - } + return ty_metadata; } -/// This function makes sure that all debug locations emitted while executing -/// `wrapped_function` are set to the given `debug_loc`. -pub fn with_source_location_override(fcx: &FunctionContext, - debug_loc: DebugLoc, - wrapped_function: F) -> R - where F: FnOnce() -> R -{ - match fcx.debug_context { - FunctionDebugContext::DebugInfoDisabled => { - wrapped_function() - } - FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(fcx.ccx, UnknownLocation); - wrapped_function() - } - FunctionDebugContext::RegularContext(box ref function_debug_context) => { - if function_debug_context.source_location_override.get() { - wrapped_function() - } else { - debug_loc.apply(fcx); - function_debug_context.source_location_override.set(true); - let result = wrapped_function(); - function_debug_context.source_location_override.set(false); - result - } - } - } -} - -/// Clears the current debug location. -/// -/// Instructions generated hereafter won't be assigned a source location. -pub fn clear_source_location(fcx: &FunctionContext) { - if fn_should_be_ignored(fcx) { - return; - } - - set_debug_location(fcx.ccx, UnknownLocation); -} - -/// Enables emitting source locations for the given functions. -/// -/// Since we don't want source locations to be emitted for the function prelude, -/// they are disabled when beginning to translate a new function. This functions -/// switches source location emitting on and must therefore be called before the -/// first real statement/expression of the function is translated. -pub fn start_emitting_source_locations(fcx: &FunctionContext) { - match fcx.debug_context { - FunctionDebugContext::RegularContext(box ref data) => { - data.source_locations_enabled.set(true) - }, - _ => { /* safe to ignore */ } - } -} - -/// Creates the function-specific debug context. -/// -/// Returns the FunctionDebugContext for the function which holds state needed -/// for debug info creation. The function may also return another variant of the -/// FunctionDebugContext enum which indicates why no debuginfo should be created -/// for the function. -pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - fn_ast_id: ast::NodeId, - param_substs: &Substs<'tcx>, - llfn: ValueRef) -> FunctionDebugContext { - if cx.sess().opts.debuginfo == NoDebugInfo { - return FunctionDebugContext::DebugInfoDisabled; - } - - // Clear the debug location so we don't assign them in the function prelude. - // Do this here already, in case we do an early exit from this function. - set_debug_location(cx, UnknownLocation); - - if fn_ast_id == ast::DUMMY_NODE_ID { - // This is a function not linked to any source location, so don't - // generate debuginfo for it. - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - let empty_generics = ast_util::empty_generics(); - - let fnitem = cx.tcx().map.get(fn_ast_id); - - let (name, fn_decl, generics, top_level_block, span, has_path) = match fnitem { - ast_map::NodeItem(ref item) => { - if contains_nodebug_attribute(&item.attrs) { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - match item.node { - ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => { - (item.ident.name, fn_decl, generics, top_level_block, item.span, true) - } - _ => { - cx.sess().span_bug(item.span, - "create_function_debug_context: item bound to non-function"); - } - } - } - ast_map::NodeImplItem(impl_item) => { - match impl_item.node { - ast::MethodImplItem(ref sig, ref body) => { - if contains_nodebug_attribute(&impl_item.attrs) { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - (impl_item.ident.name, - &sig.decl, - &sig.generics, - body, - impl_item.span, - true) - } - _ => { - cx.sess().span_bug(impl_item.span, - "create_function_debug_context() \ - called on non-method impl item?!") - } - } - } - ast_map::NodeExpr(ref expr) => { - match expr.node { - ast::ExprClosure(_, ref fn_decl, ref top_level_block) => { - let name = format!("fn{}", token::gensym("fn")); - let name = token::intern(&name[..]); - (name, fn_decl, - // This is not quite right. It should actually inherit - // the generics of the enclosing function. - &empty_generics, - top_level_block, - expr.span, - // Don't try to lookup the item path: - false) - } - _ => cx.sess().span_bug(expr.span, - "create_function_debug_context: expected an expr_fn_block here") - } - } - ast_map::NodeTraitItem(trait_item) => { - match trait_item.node { - ast::MethodTraitItem(ref sig, Some(ref body)) => { - if contains_nodebug_attribute(&trait_item.attrs) { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - (trait_item.ident.name, - &sig.decl, - &sig.generics, - body, - trait_item.span, - true) - } - _ => { - cx.sess() - .bug(&format!("create_function_debug_context: \ - unexpected sort of node: {:?}", - fnitem)) - } - } - } - ast_map::NodeForeignItem(..) | - ast_map::NodeVariant(..) | - ast_map::NodeStructCtor(..) => { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - _ => cx.sess().bug(&format!("create_function_debug_context: \ - unexpected sort of node: {:?}", - fnitem)) - }; - - // This can be the case for functions inlined from another crate - if span == codemap::DUMMY_SP { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name); - - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(cx, - fn_ast_id, - &*fn_decl, - param_substs, - span); - llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) - }; - - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let mut function_name = String::from_str(&token::get_name(name)); - let template_parameters = get_template_parameters(cx, - generics, - param_substs, - file_metadata, - &mut function_name); - - // There is no ast_map::Path for ast::ExprClosure-type functions. For now, - // just don't put them into a namespace. In the future this could be improved - // somehow (storing a path in the ast_map, or construct a path using the - // enclosing function). - let (linkage_name, containing_scope) = if has_path { - let namespace_node = namespace_for_item(cx, ast_util::local_def(fn_ast_id)); - let linkage_name = namespace_node.mangled_name_of_contained_item( - &function_name[..]); - let containing_scope = namespace_node.scope; - (linkage_name, containing_scope) - } else { - (function_name.clone(), file_metadata) - }; - - // Clang sets this parameter to the opening brace of the function's block, - // so let's do this too. - let scope_line = span_start(cx, top_level_block.span).line; - - let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id); - - let function_name = CString::new(function_name).unwrap(); - let linkage_name = CString::new(linkage_name).unwrap(); - let fn_metadata = unsafe { - llvm::LLVMDIBuilderCreateFunction( +fn pointer_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + pointer_type: Ty<'tcx>, + pointee_type_metadata: DIType) + -> DIType { + let pointer_llvm_type = type_of::type_of(cx, pointer_type); + let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); + let name = compute_debuginfo_type_name(cx, pointer_type, false); + let name = CString::new(name).unwrap(); + let ptr_metadata = unsafe { + llvm::LLVMDIBuilderCreatePointerType( DIB(cx), - containing_scope, - function_name.as_ptr(), - linkage_name.as_ptr(), - file_metadata, - loc.line as c_uint, - function_type_metadata, - is_local_to_unit, - true, - scope_line as c_uint, - FlagPrototyped as c_uint, - cx.sess().opts.optimize != config::No, - llfn, - template_parameters, - ptr::null_mut()) - }; - - let scope_map = create_scope_map(cx, - &fn_decl.inputs, - &*top_level_block, - fn_metadata, - fn_ast_id); - - // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = box FunctionDebugContextData { - scope_map: RefCell::new(scope_map), - fn_metadata: fn_metadata, - argument_counter: Cell::new(1), - source_locations_enabled: Cell::new(false), - source_location_override: Cell::new(false), - }; - - - - return FunctionDebugContext::RegularContext(fn_debug_context); - - fn get_function_signature<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - fn_ast_id: ast::NodeId, - fn_decl: &ast::FnDecl, - param_substs: &Substs<'tcx>, - error_reporting_span: Span) -> DIArray { - if cx.sess().opts.debuginfo == LimitedDebugInfo { - return create_DIArray(DIB(cx), &[]); - } - - let mut signature = Vec::with_capacity(fn_decl.inputs.len() + 1); - - // Return type -- llvm::DIBuilder wants this at index 0 - assert_type_for_node_id(cx, fn_ast_id, error_reporting_span); - let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id); - let return_type = monomorphize::apply_param_substs(cx.tcx(), - param_substs, - &return_type); - if ty::type_is_nil(return_type) { - signature.push(ptr::null_mut()) - } else { - signature.push(type_metadata(cx, return_type, codemap::DUMMY_SP)); - } - - // Arguments types - for arg in &fn_decl.inputs { - assert_type_for_node_id(cx, arg.pat.id, arg.pat.span); - let arg_type = ty::node_id_to_type(cx.tcx(), arg.pat.id); - let arg_type = monomorphize::apply_param_substs(cx.tcx(), - param_substs, - &arg_type); - signature.push(type_metadata(cx, arg_type, codemap::DUMMY_SP)); - } - - return create_DIArray(DIB(cx), &signature[..]); - } - - fn get_template_parameters<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - generics: &ast::Generics, - param_substs: &Substs<'tcx>, - file_metadata: DIFile, - name_to_append_suffix_to: &mut String) - -> DIArray - { - let self_type = param_substs.self_ty(); - let self_type = monomorphize::normalize_associated_type(cx.tcx(), &self_type); - - // Only true for static default methods: - let has_self_type = self_type.is_some(); - - if !generics.is_type_parameterized() && !has_self_type { - return create_DIArray(DIB(cx), &[]); - } - - name_to_append_suffix_to.push('<'); - - // The list to be filled with template parameters: - let mut template_params: Vec = - Vec::with_capacity(generics.ty_params.len() + 1); - - // Handle self type - if has_self_type { - let actual_self_type = self_type.unwrap(); - // Add self type name to <...> clause of function name - let actual_self_type_name = compute_debuginfo_type_name( - cx, - actual_self_type, - true); - - name_to_append_suffix_to.push_str(&actual_self_type_name[..]); - - if generics.is_type_parameterized() { - name_to_append_suffix_to.push_str(","); - } - - // Only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == FullDebugInfo { - let actual_self_type_metadata = type_metadata(cx, - actual_self_type, - codemap::DUMMY_SP); - - let name = token::get_name(special_idents::type_self.name); - - let name = CString::new(name.as_bytes()).unwrap(); - let param_metadata = unsafe { - llvm::LLVMDIBuilderCreateTemplateTypeParameter( - DIB(cx), - file_metadata, - name.as_ptr(), - actual_self_type_metadata, - ptr::null_mut(), - 0, - 0) - }; - - template_params.push(param_metadata); - } - } - - // Handle other generic parameters - let actual_types = param_substs.types.get_slice(subst::FnSpace); - for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() { - let actual_type = actual_types[index]; - // Add actual type name to <...> clause of function name - let actual_type_name = compute_debuginfo_type_name(cx, - actual_type, - true); - name_to_append_suffix_to.push_str(&actual_type_name[..]); - - if index != generics.ty_params.len() - 1 { - name_to_append_suffix_to.push_str(","); - } - - // Again, only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == FullDebugInfo { - let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP); - let ident = token::get_ident(ident); - let name = CString::new(ident.as_bytes()).unwrap(); - let param_metadata = unsafe { - llvm::LLVMDIBuilderCreateTemplateTypeParameter( - DIB(cx), - file_metadata, - name.as_ptr(), - actual_type_metadata, - ptr::null_mut(), - 0, - 0) - }; - template_params.push(param_metadata); - } - } - - name_to_append_suffix_to.push('>'); - - return create_DIArray(DIB(cx), &template_params[..]); - } -} - -//=----------------------------------------------------------------------------- -// Module-Internal debug info creation functions -//=----------------------------------------------------------------------------- - -fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool -{ - // The is_local_to_unit flag indicates whether a function is local to the - // current compilation unit (i.e. if it is *static* in the C-sense). The - // *reachable* set should provide a good approximation of this, as it - // contains everything that might leak out of the current crate (by being - // externally visible or by being inlined into something externally visible). - // It might better to use the `exported_items` set from `driver::CrateAnalysis` - // in the future, but (atm) this set is not available in the translation pass. - !cx.reachable().contains(&node_id) -} - -#[allow(non_snake_case)] -fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { - return unsafe { - llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) + pointee_type_metadata, + bytes_to_bits(pointer_size), + bytes_to_bits(pointer_align), + name.as_ptr()) }; + return ptr_metadata; } -fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor { +pub fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor { let work_dir = &cx.sess().working_dir; let compile_unit_name = match cx.sess().local_crate_source_file { None => fallback_path(cx), @@ -1667,500 +1046,230 @@ fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor { } } -fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - variable_name: ast::Name, - variable_type: Ty<'tcx>, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { - let cx: &CrateContext = bcx.ccx(); +struct MetadataCreationResult { + metadata: DIType, + already_stored_in_typemap: bool +} - let filename = span_start(cx, span).file.name.clone(); - let file_metadata = file_metadata(cx, &filename[..]); +impl MetadataCreationResult { + fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { + MetadataCreationResult { + metadata: metadata, + already_stored_in_typemap: already_stored_in_typemap + } + } +} - let name = token::get_name(variable_name); - let loc = span_start(cx, span); - let type_metadata = type_metadata(cx, variable_type, span); +enum MemberOffset { + FixedMemberOffset { bytes: usize }, + // For ComputedMemberOffset, the offset is read from the llvm type definition. + ComputedMemberOffset +} - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable | - CapturedVariable => (0, DW_TAG_auto_variable) - }; +// Description of a type member, which can either be a regular field (as in +// structs or tuples) or an enum variant. +struct MemberDescription { + name: String, + llvm_type: Type, + type_metadata: DIType, + offset: MemberOffset, + flags: c_uint +} - let name = CString::new(name.as_bytes()).unwrap(); - match (variable_access, &[][..]) { - (DirectVariable { alloca }, address_operations) | - (IndirectVariable {alloca, address_operations}, _) => { - let metadata = unsafe { - llvm::LLVMDIBuilderCreateVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name.as_ptr(), - file_metadata, - loc.line as c_uint, - type_metadata, - cx.sess().opts.optimize != config::No, - 0, - address_operations.as_ptr(), - address_operations.len() as c_uint, - argument_index) - }; - set_debug_location(cx, InternalDebugLocation::new(scope_metadata, - loc.line, - loc.col.to_usize())); - unsafe { - let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( - DIB(cx), - alloca, - metadata, - address_operations.as_ptr(), - address_operations.len() as c_uint, - bcx.llbb); +// A factory for MemberDescriptions. It produces a list of member descriptions +// for some record-like type. MemberDescriptionFactories are used to defer the +// creation of type member descriptions in order to break cycles arising from +// recursive type definitions. +enum MemberDescriptionFactory<'tcx> { + StructMDF(StructMemberDescriptionFactory<'tcx>), + TupleMDF(TupleMemberDescriptionFactory<'tcx>), + EnumMDF(EnumMemberDescriptionFactory<'tcx>), + VariantMDF(VariantMemberDescriptionFactory<'tcx>) +} - llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); +impl<'tcx> MemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) + -> Vec { + match *self { + StructMDF(ref this) => { + this.create_member_descriptions(cx) + } + TupleMDF(ref this) => { + this.create_member_descriptions(cx) + } + EnumMDF(ref this) => { + this.create_member_descriptions(cx) + } + VariantMDF(ref this) => { + this.create_member_descriptions(cx) } } } - - match variable_kind { - ArgumentVariable(_) | CapturedVariable => { - assert!(!bcx.fcx - .debug_context - .get_ref(cx, span) - .source_locations_enabled - .get()); - set_debug_location(cx, UnknownLocation); - } - _ => { /* nothing to do */ } - } } -fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile { - match debug_context(cx).created_files.borrow().get(full_path) { - Some(file_metadata) => return *file_metadata, - None => () - } +//=----------------------------------------------------------------------------- +// Structs +//=----------------------------------------------------------------------------- - debug!("file_metadata: {}", full_path); +// Creates MemberDescriptions for the fields of a struct +struct StructMemberDescriptionFactory<'tcx> { + fields: Vec>, + is_simd: bool, + span: Span, +} - // FIXME (#9639): This needs to handle non-utf8 paths - let work_dir = cx.sess().working_dir.to_str().unwrap(); - let file_name = - if full_path.starts_with(work_dir) { - &full_path[work_dir.len() + 1..full_path.len()] +impl<'tcx> StructMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) + -> Vec { + if self.fields.is_empty() { + return Vec::new(); + } + + let field_size = if self.is_simd { + machine::llsize_of_alloc(cx, type_of::type_of(cx, self.fields[0].mt.ty)) as usize } else { - full_path + 0xdeadbeef }; - let file_name = CString::new(file_name).unwrap(); - let work_dir = CString::new(work_dir).unwrap(); - let file_metadata = unsafe { - llvm::LLVMDIBuilderCreateFile(DIB(cx), file_name.as_ptr(), - work_dir.as_ptr()) - }; + self.fields.iter().enumerate().map(|(i, field)| { + let name = if field.name == special_idents::unnamed_field.name { + format!("__{}", i) + } else { + token::get_name(field.name).to_string() + }; - let mut created_files = debug_context(cx).created_files.borrow_mut(); - created_files.insert(full_path.to_string(), file_metadata); - return file_metadata; -} + let offset = if self.is_simd { + assert!(field_size != 0xdeadbeef); + FixedMemberOffset { bytes: i * field_size } + } else { + ComputedMemberOffset + }; -/// Finds the scope metadata node for the given AST node. -fn scope_metadata(fcx: &FunctionContext, - node_id: ast::NodeId, - error_reporting_span: Span) - -> DIScope { - let scope_map = &fcx.debug_context - .get_ref(fcx.ccx, error_reporting_span) - .scope_map; - match scope_map.borrow().get(&node_id).cloned() { - Some(scope_metadata) => scope_metadata, - None => { - let node = fcx.ccx.tcx().map.get(node_id); - - fcx.ccx.sess().span_bug(error_reporting_span, - &format!("debuginfo: Could not find scope info for node {:?}", - node)); - } + MemberDescription { + name: name, + llvm_type: type_of::type_of(cx, field.mt.ty), + type_metadata: type_metadata(cx, field.mt.ty, self.span), + offset: offset, + flags: FLAGS_NONE, + } + }).collect() } } -fn diverging_type_metadata(cx: &CrateContext) -> DIType { - unsafe { - llvm::LLVMDIBuilderCreateBasicType( - DIB(cx), - "!\0".as_ptr() as *const _, - bytes_to_bits(0), - bytes_to_bits(0), - DW_ATE_unsigned) - } -} -fn basic_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>) -> DIType { +fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + struct_type: Ty<'tcx>, + def_id: ast::DefId, + substs: &subst::Substs<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let struct_name = compute_debuginfo_type_name(cx, struct_type, false); + let struct_llvm_type = type_of::type_of(cx, struct_type); - debug!("basic_type_metadata: {:?}", t); + let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - let (name, encoding) = match t.sty { - ty::ty_tup(ref elements) if elements.is_empty() => - ("()".to_string(), DW_ATE_unsigned), - ty::ty_bool => ("bool".to_string(), DW_ATE_boolean), - ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char), - ty::ty_int(int_ty) => match int_ty { - ast::TyIs => ("isize".to_string(), DW_ATE_signed), - ast::TyI8 => ("i8".to_string(), DW_ATE_signed), - ast::TyI16 => ("i16".to_string(), DW_ATE_signed), - ast::TyI32 => ("i32".to_string(), DW_ATE_signed), - ast::TyI64 => ("i64".to_string(), DW_ATE_signed) - }, - ty::ty_uint(uint_ty) => match uint_ty { - ast::TyUs => ("usize".to_string(), DW_ATE_unsigned), - ast::TyU8 => ("u8".to_string(), DW_ATE_unsigned), - ast::TyU16 => ("u16".to_string(), DW_ATE_unsigned), - ast::TyU32 => ("u32".to_string(), DW_ATE_unsigned), - ast::TyU64 => ("u64".to_string(), DW_ATE_unsigned) - }, - ty::ty_float(float_ty) => match float_ty { - ast::TyF32 => ("f32".to_string(), DW_ATE_float), - ast::TyF64 => ("f64".to_string(), DW_ATE_float), - }, - _ => cx.sess().bug("debuginfo::basic_type_metadata - t is invalid type") - }; + let struct_metadata_stub = create_struct_stub(cx, + struct_llvm_type, + &struct_name[..], + unique_type_id, + containing_scope); - let llvm_type = type_of::type_of(cx, t); - let (size, align) = size_and_align_of(cx, llvm_type); - let name = CString::new(name).unwrap(); - let ty_metadata = unsafe { - llvm::LLVMDIBuilderCreateBasicType( - DIB(cx), - name.as_ptr(), - bytes_to_bits(size), - bytes_to_bits(align), - encoding) - }; + let mut fields = ty::struct_fields(cx.tcx(), def_id, substs); - return ty_metadata; -} + // The `Ty` values returned by `ty::struct_fields` can still contain + // `ty_projection` variants, so normalize those away. + for field in &mut fields { + field.mt.ty = monomorphize::normalize_associated_type(cx.tcx(), &field.mt.ty); + } -fn pointer_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - pointer_type: Ty<'tcx>, - pointee_type_metadata: DIType) - -> DIType { - let pointer_llvm_type = type_of::type_of(cx, pointer_type); - let (pointer_size, pointer_align) = size_and_align_of(cx, pointer_llvm_type); - let name = compute_debuginfo_type_name(cx, pointer_type, false); - let name = CString::new(name).unwrap(); - let ptr_metadata = unsafe { - llvm::LLVMDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - bytes_to_bits(pointer_size), - bytes_to_bits(pointer_align), - name.as_ptr()) - }; - return ptr_metadata; + create_and_register_recursive_type_forward_declaration( + cx, + struct_type, + unique_type_id, + struct_metadata_stub, + struct_llvm_type, + StructMDF(StructMemberDescriptionFactory { + fields: fields, + is_simd: ty::type_is_simd(cx.tcx(), struct_type), + span: span, + }) + ) } + //=----------------------------------------------------------------------------- -// Common facilities for record-like types (structs, enums, tuples) +// Tuples //=----------------------------------------------------------------------------- -enum MemberOffset { - FixedMemberOffset { bytes: usize }, - // For ComputedMemberOffset, the offset is read from the llvm type definition - ComputedMemberOffset -} - -// Description of a type member, which can either be a regular field (as in -// structs or tuples) or an enum variant -struct MemberDescription { - name: String, - llvm_type: Type, - type_metadata: DIType, - offset: MemberOffset, - flags: c_uint -} - -// A factory for MemberDescriptions. It produces a list of member descriptions -// for some record-like type. MemberDescriptionFactories are used to defer the -// creation of type member descriptions in order to break cycles arising from -// recursive type definitions. -enum MemberDescriptionFactory<'tcx> { - StructMDF(StructMemberDescriptionFactory<'tcx>), - TupleMDF(TupleMemberDescriptionFactory<'tcx>), - EnumMDF(EnumMemberDescriptionFactory<'tcx>), - VariantMDF(VariantMemberDescriptionFactory<'tcx>) +// Creates MemberDescriptions for the fields of a tuple +struct TupleMemberDescriptionFactory<'tcx> { + component_types: Vec>, + span: Span, } -impl<'tcx> MemberDescriptionFactory<'tcx> { +impl<'tcx> TupleMemberDescriptionFactory<'tcx> { fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> Vec { - match *self { - StructMDF(ref this) => { - this.create_member_descriptions(cx) - } - TupleMDF(ref this) => { - this.create_member_descriptions(cx) - } - EnumMDF(ref this) => { - this.create_member_descriptions(cx) - } - VariantMDF(ref this) => { - this.create_member_descriptions(cx) + self.component_types + .iter() + .enumerate() + .map(|(i, &component_type)| { + MemberDescription { + name: format!("__{}", i), + llvm_type: type_of::type_of(cx, component_type), + type_metadata: type_metadata(cx, component_type, self.span), + offset: ComputedMemberOffset, + flags: FLAGS_NONE, } - } + }).collect() } } -// A description of some recursive type. It can either be already finished (as -// with FinalMetadata) or it is not yet finished, but contains all information -// needed to generate the missing parts of the description. See the documentation -// section on Recursive Types at the top of this file for more information. -enum RecursiveTypeDescription<'tcx> { - UnfinishedMetadata { - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - llvm_type: Type, - member_description_factory: MemberDescriptionFactory<'tcx>, - }, - FinalMetadata(DICompositeType) +fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + tuple_type: Ty<'tcx>, + component_types: &[Ty<'tcx>], + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); + let tuple_llvm_type = type_of::type_of(cx, tuple_type); + + create_and_register_recursive_type_forward_declaration( + cx, + tuple_type, + unique_type_id, + create_struct_stub(cx, + tuple_llvm_type, + &tuple_name[..], + unique_type_id, + UNKNOWN_SCOPE_METADATA), + tuple_llvm_type, + TupleMDF(TupleMemberDescriptionFactory { + component_types: component_types.to_vec(), + span: span, + }) + ) } -fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( - cx: &CrateContext<'a, 'tcx>, - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - llvm_type: Type, - member_description_factory: MemberDescriptionFactory<'tcx>) - -> RecursiveTypeDescription<'tcx> { - // Insert the stub into the TypeMap in order to allow for recursive references - let mut type_map = debug_context(cx).type_map.borrow_mut(); - type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata_stub); - type_map.register_type_with_metadata(cx, unfinished_type, metadata_stub); +//=----------------------------------------------------------------------------- +// Enums +//=----------------------------------------------------------------------------- - UnfinishedMetadata { - unfinished_type: unfinished_type, - unique_type_id: unique_type_id, - metadata_stub: metadata_stub, - llvm_type: llvm_type, - member_description_factory: member_description_factory, - } -} - -impl<'tcx> RecursiveTypeDescription<'tcx> { - // Finishes up the description of the type in question (mostly by providing - // descriptions of the fields of the given type) and returns the final type metadata. - fn finalize<'a>(&self, cx: &CrateContext<'a, 'tcx>) -> MetadataCreationResult { - match *self { - FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - llvm_type, - ref member_description_factory, - .. - } => { - // Make sure that we have a forward declaration of the type in - // the TypeMap so that recursive references are possible. This - // will always be the case if the RecursiveTypeDescription has - // been properly created through the - // create_and_register_recursive_type_forward_declaration() function. - { - let type_map = debug_context(cx).type_map.borrow(); - if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || - type_map.find_metadata_for_type(unfinished_type).is_none() { - cx.sess().bug(&format!("Forward declaration of potentially recursive type \ - '{}' was not found in TypeMap!", - ppaux::ty_to_string(cx.tcx(), unfinished_type)) - ); - } - } - - // ... then create the member descriptions ... - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - // ... and attach them to the stub to complete it. - set_members_of_composite_type(cx, - metadata_stub, - llvm_type, - &member_descriptions[..]); - return MetadataCreationResult::new(metadata_stub, true); - } - } - } -} - - -//=----------------------------------------------------------------------------- -// Structs -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a struct -struct StructMemberDescriptionFactory<'tcx> { - fields: Vec>, - is_simd: bool, - span: Span, -} - -impl<'tcx> StructMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) - -> Vec { - if self.fields.is_empty() { - return Vec::new(); - } - - let field_size = if self.is_simd { - machine::llsize_of_alloc(cx, type_of::type_of(cx, self.fields[0].mt.ty)) as usize - } else { - 0xdeadbeef - }; - - self.fields.iter().enumerate().map(|(i, field)| { - let name = if field.name == special_idents::unnamed_field.name { - format!("__{}", i) - } else { - token::get_name(field.name).to_string() - }; - - let offset = if self.is_simd { - assert!(field_size != 0xdeadbeef); - FixedMemberOffset { bytes: i * field_size } - } else { - ComputedMemberOffset - }; - - MemberDescription { - name: name, - llvm_type: type_of::type_of(cx, field.mt.ty), - type_metadata: type_metadata(cx, field.mt.ty, self.span), - offset: offset, - flags: FLAGS_NONE, - } - }).collect() - } -} - - -fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - struct_type: Ty<'tcx>, - def_id: ast::DefId, - substs: &subst::Substs<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let struct_name = compute_debuginfo_type_name(cx, struct_type, false); - let struct_llvm_type = type_of::type_of(cx, struct_type); - - let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - - let struct_metadata_stub = create_struct_stub(cx, - struct_llvm_type, - &struct_name[..], - unique_type_id, - containing_scope); - - let mut fields = ty::struct_fields(cx.tcx(), def_id, substs); - - // The `Ty` values returned by `ty::struct_fields` can still contain - // `ty_projection` variants, so normalize those away. - for field in &mut fields { - field.mt.ty = monomorphize::normalize_associated_type(cx.tcx(), &field.mt.ty); - } - - create_and_register_recursive_type_forward_declaration( - cx, - struct_type, - unique_type_id, - struct_metadata_stub, - struct_llvm_type, - StructMDF(StructMemberDescriptionFactory { - fields: fields, - is_simd: ty::type_is_simd(cx.tcx(), struct_type), - span: span, - }) - ) -} - - -//=----------------------------------------------------------------------------- -// Tuples -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a tuple -struct TupleMemberDescriptionFactory<'tcx> { - component_types: Vec>, - span: Span, -} - -impl<'tcx> TupleMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) - -> Vec { - self.component_types - .iter() - .enumerate() - .map(|(i, &component_type)| { - MemberDescription { - name: format!("__{}", i), - llvm_type: type_of::type_of(cx, component_type), - type_metadata: type_metadata(cx, component_type, self.span), - offset: ComputedMemberOffset, - flags: FLAGS_NONE, - } - }).collect() - } -} - -fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - tuple_type: Ty<'tcx>, - component_types: &[Ty<'tcx>], - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); - let tuple_llvm_type = type_of::type_of(cx, tuple_type); - - create_and_register_recursive_type_forward_declaration( - cx, - tuple_type, - unique_type_id, - create_struct_stub(cx, - tuple_llvm_type, - &tuple_name[..], - unique_type_id, - UNKNOWN_SCOPE_METADATA), - tuple_llvm_type, - TupleMDF(TupleMemberDescriptionFactory { - component_types: component_types.to_vec(), - span: span, - }) - ) -} - - -//=----------------------------------------------------------------------------- -// Enums -//=----------------------------------------------------------------------------- - -// Describes the members of an enum value: An enum is described as a union of -// structs in DWARF. This MemberDescriptionFactory provides the description for -// the members of this union; so for every variant of the given enum, this factory -// will produce one MemberDescription (all with no name and a fixed offset of -// zero bytes). -struct EnumMemberDescriptionFactory<'tcx> { - enum_type: Ty<'tcx>, - type_rep: Rc>, - variants: Rc>>>, - discriminant_type_metadata: Option, - containing_scope: DIScope, - file_metadata: DIFile, - span: Span, +// Describes the members of an enum value: An enum is described as a union of +// structs in DWARF. This MemberDescriptionFactory provides the description for +// the members of this union; so for every variant of the given enum, this +// factory will produce one MemberDescription (all with no name and a fixed +// offset of zero bytes). +struct EnumMemberDescriptionFactory<'tcx> { + enum_type: Ty<'tcx>, + type_rep: Rc>, + variants: Rc>>>, + discriminant_type_metadata: Option, + containing_scope: DIScope, + file_metadata: DIFile, + span: Span, } impl<'tcx> EnumMemberDescriptionFactory<'tcx> { @@ -2355,1753 +1464,683 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> { } } -// Creates MemberDescriptions for the fields of a single enum variant. -struct VariantMemberDescriptionFactory<'tcx> { - args: Vec<(String, Ty<'tcx>)>, - discriminant_type_metadata: Option, - span: Span, -} - -impl<'tcx> VariantMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) - -> Vec { - self.args.iter().enumerate().map(|(i, &(ref name, ty))| { - MemberDescription { - name: name.to_string(), - llvm_type: type_of::type_of(cx, ty), - type_metadata: match self.discriminant_type_metadata { - Some(metadata) if i == 0 => metadata, - _ => type_metadata(cx, ty, self.span) - }, - offset: ComputedMemberOffset, - flags: FLAGS_NONE - } - }).collect() - } -} - -#[derive(Copy, Clone)] -enum EnumDiscriminantInfo { - RegularDiscriminant(DIType), - OptimizedDiscriminant, - NoDiscriminant -} - -// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type -// of the variant, and (3) a MemberDescriptionFactory for producing the -// descriptions of the fields of the variant. This is a rudimentary version of a -// full RecursiveTypeDescription. -fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - enum_type: Ty<'tcx>, - struct_def: &adt::Struct<'tcx>, - variant_info: &ty::VariantInfo<'tcx>, - discriminant_info: EnumDiscriminantInfo, - containing_scope: DIScope, - span: Span) - -> (DICompositeType, Type, MemberDescriptionFactory<'tcx>) { - let variant_llvm_type = - Type::struct_(cx, &struct_def.fields - .iter() - .map(|&t| type_of::type_of(cx, t)) - .collect::>() - , - struct_def.packed); - // Could do some consistency checks here: size, align, field count, discr type - - let variant_name = token::get_name(variant_info.name); - let variant_name = &variant_name; - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - enum_type, - variant_name); - - let metadata_stub = create_struct_stub(cx, - variant_llvm_type, - variant_name, - unique_type_id, - containing_scope); - - // Get the argument names from the enum variant info - let mut arg_names: Vec<_> = match variant_info.arg_names { - Some(ref names) => { - names.iter() - .map(|&name| token::get_name(name).to_string()) - .collect() - } - None => { - variant_info.args - .iter() - .enumerate() - .map(|(i, _)| format!("__{}", i)) - .collect() - } - }; - - // If this is not a univariant enum, there is also the discriminant field. - match discriminant_info { - RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), - _ => { /* do nothing */ } - }; - - // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args: Vec<(String, Ty)> = arg_names.iter() - .zip(struct_def.fields.iter()) - .map(|(s, &t)| (s.to_string(), t)) - .collect(); - - let member_description_factory = - VariantMDF(VariantMemberDescriptionFactory { - args: args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant(discriminant_type_metadata) => { - Some(discriminant_type_metadata) - } - _ => None - }, - span: span, - }); - - (metadata_stub, variant_llvm_type, member_description_factory) -} - -fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - enum_type: Ty<'tcx>, - enum_def_id: ast::DefId, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let enum_name = compute_debuginfo_type_name(cx, enum_type, false); - - let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, enum_def_id); - let loc = span_start(cx, definition_span); - let file_metadata = file_metadata(cx, &loc.file.name); - - let variants = ty::enum_variants(cx.tcx(), enum_def_id); - - let enumerators_metadata: Vec = variants - .iter() - .map(|v| { - let token = token::get_name(v.name); - let name = CString::new(token.as_bytes()).unwrap(); - unsafe { - llvm::LLVMDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr(), - v.disr_val as u64) - } - }) - .collect(); - - let discriminant_type_metadata = |inttype| { - // We can reuse the type of the discriminant for all monomorphized - // instances of an enum because it doesn't depend on any type parameters. - // The def_id, uniquely identifying the enum's polytype acts as key in - // this cache. - let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types - .borrow() - .get(&enum_def_id).cloned(); - match cached_discriminant_type_metadata { - Some(discriminant_type_metadata) => discriminant_type_metadata, - None => { - let discriminant_llvm_type = adt::ll_inttype(cx, inttype); - let (discriminant_size, discriminant_align) = - size_and_align_of(cx, discriminant_llvm_type); - let discriminant_base_type_metadata = - type_metadata(cx, - adt::ty_of_inttype(cx.tcx(), inttype), - codemap::DUMMY_SP); - let discriminant_name = get_enum_discriminant_name(cx, enum_def_id); - - let name = CString::new(discriminant_name.as_bytes()).unwrap(); - let discriminant_type_metadata = unsafe { - llvm::LLVMDIBuilderCreateEnumerationType( - DIB(cx), - containing_scope, - name.as_ptr(), - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(discriminant_size), - bytes_to_bits(discriminant_align), - create_DIArray(DIB(cx), &enumerators_metadata), - discriminant_base_type_metadata) - }; - - debug_context(cx).created_enum_disr_types - .borrow_mut() - .insert(enum_def_id, discriminant_type_metadata); - - discriminant_type_metadata - } - } - }; - - let type_rep = adt::represent_type(cx, enum_type); - - let discriminant_type_metadata = match *type_rep { - adt::CEnum(inttype, _, _) => { - return FinalMetadata(discriminant_type_metadata(inttype)) - }, - adt::RawNullablePointer { .. } | - adt::StructWrappedNullablePointer { .. } | - adt::Univariant(..) => None, - adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)), - }; - - let enum_llvm_type = type_of::type_of(cx, enum_type); - let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); - - let unique_type_id_str = debug_context(cx) - .type_map - .borrow() - .get_unique_type_id_as_string(unique_type_id); - - let enum_name = CString::new(enum_name).unwrap(); - let unique_type_id_str = CString::new(unique_type_id_str.as_bytes()).unwrap(); - let enum_metadata = unsafe { - llvm::LLVMDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - enum_name.as_ptr(), - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(enum_type_size), - bytes_to_bits(enum_type_align), - 0, // Flags - ptr::null_mut(), - 0, // RuntimeLang - unique_type_id_str.as_ptr()) - }; - - return create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - enum_metadata, - enum_llvm_type, - EnumMDF(EnumMemberDescriptionFactory { - enum_type: enum_type, - type_rep: type_rep.clone(), - variants: variants, - discriminant_type_metadata: discriminant_type_metadata, - containing_scope: containing_scope, - file_metadata: file_metadata, - span: span, - }), - ); - - fn get_enum_discriminant_name(cx: &CrateContext, - def_id: ast::DefId) - -> token::InternedString { - let name = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx().map.get_path_elem(def_id.node).name() - } else { - csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name() - }; - - token::get_name(name) - } -} - -/// Creates debug information for a composite type, that is, anything that -/// results in a LLVM struct. -/// -/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata(cx: &CrateContext, - composite_llvm_type: Type, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription], - containing_scope: DIScope, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: DIFile, - _definition_span: Span) - -> DICompositeType { - // Create the (empty) struct metadata node ... - let composite_type_metadata = create_struct_stub(cx, - composite_llvm_type, - composite_type_name, - composite_type_unique_id, - containing_scope); - // ... and immediately create and add the member descriptions. - set_members_of_composite_type(cx, - composite_type_metadata, - composite_llvm_type, - member_descriptions); - - return composite_type_metadata; -} - -fn set_members_of_composite_type(cx: &CrateContext, - composite_type_metadata: DICompositeType, - composite_llvm_type: Type, - member_descriptions: &[MemberDescription]) { - // In some rare cases LLVM metadata uniquing would lead to an existing type - // description being used instead of a new one created in create_struct_stub. - // This would cause a hard to trace assertion in DICompositeType::SetTypeArray(). - // The following check makes sure that we get a better error message if this - // should happen again due to some regression. - { - let mut composite_types_completed = - debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { - cx.sess().bug("debuginfo::set_members_of_composite_type() - \ - Already completed forward declaration re-encountered."); - } else { - composite_types_completed.insert(composite_type_metadata); - } - } - - let member_metadata: Vec = member_descriptions - .iter() - .enumerate() - .map(|(i, member_description)| { - let (member_size, member_align) = size_and_align_of(cx, member_description.llvm_type); - let member_offset = match member_description.offset { - FixedMemberOffset { bytes } => bytes as u64, - ComputedMemberOffset => machine::llelement_offset(cx, composite_llvm_type, i) - }; - - let member_name = member_description.name.as_bytes(); - let member_name = CString::new(member_name).unwrap(); - unsafe { - llvm::LLVMDIBuilderCreateMemberType( - DIB(cx), - composite_type_metadata, - member_name.as_ptr(), - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(member_size), - bytes_to_bits(member_align), - bytes_to_bits(member_offset), - member_description.flags, - member_description.type_metadata) - } - }) - .collect(); - - unsafe { - let type_array = create_DIArray(DIB(cx), &member_metadata[..]); - llvm::LLVMDICompositeTypeSetTypeArray(DIB(cx), composite_type_metadata, type_array); - } -} - -// A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any -// caching, does not add any fields to the struct. This can be done later with -// set_members_of_composite_type(). -fn create_struct_stub(cx: &CrateContext, - struct_llvm_type: Type, - struct_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type); - - let unique_type_id_str = debug_context(cx).type_map - .borrow() - .get_unique_type_id_as_string(unique_type_id); - let name = CString::new(struct_type_name).unwrap(); - let unique_type_id = CString::new(unique_type_id_str.as_bytes()).unwrap(); - let metadata_stub = unsafe { - // LLVMDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMDIBuilderCreateStructType( - DIB(cx), - containing_scope, - name.as_ptr(), - UNKNOWN_FILE_METADATA, - UNKNOWN_LINE_NUMBER, - bytes_to_bits(struct_size), - bytes_to_bits(struct_align), - 0, - ptr::null_mut(), - empty_array, - 0, - ptr::null_mut(), - unique_type_id.as_ptr()) - }; - - return metadata_stub; -} - -fn fixed_vec_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - unique_type_id: UniqueTypeId, - element_type: Ty<'tcx>, - len: Option, - span: Span) - -> MetadataCreationResult { - let element_type_metadata = type_metadata(cx, element_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let element_llvm_type = type_of::type_of(cx, element_type); - let (element_type_size, element_type_align) = size_and_align_of(cx, element_llvm_type); - - let (array_size_in_bytes, upper_bound) = match len { - Some(len) => (element_type_size * len, len as c_longlong), - None => (0, -1) - }; - - let subrange = unsafe { - llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) - }; - - let subscripts = create_DIArray(DIB(cx), &[subrange]); - let metadata = unsafe { - llvm::LLVMDIBuilderCreateArrayType( - DIB(cx), - bytes_to_bits(array_size_in_bytes), - bytes_to_bits(element_type_align), - element_type_metadata, - subscripts) - }; - - return MetadataCreationResult::new(metadata, false); -} - -fn vec_slice_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - vec_type: Ty<'tcx>, - element_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> MetadataCreationResult { - let data_ptr_type = ty::mk_ptr(cx.tcx(), ty::mt { - ty: element_type, - mutbl: ast::MutImmutable - }); - - let element_type_metadata = type_metadata(cx, data_ptr_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let slice_llvm_type = type_of::type_of(cx, vec_type); - let slice_type_name = compute_debuginfo_type_name(cx, vec_type, true); - - let member_llvm_types = slice_llvm_type.field_types(); - assert!(slice_layout_is_correct(cx, - &member_llvm_types[..], - element_type)); - let member_descriptions = [ - MemberDescription { - name: "data_ptr".to_string(), - llvm_type: member_llvm_types[0], - type_metadata: element_type_metadata, - offset: ComputedMemberOffset, - flags: FLAGS_NONE - }, - MemberDescription { - name: "length".to_string(), - llvm_type: member_llvm_types[1], - type_metadata: type_metadata(cx, cx.tcx().types.usize, span), - offset: ComputedMemberOffset, - flags: FLAGS_NONE - }, - ]; - - assert!(member_descriptions.len() == member_llvm_types.len()); - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name); - - let metadata = composite_type_metadata(cx, - slice_llvm_type, - &slice_type_name[..], - unique_type_id, - &member_descriptions, - UNKNOWN_SCOPE_METADATA, - file_metadata, - span); - return MetadataCreationResult::new(metadata, false); - - fn slice_layout_is_correct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - member_llvm_types: &[Type], - element_type: Ty<'tcx>) - -> bool { - member_llvm_types.len() == 2 && - member_llvm_types[0] == type_of::type_of(cx, element_type).ptr_to() && - member_llvm_types[1] == cx.int_type() - } -} - -fn subroutine_type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - unique_type_id: UniqueTypeId, - signature: &ty::PolyFnSig<'tcx>, - span: Span) - -> MetadataCreationResult -{ - let signature = ty::erase_late_bound_regions(cx.tcx(), signature); - - let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs.len() + 1); - - // return type - signature_metadata.push(match signature.output { - ty::FnConverging(ret_ty) => match ret_ty.sty { - ty::ty_tup(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, ret_ty, span) - }, - ty::FnDiverging => diverging_type_metadata(cx) - }); - - // regular arguments - for &argument_type in &signature.inputs { - signature_metadata.push(type_metadata(cx, argument_type, span)); - } - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - return MetadataCreationResult::new( - unsafe { - llvm::LLVMDIBuilderCreateSubroutineType( - DIB(cx), - UNKNOWN_FILE_METADATA, - create_DIArray(DIB(cx), &signature_metadata[..])) - }, - false); -} - -// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., Box, -// &Trait), trait_object_type should be the whole thing (e.g, Box) and -// trait_type should be the actual trait (e.g., Trait). Where the trait is part -// of a DST struct, there is no trait_object_type and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - trait_type: Ty<'tcx>, - trait_object_type: Option>, - unique_type_id: UniqueTypeId) - -> DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // But it does not describe the trait's methods. - - let def_id = match trait_type.sty { - ty::ty_trait(ref data) => data.principal_def_id(), - _ => { - let pp_type_name = ppaux::ty_to_string(cx.tcx(), trait_type); - cx.sess().bug(&format!("debuginfo: Unexpected trait-object type in \ - trait_pointer_metadata(): {}", - &pp_type_name[..])); - } - }; - - let trait_object_type = trait_object_type.unwrap_or(trait_type); - let trait_type_name = - compute_debuginfo_type_name(cx, trait_object_type, false); - - let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id); - - let trait_llvm_type = type_of::type_of(cx, trait_object_type); - - composite_type_metadata(cx, - trait_llvm_type, - &trait_type_name[..], - unique_type_id, - &[], - containing_scope, - UNKNOWN_FILE_METADATA, - codemap::DUMMY_SP) -} - -fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, - usage_site_span: Span) - -> DIType { - // Get the unique type id of this type. - let unique_type_id = { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - // First, try to find the type in TypeMap. If we have seen it before, we - // can exit early here. - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - return metadata; - }, - None => { - // The Ty is not in the TypeMap but maybe we have already seen - // an equivalent type (e.g. only differing in region arguments). - // In order to find out, generate the unique type id and look - // that up. - let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); - match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => { - // There is already an equivalent type in the TypeMap. - // Register this Ty as an alias in the cache and - // return the cached metadata. - type_map.register_type_with_metadata(cx, t, metadata); - return metadata; - }, - None => { - // There really is no type metadata for this type, so - // proceed by creating it. - unique_type_id - } - } - } - } - }; - - debug!("type_metadata: {:?}", t); - - let sty = &t.sty; - let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { - ty::ty_bool | - ty::ty_char | - ty::ty_int(_) | - ty::ty_uint(_) | - ty::ty_float(_) => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::ty_tup(ref elements) if elements.is_empty() => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::ty_enum(def_id, _) => { - prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx) - } - ty::ty_vec(typ, len) => { - fixed_vec_metadata(cx, unique_type_id, typ, len.map(|x| x as u64), usage_site_span) - } - ty::ty_str => { - fixed_vec_metadata(cx, unique_type_id, cx.tcx().types.i8, None, usage_site_span) - } - ty::ty_trait(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, t, None, unique_type_id), - false) - } - ty::ty_uniq(ty) | ty::ty_ptr(ty::mt{ty, ..}) | ty::ty_rptr(_, ty::mt{ty, ..}) => { - match ty.sty { - ty::ty_vec(typ, None) => { - vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span) - } - ty::ty_str => { - vec_slice_metadata(cx, t, cx.tcx().types.u8, unique_type_id, usage_site_span) - } - ty::ty_trait(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false) - } - _ => { - let pointee_metadata = type_metadata(cx, ty, usage_site_span); - - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return metadata, - None => { /* proceed normally */ } - }; - - MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), - false) - } - } - } - ty::ty_bare_fn(_, ref barefnty) => { - subroutine_type_metadata(cx, unique_type_id, &barefnty.sig, usage_site_span) - } - ty::ty_closure(def_id, substs) => { - let typer = NormalizingClosureTyper::new(cx.tcx()); - let sig = typer.closure_type(def_id, substs).sig; - subroutine_type_metadata(cx, unique_type_id, &sig, usage_site_span) - } - ty::ty_struct(def_id, substs) => { - prepare_struct_metadata(cx, - t, - def_id, - substs, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::ty_tup(ref elements) => { - prepare_tuple_metadata(cx, - t, - &elements[..], - unique_type_id, - usage_site_span).finalize(cx) - } - _ => { - cx.sess().bug(&format!("debuginfo: unexpected type in type_metadata: {:?}", - sty)) - } - }; - - { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - - if already_stored_in_typemap { - // Also make sure that we already have a TypeMap entry entry for the unique type id. - let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => metadata, - None => { - let unique_type_id_str = - type_map.get_unique_type_id_as_string(unique_type_id); - let error_message = format!("Expected type metadata for unique \ - type id '{}' to already be in \ - the debuginfo::TypeMap but it \ - was not. (Ty = {})", - &unique_type_id_str[..], - ppaux::ty_to_string(cx.tcx(), t)); - cx.sess().span_bug(usage_site_span, &error_message[..]); - } - }; - - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - if metadata != metadata_for_uid { - let unique_type_id_str = - type_map.get_unique_type_id_as_string(unique_type_id); - let error_message = format!("Mismatch between Ty and \ - UniqueTypeId maps in \ - debuginfo::TypeMap. \ - UniqueTypeId={}, Ty={}", - &unique_type_id_str[..], - ppaux::ty_to_string(cx.tcx(), t)); - cx.sess().span_bug(usage_site_span, &error_message[..]); - } - } - None => { - type_map.register_type_with_metadata(cx, t, metadata); - } - } - } else { - type_map.register_type_with_metadata(cx, t, metadata); - type_map.register_unique_id_with_metadata(cx, unique_type_id, metadata); - } - } - - metadata -} - -struct MetadataCreationResult { - metadata: DIType, - already_stored_in_typemap: bool -} - -impl MetadataCreationResult { - fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { - MetadataCreationResult { - metadata: metadata, - already_stored_in_typemap: already_stored_in_typemap - } - } -} - -#[derive(Copy, Clone, PartialEq)] -enum InternalDebugLocation { - KnownLocation { scope: DIScope, line: usize, col: usize }, - UnknownLocation -} - -impl InternalDebugLocation { - fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { - KnownLocation { - scope: scope, - line: line, - col: col, - } - } -} - -fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) { - if debug_location == debug_context(cx).current_debug_location.get() { - return; - } - - let metadata_node; - - match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - - unsafe { - metadata_node = llvm::LLVMDIBuilderCreateDebugLocation( - debug_context(cx).llcontext, - line as c_uint, - col as c_uint, - scope, - ptr::null_mut()); - } - } - UnknownLocation => { - debug!("clearing debug location "); - metadata_node = ptr::null_mut(); - } - }; - - unsafe { - llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); - } - - debug_context(cx).current_debug_location.set(debug_location); -} - -//=----------------------------------------------------------------------------- -// Utility Functions -//=----------------------------------------------------------------------------- - -fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { - attributes.iter().any(|attr| { - let meta_item: &ast::MetaItem = &*attr.node.value; - match meta_item.node { - ast::MetaWord(ref value) => &value[..] == "no_debug", - _ => false - } - }) -} - -/// Return codemap::Loc corresponding to the beginning of the span -fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { - cx.sess().codemap().lookup_char_pos(span.lo) -} - -fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { - (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) -} - -fn bytes_to_bits(bytes: u64) -> u64 { - bytes * 8 -} - -#[inline] -fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>) - -> &'a CrateDebugContext<'tcx> { - let debug_context: &'a CrateDebugContext<'tcx> = cx.dbg_cx().as_ref().unwrap(); - debug_context -} - -#[inline] -#[allow(non_snake_case)] -fn DIB(cx: &CrateContext) -> DIBuilderRef { - cx.dbg_cx().as_ref().unwrap().builder -} - -fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { - match fcx.debug_context { - FunctionDebugContext::RegularContext(_) => false, - _ => true - } +// Creates MemberDescriptions for the fields of a single enum variant. +struct VariantMemberDescriptionFactory<'tcx> { + args: Vec<(String, Ty<'tcx>)>, + discriminant_type_metadata: Option, + span: Span, } -fn assert_type_for_node_id(cx: &CrateContext, - node_id: ast::NodeId, - error_reporting_span: Span) { - if !cx.tcx().node_types().contains_key(&node_id) { - cx.sess().span_bug(error_reporting_span, - "debuginfo: Could not find type for node id!"); +impl<'tcx> VariantMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>) + -> Vec { + self.args.iter().enumerate().map(|(i, &(ref name, ty))| { + MemberDescription { + name: name.to_string(), + llvm_type: type_of::type_of(cx, ty), + type_metadata: match self.discriminant_type_metadata { + Some(metadata) if i == 0 => metadata, + _ => type_metadata(cx, ty, self.span) + }, + offset: ComputedMemberOffset, + flags: FLAGS_NONE + } + }).collect() } } -fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) - -> (DIScope, Span) { - let containing_scope = namespace_for_item(cx, def_id).scope; - let definition_span = if def_id.krate == ast::LOCAL_CRATE { - cx.tcx().map.span(def_id.node) - } else { - // For external items there is no span information - codemap::DUMMY_SP - }; - - (containing_scope, definition_span) +#[derive(Copy, Clone)] +enum EnumDiscriminantInfo { + RegularDiscriminant(DIType), + OptimizedDiscriminant, + NoDiscriminant } -// This procedure builds the *scope map* for a given function, which maps any -// given ast::NodeId in the function's AST to the correct DIScope metadata instance. -// -// This builder procedure walks the AST in execution order and keeps track of -// what belongs to which scope, creating DIScope DIEs along the way, and -// introducing *artificial* lexical scope descriptors where necessary. These -// artificial scopes allow GDB to correctly handle name shadowing. -fn create_scope_map(cx: &CrateContext, - args: &[ast::Arg], - fn_entry_block: &ast::Block, - fn_metadata: DISubprogram, - fn_ast_id: ast::NodeId) - -> NodeMap { - let mut scope_map = NodeMap(); - - let def_map = &cx.tcx().def_map; - - struct ScopeStackEntry { - scope_metadata: DIScope, - name: Option - } - - let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None }); - scope_map.insert(fn_ast_id, fn_metadata); - - // Push argument identifiers onto the stack so arguments integrate nicely - // with variable shadowing. - for arg in args { - pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| { - scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata, - name: Some(path1.node.name) }); - scope_map.insert(node_id, fn_metadata); - }) - } - - // Clang creates a separate scope for function bodies, so let's do this too. - with_new_scope(cx, - fn_entry_block.span, - &mut scope_stack, - &mut scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, fn_entry_block, scope_stack, scope_map); - }); - - return scope_map; - - - // local helper functions for walking the AST. - fn with_new_scope(cx: &CrateContext, - scope_span: Span, - scope_stack: &mut Vec , - scope_map: &mut NodeMap, - inner_walk: F) where - F: FnOnce(&CrateContext, &mut Vec, &mut NodeMap), - { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo); - let file_metadata = file_metadata(cx, &loc.file.name); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None }); - - inner_walk(cx, scope_stack, scope_map); - - // pop artificial scopes - while scope_stack.last().unwrap().name.is_some() { - scope_stack.pop(); - } - - if scope_stack.last().unwrap().scope_metadata != scope_metadata { - cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management."); - } - - scope_stack.pop(); - } - - fn walk_block(cx: &CrateContext, - block: &ast::Block, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata); - - // The interesting things here are statements and the concluding expression. - for statement in &block.stmts { - scope_map.insert(ast_util::stmt_id(&**statement), - scope_stack.last().unwrap().scope_metadata); - - match statement.node { - ast::StmtDecl(ref decl, _) => - walk_decl(cx, &**decl, scope_stack, scope_map), - ast::StmtExpr(ref exp, _) | - ast::StmtSemi(ref exp, _) => - walk_expr(cx, &**exp, scope_stack, scope_map), - ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway). - } - } - - if let Some(ref exp) = block.expr { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } +// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type +// of the variant, and (3) a MemberDescriptionFactory for producing the +// descriptions of the fields of the variant. This is a rudimentary version of a +// full RecursiveTypeDescription. +fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + enum_type: Ty<'tcx>, + struct_def: &adt::Struct<'tcx>, + variant_info: &ty::VariantInfo<'tcx>, + discriminant_info: EnumDiscriminantInfo, + containing_scope: DIScope, + span: Span) + -> (DICompositeType, Type, MemberDescriptionFactory<'tcx>) { + let variant_llvm_type = + Type::struct_(cx, &struct_def.fields + .iter() + .map(|&t| type_of::type_of(cx, t)) + .collect::>() + , + struct_def.packed); + // Could do some consistency checks here: size, align, field count, discr type - fn walk_decl(cx: &CrateContext, - decl: &ast::Decl, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - match *decl { - codemap::Spanned { node: ast::DeclLocal(ref local), .. } => { - scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata); + let variant_name = token::get_name(variant_info.name); + let variant_name = &variant_name; + let unique_type_id = debug_context(cx).type_map + .borrow_mut() + .get_unique_type_id_of_enum_variant( + cx, + enum_type, + variant_name); - walk_pattern(cx, &*local.pat, scope_stack, scope_map); + let metadata_stub = create_struct_stub(cx, + variant_llvm_type, + variant_name, + unique_type_id, + containing_scope); - if let Some(ref exp) = local.init { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - _ => () + // Get the argument names from the enum variant info + let mut arg_names: Vec<_> = match variant_info.arg_names { + Some(ref names) => { + names.iter() + .map(|&name| token::get_name(name).to_string()) + .collect() } - } - - fn walk_pattern(cx: &CrateContext, - pat: &ast::Pat, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - let def_map = &cx.tcx().def_map; - - // Unfortunately, we cannot just use pat_util::pat_bindings() or - // ast_util::walk_pat() here because we have to visit *all* nodes in - // order to put them into the scope map. The above functions don't do that. - match pat.node { - ast::PatIdent(_, ref path1, ref sub_pat_opt) => { - - // Check if this is a binding. If so we need to put it on the - // scope stack and maybe introduce an artificial scope - if pat_util::pat_is_binding(def_map, &*pat) { - - let name = path1.node.name; - - // LLVM does not properly generate 'DW_AT_start_scope' fields - // for variable DIEs. For this reason we have to introduce - // an artificial scope at bindings whenever a variable with - // the same name is declared in *any* parent scope. - // - // Otherwise the following error occurs: - // - // let x = 10; - // - // do_something(); // 'gdb print x' correctly prints 10 - // - // { - // do_something(); // 'gdb print x' prints 0, because it - // // already reads the uninitialized 'x' - // // from the next line... - // let x = 100; - // do_something(); // 'gdb print x' correctly prints 100 - // } - - // Is there already a binding with that name? - // N.B.: this comparison must be UNhygienic... because - // gdb knows nothing about the context, so any two - // variables with the same name will cause the problem. - let need_new_scope = scope_stack + None => { + variant_info.args .iter() - .any(|entry| entry.name == Some(name)); - - if need_new_scope { - // Create a new lexical scope and push it onto the stack - let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo); - let file_metadata = file_metadata(cx, &loc.file.name); - let parent_scope = scope_stack.last().unwrap().scope_metadata; - - let scope_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - - scope_stack.push(ScopeStackEntry { - scope_metadata: scope_metadata, - name: Some(name) - }); - - } else { - // Push a new entry anyway so the name can be found - let prev_metadata = scope_stack.last().unwrap().scope_metadata; - scope_stack.push(ScopeStackEntry { - scope_metadata: prev_metadata, - name: Some(name) - }); - } - } - - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - if let Some(ref sub_pat) = *sub_pat_opt { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } - - ast::PatWild(_) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - } - - ast::PatEnum(_, ref sub_pats_opt) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - if let Some(ref sub_pats) = *sub_pats_opt { - for p in sub_pats { - walk_pattern(cx, &**p, scope_stack, scope_map); - } - } - } + .enumerate() + .map(|(i, _)| format!("__{}", i)) + .collect() + } + }; - ast::PatQPath(..) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - } + // If this is not a univariant enum, there is also the discriminant field. + match discriminant_info { + RegularDiscriminant(_) => arg_names.insert(0, "RUST$ENUM$DISR".to_string()), + _ => { /* do nothing */ } + }; - ast::PatStruct(_, ref field_pats, _) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + // Build an array of (field name, field type) pairs to be captured in the factory closure. + let args: Vec<(String, Ty)> = arg_names.iter() + .zip(struct_def.fields.iter()) + .map(|(s, &t)| (s.to_string(), t)) + .collect(); - for &codemap::Spanned { - node: ast::FieldPat { pat: ref sub_pat, .. }, - .. - } in field_pats.iter() { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); + let member_description_factory = + VariantMDF(VariantMemberDescriptionFactory { + args: args, + discriminant_type_metadata: match discriminant_info { + RegularDiscriminant(discriminant_type_metadata) => { + Some(discriminant_type_metadata) } - } + _ => None + }, + span: span, + }); - ast::PatTup(ref sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); + (metadata_stub, variant_llvm_type, member_description_factory) +} - for sub_pat in sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } +fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + enum_type: Ty<'tcx>, + enum_def_id: ast::DefId, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let enum_name = compute_debuginfo_type_name(cx, enum_type, false); - ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } + let (containing_scope, definition_span) = get_namespace_and_span_for_item(cx, enum_def_id); + let loc = span_start(cx, definition_span); + let file_metadata = file_metadata(cx, &loc.file.name); - ast::PatLit(ref exp) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp, scope_stack, scope_map); - } + let variants = ty::enum_variants(cx.tcx(), enum_def_id); - ast::PatRange(ref exp1, ref exp2) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - walk_expr(cx, &**exp1, scope_stack, scope_map); - walk_expr(cx, &**exp2, scope_stack, scope_map); + let enumerators_metadata: Vec = variants + .iter() + .map(|v| { + let token = token::get_name(v.name); + let name = CString::new(token.as_bytes()).unwrap(); + unsafe { + llvm::LLVMDIBuilderCreateEnumerator( + DIB(cx), + name.as_ptr(), + v.disr_val as u64) } + }) + .collect(); - ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { - scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata); - - for sub_pat in front_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } + let discriminant_type_metadata = |inttype| { + // We can reuse the type of the discriminant for all monomorphized + // instances of an enum because it doesn't depend on any type + // parameters. The def_id, uniquely identifying the enum's polytype acts + // as key in this cache. + let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types + .borrow() + .get(&enum_def_id).cloned(); + match cached_discriminant_type_metadata { + Some(discriminant_type_metadata) => discriminant_type_metadata, + None => { + let discriminant_llvm_type = adt::ll_inttype(cx, inttype); + let (discriminant_size, discriminant_align) = + size_and_align_of(cx, discriminant_llvm_type); + let discriminant_base_type_metadata = + type_metadata(cx, + adt::ty_of_inttype(cx.tcx(), inttype), + codemap::DUMMY_SP); + let discriminant_name = get_enum_discriminant_name(cx, enum_def_id); - if let Some(ref sub_pat) = *middle_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } + let name = CString::new(discriminant_name.as_bytes()).unwrap(); + let discriminant_type_metadata = unsafe { + llvm::LLVMDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + name.as_ptr(), + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(discriminant_size), + bytes_to_bits(discriminant_align), + create_DIArray(DIB(cx), &enumerators_metadata), + discriminant_base_type_metadata) + }; - for sub_pat in back_sub_pats { - walk_pattern(cx, &**sub_pat, scope_stack, scope_map); - } - } + debug_context(cx).created_enum_disr_types + .borrow_mut() + .insert(enum_def_id, discriminant_type_metadata); - ast::PatMac(_) => { - cx.sess().span_bug(pat.span, "debuginfo::create_scope_map() - \ - Found unexpanded macro."); + discriminant_type_metadata } } - } - - fn walk_expr(cx: &CrateContext, - exp: &ast::Expr, - scope_stack: &mut Vec , - scope_map: &mut NodeMap) { - - scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata); - - match exp.node { - ast::ExprLit(_) | - ast::ExprBreak(_) | - ast::ExprAgain(_) | - ast::ExprPath(..) => {} - - ast::ExprCast(ref sub_exp, _) | - ast::ExprAddrOf(_, ref sub_exp) | - ast::ExprField(ref sub_exp, _) | - ast::ExprTupField(ref sub_exp, _) | - ast::ExprParen(ref sub_exp) => - walk_expr(cx, &**sub_exp, scope_stack, scope_map), - - ast::ExprBox(ref place, ref sub_expr) => { - place.as_ref().map( - |e| walk_expr(cx, &**e, scope_stack, scope_map)); - walk_expr(cx, &**sub_expr, scope_stack, scope_map); - } - - ast::ExprRet(ref exp_opt) => match *exp_opt { - Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map), - None => () - }, - - ast::ExprUnary(_, ref sub_exp) => { - walk_expr(cx, &**sub_exp, scope_stack, scope_map); - } + }; - ast::ExprAssignOp(_, ref lhs, ref rhs) | - ast::ExprIndex(ref lhs, ref rhs) | - ast::ExprBinary(_, ref lhs, ref rhs) => { - walk_expr(cx, &**lhs, scope_stack, scope_map); - walk_expr(cx, &**rhs, scope_stack, scope_map); - } + let type_rep = adt::represent_type(cx, enum_type); - ast::ExprRange(ref start, ref end) => { - start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); - end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map)); - } + let discriminant_type_metadata = match *type_rep { + adt::CEnum(inttype, _, _) => { + return FinalMetadata(discriminant_type_metadata(inttype)) + }, + adt::RawNullablePointer { .. } | + adt::StructWrappedNullablePointer { .. } | + adt::Univariant(..) => None, + adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)), + }; - ast::ExprVec(ref init_expressions) | - ast::ExprTup(ref init_expressions) => { - for ie in init_expressions { - walk_expr(cx, &**ie, scope_stack, scope_map); - } - } + let enum_llvm_type = type_of::type_of(cx, enum_type); + let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); - ast::ExprAssign(ref sub_exp1, ref sub_exp2) | - ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => { - walk_expr(cx, &**sub_exp1, scope_stack, scope_map); - walk_expr(cx, &**sub_exp2, scope_stack, scope_map); - } + let unique_type_id_str = debug_context(cx) + .type_map + .borrow() + .get_unique_type_id_as_string(unique_type_id); - ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); - - with_new_scope(cx, - then_block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**then_block, scope_stack, scope_map); - }); - - match *opt_else_exp { - Some(ref else_exp) => - walk_expr(cx, &**else_exp, scope_stack, scope_map), - _ => () - } - } + let enum_name = CString::new(enum_name).unwrap(); + let unique_type_id_str = CString::new(unique_type_id_str.as_bytes()).unwrap(); + let enum_metadata = unsafe { + llvm::LLVMDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + enum_name.as_ptr(), + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(enum_type_size), + bytes_to_bits(enum_type_align), + 0, // Flags + ptr::null_mut(), + 0, // RuntimeLang + unique_type_id_str.as_ptr()) + }; - ast::ExprIfLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded if-let."); - } + return create_and_register_recursive_type_forward_declaration( + cx, + enum_type, + unique_type_id, + enum_metadata, + enum_llvm_type, + EnumMDF(EnumMemberDescriptionFactory { + enum_type: enum_type, + type_rep: type_rep.clone(), + variants: variants, + discriminant_type_metadata: discriminant_type_metadata, + containing_scope: containing_scope, + file_metadata: file_metadata, + span: span, + }), + ); - ast::ExprWhile(ref cond_exp, ref loop_body, _) => { - walk_expr(cx, &**cond_exp, scope_stack, scope_map); + fn get_enum_discriminant_name(cx: &CrateContext, + def_id: ast::DefId) + -> token::InternedString { + let name = if def_id.krate == ast::LOCAL_CRATE { + cx.tcx().map.get_path_elem(def_id.node).name() + } else { + csearch::get_item_path(cx.tcx(), def_id).last().unwrap().name() + }; - with_new_scope(cx, - loop_body.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**loop_body, scope_stack, scope_map); - }) - } + token::get_name(name) + } +} - ast::ExprWhileLet(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded while-let."); - } +/// Creates debug information for a composite type, that is, anything that +/// results in a LLVM struct. +/// +/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. +fn composite_type_metadata(cx: &CrateContext, + composite_llvm_type: Type, + composite_type_name: &str, + composite_type_unique_id: UniqueTypeId, + member_descriptions: &[MemberDescription], + containing_scope: DIScope, - ast::ExprForLoop(..) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded for loop."); - } + // Ignore source location information as long as it + // can't be reconstructed for non-local crates. + _file_metadata: DIFile, + _definition_span: Span) + -> DICompositeType { + // Create the (empty) struct metadata node ... + let composite_type_metadata = create_struct_stub(cx, + composite_llvm_type, + composite_type_name, + composite_type_unique_id, + containing_scope); + // ... and immediately create and add the member descriptions. + set_members_of_composite_type(cx, + composite_type_metadata, + composite_llvm_type, + member_descriptions); - ast::ExprMac(_) => { - cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ - Found unexpanded macro."); - } + return composite_type_metadata; +} - ast::ExprLoop(ref block, _) | - ast::ExprBlock(ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - walk_block(cx, &**block, scope_stack, scope_map); - }) - } +fn set_members_of_composite_type(cx: &CrateContext, + composite_type_metadata: DICompositeType, + composite_llvm_type: Type, + member_descriptions: &[MemberDescription]) { + // In some rare cases LLVM metadata uniquing would lead to an existing type + // description being used instead of a new one created in + // create_struct_stub. This would cause a hard to trace assertion in + // DICompositeType::SetTypeArray(). The following check makes sure that we + // get a better error message if this should happen again due to some + // regression. + { + let mut composite_types_completed = + debug_context(cx).composite_types_completed.borrow_mut(); + if composite_types_completed.contains(&composite_type_metadata) { + cx.sess().bug("debuginfo::set_members_of_composite_type() - \ + Already completed forward declaration re-encountered."); + } else { + composite_types_completed.insert(composite_type_metadata); + } + } - ast::ExprClosure(_, ref decl, ref block) => { - with_new_scope(cx, - block.span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for &ast::Arg { pat: ref pattern, .. } in &decl.inputs { - walk_pattern(cx, &**pattern, scope_stack, scope_map); - } + let member_metadata: Vec = member_descriptions + .iter() + .enumerate() + .map(|(i, member_description)| { + let (member_size, member_align) = size_and_align_of(cx, member_description.llvm_type); + let member_offset = match member_description.offset { + FixedMemberOffset { bytes } => bytes as u64, + ComputedMemberOffset => machine::llelement_offset(cx, composite_llvm_type, i) + }; - walk_block(cx, &**block, scope_stack, scope_map); - }) + let member_name = member_description.name.as_bytes(); + let member_name = CString::new(member_name).unwrap(); + unsafe { + llvm::LLVMDIBuilderCreateMemberType( + DIB(cx), + composite_type_metadata, + member_name.as_ptr(), + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(member_size), + bytes_to_bits(member_align), + bytes_to_bits(member_offset), + member_description.flags, + member_description.type_metadata) } + }) + .collect(); - ast::ExprCall(ref fn_exp, ref args) => { - walk_expr(cx, &**fn_exp, scope_stack, scope_map); + unsafe { + let type_array = create_DIArray(DIB(cx), &member_metadata[..]); + llvm::LLVMDICompositeTypeSetTypeArray(DIB(cx), composite_type_metadata, type_array); + } +} - for arg_exp in args { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } +// A convenience wrapper around LLVMDIBuilderCreateStructType(). Does not do any +// caching, does not add any fields to the struct. This can be done later with +// set_members_of_composite_type(). +fn create_struct_stub(cx: &CrateContext, + struct_llvm_type: Type, + struct_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (struct_size, struct_align) = size_and_align_of(cx, struct_llvm_type); - ast::ExprMethodCall(_, _, ref args) => { - for arg_exp in args { - walk_expr(cx, &**arg_exp, scope_stack, scope_map); - } - } + let unique_type_id_str = debug_context(cx).type_map + .borrow() + .get_unique_type_id_as_string(unique_type_id); + let name = CString::new(struct_type_name).unwrap(); + let unique_type_id = CString::new(unique_type_id_str.as_bytes()).unwrap(); + let metadata_stub = unsafe { + // LLVMDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); - ast::ExprMatch(ref discriminant_exp, ref arms, _) => { - walk_expr(cx, &**discriminant_exp, scope_stack, scope_map); + llvm::LLVMDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name.as_ptr(), + UNKNOWN_FILE_METADATA, + UNKNOWN_LINE_NUMBER, + bytes_to_bits(struct_size), + bytes_to_bits(struct_align), + 0, + ptr::null_mut(), + empty_array, + 0, + ptr::null_mut(), + unique_type_id.as_ptr()) + }; - // For each arm we have to first walk the pattern as these might - // introduce new artificial scopes. It should be sufficient to - // walk only one pattern per arm, as they all must contain the - // same binding names. + return metadata_stub; +} - for arm_ref in arms { - let arm_span = arm_ref.pats[0].span; +/// Creates debug information for the given global variable. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_global_var_metadata(cx: &CrateContext, + node_id: ast::NodeId, + global: ValueRef) { + if cx.dbg_cx().is_none() { + return; + } - with_new_scope(cx, - arm_span, - scope_stack, - scope_map, - |cx, scope_stack, scope_map| { - for pat in &arm_ref.pats { - walk_pattern(cx, &**pat, scope_stack, scope_map); - } + // Don't create debuginfo for globals inlined from other crates. The other + // crate should already contain debuginfo for it. More importantly, the + // global might not even exist in un-inlined form anywhere which would lead + // to a linker errors. + if cx.external_srcs().borrow().contains_key(&node_id) { + return; + } - if let Some(ref guard_exp) = arm_ref.guard { - walk_expr(cx, &**guard_exp, scope_stack, scope_map) - } + let var_item = cx.tcx().map.get(node_id); - walk_expr(cx, &*arm_ref.body, scope_stack, scope_map); - }) + let (name, span) = match var_item { + ast_map::NodeItem(item) => { + match item.node { + ast::ItemStatic(..) => (item.ident.name, item.span), + ast::ItemConst(..) => (item.ident.name, item.span), + _ => { + cx.sess() + .span_bug(item.span, + &format!("debuginfo::\ + create_global_var_metadata() - + Captured var-id refers to \ + unexpected ast_item variant: {:?}", + var_item)) } } + }, + _ => cx.sess().bug(&format!("debuginfo::create_global_var_metadata() \ + - Captured var-id refers to unexpected \ + ast_map variant: {:?}", + var_item)) + }; - ast::ExprStruct(_, ref fields, ref base_exp) => { - for &ast::Field { expr: ref exp, .. } in fields { - walk_expr(cx, &**exp, scope_stack, scope_map); - } + let (file_metadata, line_number) = if span != codemap::DUMMY_SP { + let loc = span_start(cx, span); + (file_metadata(cx, &loc.file.name), loc.line as c_uint) + } else { + (UNKNOWN_FILE_METADATA, UNKNOWN_LINE_NUMBER) + }; - match *base_exp { - Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map), - None => () - } - } + let is_local_to_unit = is_node_local_to_unit(cx, node_id); + let variable_type = ty::node_id_to_type(cx.tcx(), node_id); + let type_metadata = type_metadata(cx, variable_type, span); + let namespace_node = namespace_for_item(cx, ast_util::local_def(node_id)); + let var_name = token::get_name(name).to_string(); + let linkage_name = + namespace_node.mangled_name_of_contained_item(&var_name[..]); + let var_scope = namespace_node.scope; - ast::ExprInlineAsm(ast::InlineAsm { ref inputs, - ref outputs, - .. }) => { - // inputs, outputs: Vec<(String, P)> - for &(_, ref exp) in inputs { - walk_expr(cx, &**exp, scope_stack, scope_map); - } + let var_name = CString::new(var_name).unwrap(); + let linkage_name = CString::new(linkage_name).unwrap(); + unsafe { + llvm::LLVMDIBuilderCreateStaticVariable(DIB(cx), + var_scope, + var_name.as_ptr(), + linkage_name.as_ptr(), + file_metadata, + line_number, + type_metadata, + is_local_to_unit, + global, + ptr::null_mut()); + } +} - for &(_, ref exp, _) in outputs { - walk_expr(cx, &**exp, scope_stack, scope_map); - } - } - } +/// Creates debug information for the given local variable. +/// +/// This function assumes that there's a datum for each pattern component of the +/// local in `bcx.fcx.lllocals`. +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_local_var_metadata(bcx: Block, local: &ast::Local) { + if bcx.unreachable.get() || + fn_should_be_ignored(bcx.fcx) || + bcx.sess().opts.debuginfo != FullDebugInfo { + return; } -} - - -//=----------------------------------------------------------------------------- -// Type Names for Debug Info -//=----------------------------------------------------------------------------- - -// Compute the name of the type as it should be stored in debuginfo. Does not do -// any caching, i.e. calling the function twice with the same type will also do -// the work twice. The `qualified` parameter only affects the first level of the -// type name, further levels (i.e. type parameters) are always fully qualified. -fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool) - -> String { - let mut result = String::with_capacity(64); - push_debuginfo_type_name(cx, t, qualified, &mut result); - result -} -// Pushes the name of the type as it should be stored in debuginfo on the -// `output` String. See also compute_debuginfo_type_name(). -fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool, - output: &mut String) { - match t.sty { - ty::ty_bool => output.push_str("bool"), - ty::ty_char => output.push_str("char"), - ty::ty_str => output.push_str("str"), - ty::ty_int(ast::TyIs) => output.push_str("isize"), - ty::ty_int(ast::TyI8) => output.push_str("i8"), - ty::ty_int(ast::TyI16) => output.push_str("i16"), - ty::ty_int(ast::TyI32) => output.push_str("i32"), - ty::ty_int(ast::TyI64) => output.push_str("i64"), - ty::ty_uint(ast::TyUs) => output.push_str("usize"), - ty::ty_uint(ast::TyU8) => output.push_str("u8"), - ty::ty_uint(ast::TyU16) => output.push_str("u16"), - ty::ty_uint(ast::TyU32) => output.push_str("u32"), - ty::ty_uint(ast::TyU64) => output.push_str("u64"), - ty::ty_float(ast::TyF32) => output.push_str("f32"), - ty::ty_float(ast::TyF64) => output.push_str("f64"), - ty::ty_struct(def_id, substs) | - ty::ty_enum(def_id, substs) => { - push_item_name(cx, def_id, qualified, output); - push_type_params(cx, substs, output); - }, - ty::ty_tup(ref component_types) => { - output.push('('); - for &component_type in component_types { - push_debuginfo_type_name(cx, component_type, true, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::ty_uniq(inner_type) => { - output.push_str("Box<"); - push_debuginfo_type_name(cx, inner_type, true, output); - output.push('>'); - }, - ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { - output.push('*'); - match mutbl { - ast::MutImmutable => output.push_str("const "), - ast::MutMutable => output.push_str("mut "), - } + let cx = bcx.ccx(); + let def_map = &cx.tcx().def_map; + let locals = bcx.fcx.lllocals.borrow(); - push_debuginfo_type_name(cx, inner_type, true, output); - }, - ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { - output.push('&'); - if mutbl == ast::MutMutable { - output.push_str("mut "); + pat_util::pat_bindings(def_map, &*local.pat, |_, node_id, span, var_ident| { + let datum = match locals.get(&node_id) { + Some(datum) => datum, + None => { + bcx.sess().span_bug(span, + &format!("no entry in lllocals table for {}", + node_id)); } + }; - push_debuginfo_type_name(cx, inner_type, true, output); - }, - ty::ty_vec(inner_type, optional_length) => { - output.push('['); - push_debuginfo_type_name(cx, inner_type, true, output); - - match optional_length { - Some(len) => { - output.push_str(&format!("; {}", len)); - } - None => { /* nothing to do */ } - }; - - output.push(']'); - }, - ty::ty_trait(ref trait_data) => { - let principal = ty::erase_late_bound_regions(cx.tcx(), &trait_data.principal); - push_item_name(cx, principal.def_id, false, output); - push_type_params(cx, principal.substs, output); - }, - ty::ty_bare_fn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => { - if unsafety == ast::Unsafety::Unsafe { - output.push_str("unsafe "); - } + if unsafe { llvm::LLVMIsAAllocaInst(datum.val) } == ptr::null_mut() { + cx.sess().span_bug(span, "debuginfo::create_local_var_metadata() - \ + Referenced variable location is not an alloca!"); + } - if abi != ::syntax::abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } + let scope_metadata = scope_metadata(bcx.fcx, node_id, span); - output.push_str("fn("); + declare_local(bcx, + var_ident.node.name, + datum.ty, + scope_metadata, + VariableAccess::DirectVariable { alloca: datum.val }, + VariableKind::LocalVariable, + span); + }) +} - let sig = ty::erase_late_bound_regions(cx.tcx(), sig); - if !sig.inputs.is_empty() { - for ¶meter_type in &sig.inputs { - push_debuginfo_type_name(cx, parameter_type, true, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } +/// Creates debug information for a variable captured in a closure. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_captured_var_metadata<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + node_id: ast::NodeId, + env_pointer: ValueRef, + env_index: usize, + captured_by_ref: bool, + span: Span) { + if bcx.unreachable.get() || + fn_should_be_ignored(bcx.fcx) || + bcx.sess().opts.debuginfo != FullDebugInfo { + return; + } - if sig.variadic { - if !sig.inputs.is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } + let cx = bcx.ccx(); - output.push(')'); + let ast_item = cx.tcx().map.find(node_id); - match sig.output { - ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} - ty::FnConverging(result_type) => { - output.push_str(" -> "); - push_debuginfo_type_name(cx, result_type, true, output); + let variable_name = match ast_item { + None => { + cx.sess().span_bug(span, "debuginfo::create_captured_var_metadata: node not found"); + } + Some(ast_map::NodeLocal(pat)) | Some(ast_map::NodeArg(pat)) => { + match pat.node { + ast::PatIdent(_, ref path1, _) => { + path1.node.name } - ty::FnDiverging => { - output.push_str(" -> !"); + _ => { + cx.sess() + .span_bug(span, + &format!( + "debuginfo::create_captured_var_metadata() - \ + Captured var-id refers to unexpected \ + ast_map variant: {:?}", + ast_item)); } } - }, - ty::ty_closure(..) => { - output.push_str("closure"); } - ty::ty_err | - ty::ty_infer(_) | - ty::ty_projection(..) | - ty::ty_param(_) => { - cx.sess().bug(&format!("debuginfo: Trying to create type name for \ - unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t))); + _ => { + cx.sess() + .span_bug(span, + &format!("debuginfo::create_captured_var_metadata() - \ + Captured var-id refers to unexpected \ + ast_map variant: {:?}", + ast_item)); } - } - - fn push_item_name(cx: &CrateContext, - def_id: ast::DefId, - qualified: bool, - output: &mut String) { - ty::with_path(cx.tcx(), def_id, |path| { - if qualified { - if def_id.krate == ast::LOCAL_CRATE { - output.push_str(crate_root_namespace(cx)); - output.push_str("::"); - } - - let mut path_element_count = 0; - for path_element in path { - let name = token::get_name(path_element.name()); - output.push_str(&name); - output.push_str("::"); - path_element_count += 1; - } - - if path_element_count == 0 { - cx.sess().bug("debuginfo: Encountered empty item path!"); - } + }; - output.pop(); - output.pop(); - } else { - let name = token::get_name(path.last() - .expect("debuginfo: Empty item path?") - .name()); - output.push_str(&name); - } - }); - } + let variable_type = common::node_id_type(bcx, node_id); + let scope_metadata = bcx.fcx.debug_context.get_ref(cx, span).fn_metadata; - // Pushes the type parameters in the given `Substs` to the output string. - // This ignores region parameters, since they can't reliably be - // reconstructed for items from non-local crates. For local crates, this - // would be possible but with inlining and LTO we have to use the least - // common denominator - otherwise we would run into conflicts. - fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, - substs: &subst::Substs<'tcx>, - output: &mut String) { - if substs.types.is_empty() { - return; - } + // env_pointer is the alloca containing the pointer to the environment, + // so it's type is **EnvironmentType. In order to find out the type of + // the environment we have to "dereference" two times. + let llvm_env_data_type = common::val_ty(env_pointer).element_type() + .element_type(); + let byte_offset_of_var_in_env = machine::llelement_offset(cx, + llvm_env_data_type, + env_index); - output.push('<'); + let address_operations = unsafe { + [llvm::LLVMDIBuilderCreateOpDeref(), + llvm::LLVMDIBuilderCreateOpPlus(), + byte_offset_of_var_in_env as i64, + llvm::LLVMDIBuilderCreateOpDeref()] + }; - for &type_parameter in substs.types.iter() { - push_debuginfo_type_name(cx, type_parameter, true, output); - output.push_str(", "); - } + let address_op_count = if captured_by_ref { + address_operations.len() + } else { + address_operations.len() - 1 + }; - output.pop(); - output.pop(); + let variable_access = VariableAccess::IndirectVariable { + alloca: env_pointer, + address_operations: &address_operations[..address_op_count] + }; - output.push('>'); - } + declare_local(bcx, + variable_name, + variable_type, + scope_metadata, + variable_access, + VariableKind::CapturedVariable, + span); } +/// Creates debug information for a local variable introduced in the head of a +/// match-statement arm. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_match_binding_metadata<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + variable_name: ast::Name, + binding: BindingInfo<'tcx>) { + if bcx.unreachable.get() || + fn_should_be_ignored(bcx.fcx) || + bcx.sess().opts.debuginfo != FullDebugInfo { + return; + } -//=----------------------------------------------------------------------------- -// Namespace Handling -//=----------------------------------------------------------------------------- - -struct NamespaceTreeNode { - name: ast::Name, - scope: DIScope, - parent: Option>, -} - -impl NamespaceTreeNode { - fn mangled_name_of_contained_item(&self, item_name: &str) -> String { - fn fill_nested(node: &NamespaceTreeNode, output: &mut String) { - match node.parent { - Some(ref parent) => fill_nested(&*parent.upgrade().unwrap(), output), - None => {} - } - let string = token::get_name(node.name); - output.push_str(&format!("{}", string.len())); - output.push_str(&string); + let scope_metadata = scope_metadata(bcx.fcx, binding.id, binding.span); + let aops = unsafe { + [llvm::LLVMDIBuilderCreateOpDeref()] + }; + // Regardless of the actual type (`T`) we're always passed the stack slot + // (alloca) for the binding. For ByRef bindings that's a `T*` but for ByMove + // bindings we actually have `T**`. So to get the actual variable we need to + // dereference once more. For ByCopy we just use the stack slot we created + // for the binding. + let var_access = match binding.trmode { + TrByCopy(llbinding) => VariableAccess::DirectVariable { + alloca: llbinding + }, + TrByMove => VariableAccess::IndirectVariable { + alloca: binding.llmatch, + address_operations: &aops + }, + TrByRef => VariableAccess::DirectVariable { + alloca: binding.llmatch } + }; - let mut name = String::from_str("_ZN"); - fill_nested(self, &mut name); - name.push_str(&format!("{}", item_name.len())); - name.push_str(item_name); - name.push('E'); - name - } -} - -fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str { - &cx.link_meta().crate_name + declare_local(bcx, + variable_name, + binding.ty, + scope_metadata, + var_access, + VariableKind::LocalVariable, + binding.span); } -fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc { - ty::with_path(cx.tcx(), def_id, |path| { - // prepend crate name if not already present - let krate = if def_id.krate == ast::LOCAL_CRATE { - let crate_namespace_name = token::intern(crate_root_namespace(cx)); - Some(ast_map::PathMod(crate_namespace_name)) - } else { - None - }; - let mut path = krate.into_iter().chain(path).peekable(); - - let mut current_key = Vec::new(); - let mut parent_node: Option> = None; - - // Create/Lookup namespace for each element of the path. - loop { - // Emulate a for loop so we can use peek below. - let path_element = match path.next() { - Some(e) => e, - None => break - }; - // Ignore the name of the item (the last path element). - if path.peek().is_none() { - break; - } - - let name = path_element.name(); - current_key.push(name); - - let existing_node = debug_context(cx).namespace_map.borrow() - .get(¤t_key).cloned(); - let current_node = match existing_node { - Some(existing_node) => existing_node, - None => { - // create and insert - let parent_scope = match parent_node { - Some(ref node) => node.scope, - None => ptr::null_mut() - }; - let namespace_name = token::get_name(name); - let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); - let scope = unsafe { - llvm::LLVMDIBuilderCreateNameSpace( - DIB(cx), - parent_scope, - namespace_name.as_ptr(), - // cannot reconstruct file ... - ptr::null_mut(), - // ... or line information, but that's not so important. - 0) - }; - - let node = Rc::new(NamespaceTreeNode { - name: name, - scope: scope, - parent: parent_node.map(|parent| parent.downgrade()), - }); - - debug_context(cx).namespace_map.borrow_mut() - .insert(current_key.clone(), node.clone()); - - node - } - }; +/// Creates debug information for the given function argument. +/// +/// This function assumes that there's a datum for each pattern component of the +/// argument in `bcx.fcx.lllocals`. +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_argument_metadata(bcx: Block, arg: &ast::Arg) { + if bcx.unreachable.get() || + fn_should_be_ignored(bcx.fcx) || + bcx.sess().opts.debuginfo != FullDebugInfo { + return; + } - parent_node = Some(current_node); - } + let def_map = &bcx.tcx().def_map; + let scope_metadata = bcx + .fcx + .debug_context + .get_ref(bcx.ccx(), arg.pat.span) + .fn_metadata; + let locals = bcx.fcx.lllocals.borrow(); - match parent_node { - Some(node) => node, + pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, span, var_ident| { + let datum = match locals.get(&node_id) { + Some(v) => v, None => { - cx.sess().bug(&format!("debuginfo::namespace_for_item(): \ - path too short for {:?}", - def_id)); + bcx.sess().span_bug(span, + &format!("no entry in lllocals table for {}", + node_id)); } - } - }) -} - - -//=----------------------------------------------------------------------------- -// .debug_gdb_scripts binary section -//=----------------------------------------------------------------------------- + }; -/// Inserts a side-effect free instruction sequence that makes sure that the -/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. -pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { - if needs_gdb_debug_scripts_section(ccx) { - let empty = CString::new("").unwrap(); - let gdb_debug_scripts_section_global = - get_or_insert_gdb_debug_scripts_section_global(ccx); - unsafe { - let volative_load_instruction = - llvm::LLVMBuildLoad(ccx.raw_builder(), - gdb_debug_scripts_section_global, - empty.as_ptr()); - llvm::LLVMSetVolatile(volative_load_instruction, llvm::True); + if unsafe { llvm::LLVMIsAAllocaInst(datum.val) } == ptr::null_mut() { + bcx.sess().span_bug(span, "debuginfo::create_argument_metadata() - \ + Referenced variable location is not an alloca!"); } - } -} - -/// Allocates the global variable responsible for the .debug_gdb_scripts binary -/// section. -fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext) - -> llvm::ValueRef { - let section_var_name = "__rustc_debug_gdb_scripts_section__"; - let section_var = unsafe { - llvm::LLVMGetNamedGlobal(ccx.llmod(), - section_var_name.as_ptr() as *const _) - }; - - if section_var == ptr::null_mut() { - let section_name = b".debug_gdb_scripts\0"; - let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; - - unsafe { - let llvm_type = Type::array(&Type::i8(ccx), - section_contents.len() as u64); - - let section_var = declare::define_global(ccx, section_var_name, - llvm_type).unwrap_or_else(||{ - ccx.sess().bug(&format!("symbol `{}` is already defined", section_var_name)) - }); - llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(section_var, C_bytes(ccx, section_contents)); - llvm::LLVMSetGlobalConstant(section_var, llvm::True); - llvm::LLVMSetUnnamedAddr(section_var, llvm::True); - llvm::SetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); - // This should make sure that the whole section is not larger than - // the string it contains. Otherwise we get a warning from GDB. - llvm::LLVMSetAlignment(section_var, 1); - section_var - } - } else { - section_var - } -} + let argument_index = { + let counter = &bcx + .fcx + .debug_context + .get_ref(bcx.ccx(), span) + .argument_counter; + let argument_index = counter.get(); + counter.set(argument_index + 1); + argument_index + }; -fn needs_gdb_debug_scripts_section(ccx: &CrateContext) -> bool { - let omit_gdb_pretty_printer_section = - attr::contains_name(&ccx.tcx() - .map - .krate() - .attrs, - "omit_gdb_pretty_printer_section"); - - !omit_gdb_pretty_printer_section && - !ccx.sess().target.target.options.is_like_osx && - !ccx.sess().target.target.options.is_like_windows && - ccx.sess().opts.debuginfo != NoDebugInfo + declare_local(bcx, + var_ident.node.name, + datum.ty, + scope_metadata, + VariableAccess::DirectVariable { alloca: datum.val }, + VariableKind::ArgumentVariable(argument_index), + span); + }) } diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs new file mode 100644 index 0000000000000..e4312b669ad98 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -0,0 +1,651 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// See doc.rs for documentation. +mod doc; + +use self::VariableAccess::*; +use self::VariableKind::*; + +use self::utils::{DIB, span_start, assert_type_for_node_id, contains_nodebug_attribute, + create_DIArray, is_node_local_to_unit}; +use self::namespace::{namespace_for_item, NamespaceTreeNode}; +use self::type_names::compute_debuginfo_type_name; +use self::metadata::{type_metadata, file_metadata, scope_metadata, TypeMap, compile_unit_metadata}; +use self::source_loc::InternalDebugLocation; + +use llvm; +use llvm::{ModuleRef, ContextRef, ValueRef}; +use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, + DIDescriptor, FlagPrototyped}; +use middle::subst::{self, Substs}; +use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext, Block}; +use trans; +use trans::monomorphize; +use middle::ty::{self, Ty, ClosureTyper}; +use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; +use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet}; + +use libc::c_uint; +use std::cell::{Cell, RefCell}; +use std::ffi::CString; +use std::ptr; +use std::rc::Rc; +use syntax::codemap::{Span, Pos}; +use syntax::{ast, codemap, ast_util, ast_map}; +use syntax::parse::token::{self, special_idents}; + +pub mod gdb; +mod utils; +mod namespace; +mod type_names; +mod metadata; +mod create_scope_map; +mod source_loc; + +pub use self::source_loc::set_source_location; +pub use self::source_loc::clear_source_location; +pub use self::source_loc::start_emitting_source_locations; +pub use self::source_loc::get_cleanup_debug_loc_for_ast_node; +pub use self::source_loc::with_source_location_override; +pub use self::metadata::create_match_binding_metadata; +pub use self::metadata::create_argument_metadata; +pub use self::metadata::create_captured_var_metadata; +pub use self::metadata::create_global_var_metadata; +pub use self::metadata::create_local_var_metadata; + +#[allow(non_upper_case_globals)] +const DW_TAG_auto_variable: c_uint = 0x100; +#[allow(non_upper_case_globals)] +const DW_TAG_arg_variable: c_uint = 0x101; + +/// A context object for maintaining all state needed by the debuginfo module. +pub struct CrateDebugContext<'tcx> { + llcontext: ContextRef, + builder: DIBuilderRef, + current_debug_location: Cell, + created_files: RefCell>, + created_enum_disr_types: RefCell>, + + type_map: RefCell>, + namespace_map: RefCell, Rc>>, + + // This collection is used to assert that composite types (structs, enums, + // ...) have their members only set once: + composite_types_completed: RefCell>, +} + +impl<'tcx> CrateDebugContext<'tcx> { + pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { + debug!("CrateDebugContext::new"); + let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) }; + // DIBuilder inherits context from the module, so we'd better use the same one + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + return CrateDebugContext { + llcontext: llcontext, + builder: builder, + current_debug_location: Cell::new(InternalDebugLocation::UnknownLocation), + created_files: RefCell::new(FnvHashMap()), + created_enum_disr_types: RefCell::new(DefIdMap()), + type_map: RefCell::new(TypeMap::new()), + namespace_map: RefCell::new(FnvHashMap()), + composite_types_completed: RefCell::new(FnvHashSet()), + }; + } +} + +pub enum FunctionDebugContext { + RegularContext(Box), + DebugInfoDisabled, + FunctionWithoutDebugInfo, +} + +impl FunctionDebugContext { + fn get_ref<'a>(&'a self, + cx: &CrateContext, + span: Span) + -> &'a FunctionDebugContextData { + match *self { + FunctionDebugContext::RegularContext(box ref data) => data, + FunctionDebugContext::DebugInfoDisabled => { + cx.sess().span_bug(span, + FunctionDebugContext::debuginfo_disabled_message()); + } + FunctionDebugContext::FunctionWithoutDebugInfo => { + cx.sess().span_bug(span, + FunctionDebugContext::should_be_ignored_message()); + } + } + } + + fn debuginfo_disabled_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" + } + + fn should_be_ignored_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext for function that should be \ + ignored by debug info!" + } +} + +struct FunctionDebugContextData { + scope_map: RefCell>, + fn_metadata: DISubprogram, + argument_counter: Cell, + source_locations_enabled: Cell, + source_location_override: Cell, +} + +pub enum VariableAccess<'a> { + // The llptr given is an alloca containing the variable's value + DirectVariable { alloca: ValueRef }, + // The llptr given is an alloca containing the start of some pointer chain + // leading to the variable's content. + IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } +} + +pub enum VariableKind { + ArgumentVariable(usize /*index*/), + LocalVariable, + CapturedVariable, +} + +/// Create any deferred debug metadata nodes +pub fn finalize(cx: &CrateContext) { + if cx.dbg_cx().is_none() { + return; + } + + debug!("finalize"); + let _ = compile_unit_metadata(cx); + + if gdb::needs_gdb_debug_scripts_section(cx) { + // Add a .debug_gdb_scripts section to this compile-unit. This will + // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, + // which activates the Rust pretty printers for binary this section is + // contained in. + gdb::get_or_insert_gdb_debug_scripts_section_global(cx); + } + + unsafe { + llvm::LLVMDIBuilderFinalize(DIB(cx)); + llvm::LLVMDIBuilderDispose(DIB(cx)); + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than OS X currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for OS X to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + if cx.sess().target.target.options.is_like_osx || + cx.sess().target.target.options.is_like_android { + llvm::LLVMRustAddModuleFlag(cx.llmod(), + "Dwarf Version\0".as_ptr() as *const _, + 2) + } + + // Prevent bitcode readers from deleting the debug info. + let ptr = "Debug Info Version\0".as_ptr(); + llvm::LLVMRustAddModuleFlag(cx.llmod(), ptr as *const _, + llvm::LLVMRustDebugMetadataVersion); + }; +} + +/// Creates the function-specific debug context. +/// +/// Returns the FunctionDebugContext for the function which holds state needed +/// for debug info creation. The function may also return another variant of the +/// FunctionDebugContext enum which indicates why no debuginfo should be created +/// for the function. +pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + fn_ast_id: ast::NodeId, + param_substs: &Substs<'tcx>, + llfn: ValueRef) -> FunctionDebugContext { + if cx.sess().opts.debuginfo == NoDebugInfo { + return FunctionDebugContext::DebugInfoDisabled; + } + + // Clear the debug location so we don't assign them in the function prelude. + // Do this here already, in case we do an early exit from this function. + source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation); + + if fn_ast_id == ast::DUMMY_NODE_ID { + // This is a function not linked to any source location, so don't + // generate debuginfo for it. + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + let empty_generics = ast_util::empty_generics(); + + let fnitem = cx.tcx().map.get(fn_ast_id); + + let (name, fn_decl, generics, top_level_block, span, has_path) = match fnitem { + ast_map::NodeItem(ref item) => { + if contains_nodebug_attribute(&item.attrs) { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + match item.node { + ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => { + (item.ident.name, fn_decl, generics, top_level_block, item.span, true) + } + _ => { + cx.sess().span_bug(item.span, + "create_function_debug_context: item bound to non-function"); + } + } + } + ast_map::NodeImplItem(impl_item) => { + match impl_item.node { + ast::MethodImplItem(ref sig, ref body) => { + if contains_nodebug_attribute(&impl_item.attrs) { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + (impl_item.ident.name, + &sig.decl, + &sig.generics, + body, + impl_item.span, + true) + } + _ => { + cx.sess().span_bug(impl_item.span, + "create_function_debug_context() \ + called on non-method impl item?!") + } + } + } + ast_map::NodeExpr(ref expr) => { + match expr.node { + ast::ExprClosure(_, ref fn_decl, ref top_level_block) => { + let name = format!("fn{}", token::gensym("fn")); + let name = token::intern(&name[..]); + (name, fn_decl, + // This is not quite right. It should actually inherit + // the generics of the enclosing function. + &empty_generics, + top_level_block, + expr.span, + // Don't try to lookup the item path: + false) + } + _ => cx.sess().span_bug(expr.span, + "create_function_debug_context: expected an expr_fn_block here") + } + } + ast_map::NodeTraitItem(trait_item) => { + match trait_item.node { + ast::MethodTraitItem(ref sig, Some(ref body)) => { + if contains_nodebug_attribute(&trait_item.attrs) { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + (trait_item.ident.name, + &sig.decl, + &sig.generics, + body, + trait_item.span, + true) + } + _ => { + cx.sess() + .bug(&format!("create_function_debug_context: \ + unexpected sort of node: {:?}", + fnitem)) + } + } + } + ast_map::NodeForeignItem(..) | + ast_map::NodeVariant(..) | + ast_map::NodeStructCtor(..) => { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + _ => cx.sess().bug(&format!("create_function_debug_context: \ + unexpected sort of node: {:?}", + fnitem)) + }; + + // This can be the case for functions inlined from another crate + if span == codemap::DUMMY_SP { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, &loc.file.name); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(cx, + fn_ast_id, + &*fn_decl, + param_substs, + span); + llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) + }; + + // Get_template_parameters() will append a `<...>` clause to the function + // name if necessary. + let mut function_name = String::from_str(&token::get_name(name)); + let template_parameters = get_template_parameters(cx, + generics, + param_substs, + file_metadata, + &mut function_name); + + // There is no ast_map::Path for ast::ExprClosure-type functions. For now, + // just don't put them into a namespace. In the future this could be improved + // somehow (storing a path in the ast_map, or construct a path using the + // enclosing function). + let (linkage_name, containing_scope) = if has_path { + let namespace_node = namespace_for_item(cx, ast_util::local_def(fn_ast_id)); + let linkage_name = namespace_node.mangled_name_of_contained_item( + &function_name[..]); + let containing_scope = namespace_node.scope; + (linkage_name, containing_scope) + } else { + (function_name.clone(), file_metadata) + }; + + // Clang sets this parameter to the opening brace of the function's block, + // so let's do this too. + let scope_line = span_start(cx, top_level_block.span).line; + + let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id); + + let function_name = CString::new(function_name).unwrap(); + let linkage_name = CString::new(linkage_name).unwrap(); + let fn_metadata = unsafe { + llvm::LLVMDIBuilderCreateFunction( + DIB(cx), + containing_scope, + function_name.as_ptr(), + linkage_name.as_ptr(), + file_metadata, + loc.line as c_uint, + function_type_metadata, + is_local_to_unit, + true, + scope_line as c_uint, + FlagPrototyped as c_uint, + cx.sess().opts.optimize != config::No, + llfn, + template_parameters, + ptr::null_mut()) + }; + + let scope_map = create_scope_map::create_scope_map(cx, + &fn_decl.inputs, + &*top_level_block, + fn_metadata, + fn_ast_id); + + // Initialize fn debug context (including scope map and namespace map) + let fn_debug_context = box FunctionDebugContextData { + scope_map: RefCell::new(scope_map), + fn_metadata: fn_metadata, + argument_counter: Cell::new(1), + source_locations_enabled: Cell::new(false), + source_location_override: Cell::new(false), + }; + + + + return FunctionDebugContext::RegularContext(fn_debug_context); + + fn get_function_signature<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + fn_ast_id: ast::NodeId, + fn_decl: &ast::FnDecl, + param_substs: &Substs<'tcx>, + error_reporting_span: Span) -> DIArray { + if cx.sess().opts.debuginfo == LimitedDebugInfo { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(fn_decl.inputs.len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + assert_type_for_node_id(cx, fn_ast_id, error_reporting_span); + let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id); + let return_type = monomorphize::apply_param_substs(cx.tcx(), + param_substs, + &return_type); + if ty::type_is_nil(return_type) { + signature.push(ptr::null_mut()) + } else { + signature.push(type_metadata(cx, return_type, codemap::DUMMY_SP)); + } + + // Arguments types + for arg in &fn_decl.inputs { + assert_type_for_node_id(cx, arg.pat.id, arg.pat.span); + let arg_type = ty::node_id_to_type(cx.tcx(), arg.pat.id); + let arg_type = monomorphize::apply_param_substs(cx.tcx(), + param_substs, + &arg_type); + signature.push(type_metadata(cx, arg_type, codemap::DUMMY_SP)); + } + + return create_DIArray(DIB(cx), &signature[..]); + } + + fn get_template_parameters<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + generics: &ast::Generics, + param_substs: &Substs<'tcx>, + file_metadata: DIFile, + name_to_append_suffix_to: &mut String) + -> DIArray + { + let self_type = param_substs.self_ty(); + let self_type = monomorphize::normalize_associated_type(cx.tcx(), &self_type); + + // Only true for static default methods: + let has_self_type = self_type.is_some(); + + if !generics.is_type_parameterized() && !has_self_type { + return create_DIArray(DIB(cx), &[]); + } + + name_to_append_suffix_to.push('<'); + + // The list to be filled with template parameters: + let mut template_params: Vec = + Vec::with_capacity(generics.ty_params.len() + 1); + + // Handle self type + if has_self_type { + let actual_self_type = self_type.unwrap(); + // Add self type name to <...> clause of function name + let actual_self_type_name = compute_debuginfo_type_name( + cx, + actual_self_type, + true); + + name_to_append_suffix_to.push_str(&actual_self_type_name[..]); + + if generics.is_type_parameterized() { + name_to_append_suffix_to.push_str(","); + } + + // Only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == FullDebugInfo { + let actual_self_type_metadata = type_metadata(cx, + actual_self_type, + codemap::DUMMY_SP); + + let name = token::get_name(special_idents::type_self.name); + + let name = CString::new(name.as_bytes()).unwrap(); + let param_metadata = unsafe { + llvm::LLVMDIBuilderCreateTemplateTypeParameter( + DIB(cx), + file_metadata, + name.as_ptr(), + actual_self_type_metadata, + ptr::null_mut(), + 0, + 0) + }; + + template_params.push(param_metadata); + } + } + + // Handle other generic parameters + let actual_types = param_substs.types.get_slice(subst::FnSpace); + for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() { + let actual_type = actual_types[index]; + // Add actual type name to <...> clause of function name + let actual_type_name = compute_debuginfo_type_name(cx, + actual_type, + true); + name_to_append_suffix_to.push_str(&actual_type_name[..]); + + if index != generics.ty_params.len() - 1 { + name_to_append_suffix_to.push_str(","); + } + + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == FullDebugInfo { + let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP); + let ident = token::get_ident(ident); + let name = CString::new(ident.as_bytes()).unwrap(); + let param_metadata = unsafe { + llvm::LLVMDIBuilderCreateTemplateTypeParameter( + DIB(cx), + file_metadata, + name.as_ptr(), + actual_type_metadata, + ptr::null_mut(), + 0, + 0) + }; + template_params.push(param_metadata); + } + } + + name_to_append_suffix_to.push('>'); + + return create_DIArray(DIB(cx), &template_params[..]); + } +} + +fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + variable_name: ast::Name, + variable_type: Ty<'tcx>, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { + let cx: &CrateContext = bcx.ccx(); + + let filename = span_start(cx, span).file.name.clone(); + let file_metadata = file_metadata(cx, &filename[..]); + + let name = token::get_name(variable_name); + let loc = span_start(cx, span); + let type_metadata = type_metadata(cx, variable_type, span); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable | + CapturedVariable => (0, DW_TAG_auto_variable) + }; + + let name = CString::new(name.as_bytes()).unwrap(); + match (variable_access, &[][..]) { + (DirectVariable { alloca }, address_operations) | + (IndirectVariable {alloca, address_operations}, _) => { + let metadata = unsafe { + llvm::LLVMDIBuilderCreateVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name.as_ptr(), + file_metadata, + loc.line as c_uint, + type_metadata, + cx.sess().opts.optimize != config::No, + 0, + address_operations.as_ptr(), + address_operations.len() as c_uint, + argument_index) + }; + source_loc::set_debug_location(cx, InternalDebugLocation::new(scope_metadata, + loc.line, + loc.col.to_usize())); + unsafe { + let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( + DIB(cx), + alloca, + metadata, + address_operations.as_ptr(), + address_operations.len() as c_uint, + bcx.llbb); + + llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); + } + } + } + + match variable_kind { + ArgumentVariable(_) | CapturedVariable => { + assert!(!bcx.fcx + .debug_context + .get_ref(cx, span) + .source_locations_enabled + .get()); + source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation); + } + _ => { /* nothing to do */ } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum DebugLoc { + At(ast::NodeId, Span), + None +} + +impl DebugLoc { + pub fn apply(&self, fcx: &FunctionContext) { + match *self { + DebugLoc::At(node_id, span) => { + source_loc::set_source_location(fcx, node_id, span); + } + DebugLoc::None => { + source_loc::clear_source_location(fcx); + } + } + } +} + +pub trait ToDebugLoc { + fn debug_loc(&self) -> DebugLoc; +} + +impl ToDebugLoc for ast::Expr { + fn debug_loc(&self) -> DebugLoc { + DebugLoc::At(self.id, self.span) + } +} + +impl ToDebugLoc for NodeIdAndSpan { + fn debug_loc(&self) -> DebugLoc { + DebugLoc::At(self.id, self.span) + } +} + +impl ToDebugLoc for Option { + fn debug_loc(&self) -> DebugLoc { + match *self { + Some(NodeIdAndSpan { id, span }) => DebugLoc::At(id, span), + None => DebugLoc::None + } + } +} diff --git a/src/librustc_trans/trans/debuginfo/namespace.rs b/src/librustc_trans/trans/debuginfo/namespace.rs new file mode 100644 index 0000000000000..0aa0408c0ef33 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/namespace.rs @@ -0,0 +1,134 @@ +// 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. + +// Namespace Handling. + +use super::utils::{DIB, debug_context}; + +use llvm; +use llvm::debuginfo::DIScope; +use trans::common::CrateContext; +use middle::ty::{self, ClosureTyper}; + +use std::ffi::CString; +use std::ptr; +use std::rc::{Rc, Weak}; +use syntax::{ast, ast_map}; +use syntax::parse::token; + +pub struct NamespaceTreeNode { + pub name: ast::Name, + pub scope: DIScope, + pub parent: Option>, +} + +impl NamespaceTreeNode { + pub fn mangled_name_of_contained_item(&self, item_name: &str) -> String { + fn fill_nested(node: &NamespaceTreeNode, output: &mut String) { + match node.parent { + Some(ref parent) => fill_nested(&*parent.upgrade().unwrap(), output), + None => {} + } + let string = token::get_name(node.name); + output.push_str(&format!("{}", string.len())); + output.push_str(&string); + } + + let mut name = String::from_str("_ZN"); + fill_nested(self, &mut name); + name.push_str(&format!("{}", item_name.len())); + name.push_str(item_name); + name.push('E'); + name + } +} + +pub fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str { + &cx.link_meta().crate_name +} + +pub fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc { + ty::with_path(cx.tcx(), def_id, |path| { + // prepend crate name if not already present + let krate = if def_id.krate == ast::LOCAL_CRATE { + let crate_namespace_name = token::intern(crate_root_namespace(cx)); + Some(ast_map::PathMod(crate_namespace_name)) + } else { + None + }; + let mut path = krate.into_iter().chain(path).peekable(); + + let mut current_key = Vec::new(); + let mut parent_node: Option> = None; + + // Create/Lookup namespace for each element of the path. + loop { + // Emulate a for loop so we can use peek below. + let path_element = match path.next() { + Some(e) => e, + None => break + }; + // Ignore the name of the item (the last path element). + if path.peek().is_none() { + break; + } + + let name = path_element.name(); + current_key.push(name); + + let existing_node = debug_context(cx).namespace_map.borrow() + .get(¤t_key).cloned(); + let current_node = match existing_node { + Some(existing_node) => existing_node, + None => { + // create and insert + let parent_scope = match parent_node { + Some(ref node) => node.scope, + None => ptr::null_mut() + }; + let namespace_name = token::get_name(name); + let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); + let scope = unsafe { + llvm::LLVMDIBuilderCreateNameSpace( + DIB(cx), + parent_scope, + namespace_name.as_ptr(), + // cannot reconstruct file ... + ptr::null_mut(), + // ... or line information, but that's not so important. + 0) + }; + + let node = Rc::new(NamespaceTreeNode { + name: name, + scope: scope, + parent: parent_node.map(|parent| parent.downgrade()), + }); + + debug_context(cx).namespace_map.borrow_mut() + .insert(current_key.clone(), node.clone()); + + node + } + }; + + parent_node = Some(current_node); + } + + match parent_node { + Some(node) => node, + None => { + cx.sess().bug(&format!("debuginfo::namespace_for_item(): \ + path too short for {:?}", + def_id)); + } + } + }) +} diff --git a/src/librustc_trans/trans/debuginfo/source_loc.rs b/src/librustc_trans/trans/debuginfo/source_loc.rs new file mode 100644 index 0000000000000..981a23fd664a9 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/source_loc.rs @@ -0,0 +1,231 @@ +// 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 self::InternalDebugLocation::*; + +use super::utils::{debug_context, span_start, fn_should_be_ignored}; +use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER}; +use super::{FunctionDebugContext, DebugLoc}; + +use llvm; +use llvm::debuginfo::DIScope; +use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext}; + +use libc::c_uint; +use std::ptr; +use syntax::codemap::{Span, Pos}; +use syntax::{ast, codemap}; + +pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + node_id: ast::NodeId, + node_span: Span, + is_block: bool) + -> NodeIdAndSpan { + // A debug location needs two things: + // (1) A span (of which only the beginning will actually be used) + // (2) An AST node-id which will be used to look up the lexical scope + // for the location in the functions scope-map + // + // This function will calculate the debug location for compiler-generated + // cleanup calls that are executed when control-flow leaves the + // scope identified by `node_id`. + // + // For everything but block-like things we can simply take id and span of + // the given expression, meaning that from a debugger's view cleanup code is + // executed at the same source location as the statement/expr itself. + // + // Blocks are a special case. Here we want the cleanup to be linked to the + // closing curly brace of the block. The *scope* the cleanup is executed in + // is up to debate: It could either still be *within* the block being + // cleaned up, meaning that locals from the block are still visible in the + // debugger. + // Or it could be in the scope that the block is contained in, so any locals + // from within the block are already considered out-of-scope and thus not + // accessible in the debugger anymore. + // + // The current implementation opts for the second option: cleanup of a block + // already happens in the parent scope of the block. The main reason for + // this decision is that scoping becomes controlflow dependent when variable + // shadowing is involved and it's impossible to decide statically which + // scope is actually left when the cleanup code is executed. + // In practice it shouldn't make much of a difference. + + let mut cleanup_span = node_span; + + if is_block { + // Not all blocks actually have curly braces (e.g. simple closure + // bodies), in which case we also just want to return the span of the + // whole expression. + let code_snippet = cx.sess().codemap().span_to_snippet(node_span); + if let Ok(code_snippet) = code_snippet { + let bytes = code_snippet.as_bytes(); + + if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" { + cleanup_span = Span { + lo: node_span.hi - codemap::BytePos(1), + hi: node_span.hi, + expn_id: node_span.expn_id + }; + } + } + } + + NodeIdAndSpan { + id: node_id, + span: cleanup_span + } +} + + +/// Sets the current debug location at the beginning of the span. +/// +/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id +/// parameter is used to reliably find the correct visibility scope for the code +/// position. +pub fn set_source_location(fcx: &FunctionContext, + node_id: ast::NodeId, + span: Span) { + match fcx.debug_context { + FunctionDebugContext::DebugInfoDisabled => return, + FunctionDebugContext::FunctionWithoutDebugInfo => { + set_debug_location(fcx.ccx, UnknownLocation); + return; + } + FunctionDebugContext::RegularContext(box ref function_debug_context) => { + if function_debug_context.source_location_override.get() { + // Just ignore any attempts to set a new debug location while + // the override is active. + return; + } + + let cx = fcx.ccx; + + debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); + + if function_debug_context.source_locations_enabled.get() { + let loc = span_start(cx, span); + let scope = scope_metadata(fcx, node_id, span); + + set_debug_location(cx, InternalDebugLocation::new(scope, + loc.line, + loc.col.to_usize())); + } else { + set_debug_location(cx, UnknownLocation); + } + } + } +} + +/// This function makes sure that all debug locations emitted while executing +/// `wrapped_function` are set to the given `debug_loc`. +pub fn with_source_location_override(fcx: &FunctionContext, + debug_loc: DebugLoc, + wrapped_function: F) -> R + where F: FnOnce() -> R +{ + match fcx.debug_context { + FunctionDebugContext::DebugInfoDisabled => { + wrapped_function() + } + FunctionDebugContext::FunctionWithoutDebugInfo => { + set_debug_location(fcx.ccx, UnknownLocation); + wrapped_function() + } + FunctionDebugContext::RegularContext(box ref function_debug_context) => { + if function_debug_context.source_location_override.get() { + wrapped_function() + } else { + debug_loc.apply(fcx); + function_debug_context.source_location_override.set(true); + let result = wrapped_function(); + function_debug_context.source_location_override.set(false); + result + } + } + } +} + +/// Clears the current debug location. +/// +/// Instructions generated hereafter won't be assigned a source location. +pub fn clear_source_location(fcx: &FunctionContext) { + if fn_should_be_ignored(fcx) { + return; + } + + set_debug_location(fcx.ccx, UnknownLocation); +} + +/// Enables emitting source locations for the given functions. +/// +/// Since we don't want source locations to be emitted for the function prelude, +/// they are disabled when beginning to translate a new function. This functions +/// switches source location emitting on and must therefore be called before the +/// first real statement/expression of the function is translated. +pub fn start_emitting_source_locations(fcx: &FunctionContext) { + match fcx.debug_context { + FunctionDebugContext::RegularContext(box ref data) => { + data.source_locations_enabled.set(true) + }, + _ => { /* safe to ignore */ } + } +} + + +#[derive(Copy, Clone, PartialEq)] +pub enum InternalDebugLocation { + KnownLocation { scope: DIScope, line: usize, col: usize }, + UnknownLocation +} + +impl InternalDebugLocation { + pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { + KnownLocation { + scope: scope, + line: line, + col: col, + } + } +} + +pub fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) { + if debug_location == debug_context(cx).current_debug_location.get() { + return; + } + + let metadata_node; + + match debug_location { + KnownLocation { scope, line, .. } => { + // Always set the column to zero like Clang and GCC + let col = UNKNOWN_COLUMN_NUMBER; + debug!("setting debug location to {} {}", line, col); + + unsafe { + metadata_node = llvm::LLVMDIBuilderCreateDebugLocation( + debug_context(cx).llcontext, + line as c_uint, + col as c_uint, + scope, + ptr::null_mut()); + } + } + UnknownLocation => { + debug!("clearing debug location "); + metadata_node = ptr::null_mut(); + } + }; + + unsafe { + llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); + } + + debug_context(cx).current_debug_location.set(debug_location); +} diff --git a/src/librustc_trans/trans/debuginfo/type_names.rs b/src/librustc_trans/trans/debuginfo/type_names.rs new file mode 100644 index 0000000000000..2d0003d93a5d2 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/type_names.rs @@ -0,0 +1,230 @@ +// 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. + +// Type Names for Debug Info. + +use super::namespace::crate_root_namespace; + +use trans::common::CrateContext; +use middle::subst::{self, Substs}; +use middle::ty::{self, Ty, ClosureTyper}; +use syntax::ast; +use syntax::parse::token; +use util::ppaux; + + +// Compute the name of the type as it should be stored in debuginfo. Does not do +// any caching, i.e. calling the function twice with the same type will also do +// the work twice. The `qualified` parameter only affects the first level of the +// type name, further levels (i.e. type parameters) are always fully qualified. +pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool) + -> String { + let mut result = String::with_capacity(64); + push_debuginfo_type_name(cx, t, qualified, &mut result); + result +} + +// Pushes the name of the type as it should be stored in debuginfo on the +// `output` String. See also compute_debuginfo_type_name(). +pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool, + output: &mut String) { + match t.sty { + ty::ty_bool => output.push_str("bool"), + ty::ty_char => output.push_str("char"), + ty::ty_str => output.push_str("str"), + ty::ty_int(ast::TyIs) => output.push_str("isize"), + ty::ty_int(ast::TyI8) => output.push_str("i8"), + ty::ty_int(ast::TyI16) => output.push_str("i16"), + ty::ty_int(ast::TyI32) => output.push_str("i32"), + ty::ty_int(ast::TyI64) => output.push_str("i64"), + ty::ty_uint(ast::TyUs) => output.push_str("usize"), + ty::ty_uint(ast::TyU8) => output.push_str("u8"), + ty::ty_uint(ast::TyU16) => output.push_str("u16"), + ty::ty_uint(ast::TyU32) => output.push_str("u32"), + ty::ty_uint(ast::TyU64) => output.push_str("u64"), + ty::ty_float(ast::TyF32) => output.push_str("f32"), + ty::ty_float(ast::TyF64) => output.push_str("f64"), + ty::ty_struct(def_id, substs) | + ty::ty_enum(def_id, substs) => { + push_item_name(cx, def_id, qualified, output); + push_type_params(cx, substs, output); + }, + ty::ty_tup(ref component_types) => { + output.push('('); + for &component_type in component_types { + push_debuginfo_type_name(cx, component_type, true, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::ty_uniq(inner_type) => { + output.push_str("Box<"); + push_debuginfo_type_name(cx, inner_type, true, output); + output.push('>'); + }, + ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => { + output.push('*'); + match mutbl { + ast::MutImmutable => output.push_str("const "), + ast::MutMutable => output.push_str("mut "), + } + + push_debuginfo_type_name(cx, inner_type, true, output); + }, + ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => { + output.push('&'); + if mutbl == ast::MutMutable { + output.push_str("mut "); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + }, + ty::ty_vec(inner_type, optional_length) => { + output.push('['); + push_debuginfo_type_name(cx, inner_type, true, output); + + match optional_length { + Some(len) => { + output.push_str(&format!("; {}", len)); + } + None => { /* nothing to do */ } + }; + + output.push(']'); + }, + ty::ty_trait(ref trait_data) => { + let principal = ty::erase_late_bound_regions(cx.tcx(), &trait_data.principal); + push_item_name(cx, principal.def_id, false, output); + push_type_params(cx, principal.substs, output); + }, + ty::ty_bare_fn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => { + if unsafety == ast::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + if abi != ::syntax::abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = ty::erase_late_bound_regions(cx.tcx(), sig); + if !sig.inputs.is_empty() { + for ¶meter_type in &sig.inputs { + push_debuginfo_type_name(cx, parameter_type, true, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs.is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + match sig.output { + ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_debuginfo_type_name(cx, result_type, true, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } + } + }, + ty::ty_closure(..) => { + output.push_str("closure"); + } + ty::ty_err | + ty::ty_infer(_) | + ty::ty_projection(..) | + ty::ty_param(_) => { + cx.sess().bug(&format!("debuginfo: Trying to create type name for \ + unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t))); + } + } + + fn push_item_name(cx: &CrateContext, + def_id: ast::DefId, + qualified: bool, + output: &mut String) { + ty::with_path(cx.tcx(), def_id, |path| { + if qualified { + if def_id.krate == ast::LOCAL_CRATE { + output.push_str(crate_root_namespace(cx)); + output.push_str("::"); + } + + let mut path_element_count = 0; + for path_element in path { + let name = token::get_name(path_element.name()); + output.push_str(&name); + output.push_str("::"); + path_element_count += 1; + } + + if path_element_count == 0 { + cx.sess().bug("debuginfo: Encountered empty item path!"); + } + + output.pop(); + output.pop(); + } else { + let name = token::get_name(path.last() + .expect("debuginfo: Empty item path?") + .name()); + output.push_str(&name); + } + }); + } + + // Pushes the type parameters in the given `Substs` to the output string. + // This ignores region parameters, since they can't reliably be + // reconstructed for items from non-local crates. For local crates, this + // would be possible but with inlining and LTO we have to use the least + // common denominator - otherwise we would run into conflicts. + fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, + substs: &subst::Substs<'tcx>, + output: &mut String) { + if substs.types.is_empty() { + return; + } + + output.push('<'); + + for &type_parameter in substs.types.iter() { + push_debuginfo_type_name(cx, type_parameter, true, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } +} + diff --git a/src/librustc_trans/trans/debuginfo/utils.rs b/src/librustc_trans/trans/debuginfo/utils.rs new file mode 100644 index 0000000000000..0c12f6ed095f1 --- /dev/null +++ b/src/librustc_trans/trans/debuginfo/utils.rs @@ -0,0 +1,108 @@ +// 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. + +// Utility Functions. + +use super::{FunctionDebugContext, CrateDebugContext}; +use super::namespace::namespace_for_item; + +use llvm; +use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; +use trans::machine; +use trans::common::{CrateContext, FunctionContext}; +use trans::type_::Type; + +use syntax::codemap::Span; +use syntax::{ast, codemap}; + +pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool +{ + // The is_local_to_unit flag indicates whether a function is local to the + // current compilation unit (i.e. if it is *static* in the C-sense). The + // *reachable* set should provide a good approximation of this, as it + // contains everything that might leak out of the current crate (by being + // externally visible or by being inlined into something externally + // visible). It might better to use the `exported_items` set from + // `driver::CrateAnalysis` in the future, but (atm) this set is not + // available in the translation pass. + !cx.reachable().contains(&node_id) +} + +#[allow(non_snake_case)] +pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { + return unsafe { + llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) + }; +} + +pub fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool { + attributes.iter().any(|attr| { + let meta_item: &ast::MetaItem = &*attr.node.value; + match meta_item.node { + ast::MetaWord(ref value) => &value[..] == "no_debug", + _ => false + } + }) +} + +/// Return codemap::Loc corresponding to the beginning of the span +pub fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc { + cx.sess().codemap().lookup_char_pos(span.lo) +} + +pub fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) { + (machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64) +} + +pub fn bytes_to_bits(bytes: u64) -> u64 { + bytes * 8 +} + +#[inline] +pub fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>) + -> &'a CrateDebugContext<'tcx> { + let debug_context: &'a CrateDebugContext<'tcx> = cx.dbg_cx().as_ref().unwrap(); + debug_context +} + +#[inline] +#[allow(non_snake_case)] +pub fn DIB(cx: &CrateContext) -> DIBuilderRef { + cx.dbg_cx().as_ref().unwrap().builder +} + +pub fn fn_should_be_ignored(fcx: &FunctionContext) -> bool { + match fcx.debug_context { + FunctionDebugContext::RegularContext(_) => false, + _ => true + } +} + +pub fn assert_type_for_node_id(cx: &CrateContext, + node_id: ast::NodeId, + error_reporting_span: Span) { + if !cx.tcx().node_types().contains_key(&node_id) { + cx.sess().span_bug(error_reporting_span, + "debuginfo: Could not find type for node id!"); + } +} + +pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId) + -> (DIScope, Span) { + let containing_scope = namespace_for_item(cx, def_id).scope; + let definition_span = if def_id.krate == ast::LOCAL_CRATE { + cx.tcx().map.span(def_id.node) + } else { + // For external items there is no span information + codemap::DUMMY_SP + }; + + (containing_scope, definition_span) +} diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 9e7449f670f0f..35946491ba176 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -71,12 +71,12 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: llvm::SetUnnamedAddr(llfn, true); if output == ty::FnDiverging { - llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute); + llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoReturnAttribute); } if ccx.tcx().sess.opts.cg.no_redzone .unwrap_or(ccx.tcx().sess.target.target.options.disable_redzone) { - llvm::SetFunctionAttribute(llfn, llvm::NoRedZoneAttribute) + llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoRedZoneAttribute) } if ccx.is_split_stack_supported() && !ccx.sess().opts.cg.no_stack_check { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 27919d645b695..f5ee44d69cc63 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -126,8 +126,11 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap(); - if !qualif.intersects(check_const::NOT_CONST | check_const::NEEDS_DROP) { - if !qualif.intersects(check_const::PREFER_IN_PLACE) { + if !qualif.intersects( + check_const::ConstQualif::NOT_CONST | + check_const::ConstQualif::NEEDS_DROP + ) { + if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) { if let SaveIn(lldest) = dest { let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, bcx.fcx.param_substs); @@ -209,12 +212,15 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let mut bcx = bcx; let fcx = bcx.fcx; let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap(); - let adjusted_global = !qualif.intersects(check_const::NON_STATIC_BORROWS); - let global = if !qualif.intersects(check_const::NOT_CONST | check_const::NEEDS_DROP) { + let adjusted_global = !qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS); + let global = if !qualif.intersects( + check_const::ConstQualif::NOT_CONST | + check_const::ConstQualif::NEEDS_DROP + ) { let global = consts::get_const_expr_as_global(bcx.ccx(), expr, qualif, bcx.fcx.param_substs); - if qualif.intersects(check_const::HAS_STATIC_BORROWS) { + if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) { // Is borrowed as 'static, must return lvalue. // Cast pointer to global, because constants have different types. diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs index c025be2ee9877..d760b2c52ca1b 100644 --- a/src/librustc_trans/trans/foreign.rs +++ b/src/librustc_trans/trans/foreign.rs @@ -349,8 +349,8 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // The outptr can be noalias and nocapture because it's entirely // invisible to the program. We also know it's nonnull as well // as how many bytes we can dereference - attrs.arg(1, llvm::NoAliasAttribute) - .arg(1, llvm::NoCaptureAttribute) + attrs.arg(1, llvm::Attribute::NoAliasAttribute) + .arg(1, llvm::Attribute::NoCaptureAttribute) .arg(1, llvm::DereferenceableAttribute(llret_sz)); }; diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 652f6ad366aaa..a2a9e89ff6351 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -383,7 +383,7 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Issue #23611: schedule cleanup of contents, re-inspecting the // discriminant (if any) in case of variant swap in drop code. - bcx.fcx.schedule_drop_enum_contents(cleanup::CustomScope(contents_scope), v0, t); + bcx.fcx.schedule_drop_adt_contents(cleanup::CustomScope(contents_scope), v0, t); let glue_type = get_drop_glue_type(bcx.ccx(), t); let dtor_ty = ty::mk_ctor_fn(bcx.tcx(), class_did, &[glue_type], ty::mk_nil(bcx.tcx())); diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 6bfa80f9c40b3..7188fdebeecf4 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -144,6 +144,9 @@ pub fn check_intrinsics(ccx: &CrateContext) { ccx.sess().abort_if_errors(); } +/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, +/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, +/// add them to librustc_trans/trans/context.rs pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::NodeId, callee_ty: Ty<'tcx>, @@ -676,6 +679,11 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, llargs[1], call_debug_location), + (_, "unchecked_udiv") => UDiv(bcx, llargs[0], llargs[1], call_debug_location), + (_, "unchecked_sdiv") => SDiv(bcx, llargs[0], llargs[1], call_debug_location), + (_, "unchecked_urem") => URem(bcx, llargs[0], llargs[1], call_debug_location), + (_, "unchecked_srem") => SRem(bcx, llargs[0], llargs[1], call_debug_location), + (_, "overflowing_add") => Add(bcx, llargs[0], llargs[1], call_debug_location), (_, "overflowing_sub") => Sub(bcx, llargs[0], llargs[1], call_debug_location), (_, "overflowing_mul") => Mul(bcx, llargs[0], llargs[1], call_debug_location), @@ -763,7 +771,12 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } "fence" => { - AtomicFence(bcx, order); + AtomicFence(bcx, order, llvm::CrossThread); + C_nil(ccx) + } + + "singlethreadfence" => { + AtomicFence(bcx, order, llvm::SingleThread); C_nil(ccx) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f9c78cd36e6ac..cb5b569fd79cb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4882,6 +4882,8 @@ pub fn check_bounds_are_used<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } } +/// Remember to add all intrinsics here, in librustc_trans/trans/intrinsic.rs, +/// and in libcore/intrinsics.rs pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { fn param<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, n: u32) -> Ty<'tcx> { let name = token::intern(&format!("P{}", n)); @@ -4910,7 +4912,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)), param(ccx, 0)), param(ccx, 0)) } - "fence" => { + "fence" | "singlethreadfence" => { (0, Vec::new(), ty::mk_nil(tcx)) } op => { @@ -5119,6 +5121,9 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) { (0, vec!(tcx.types.u64, tcx.types.u64), ty::mk_tup(tcx, vec!(tcx.types.u64, tcx.types.bool))), + "unchecked_udiv" | "unchecked_sdiv" | "unchecked_urem" | "unchecked_srem" => + (1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0)), + "overflowing_add" | "overflowing_sub" | "overflowing_mul" => (1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0)), diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b5dfbf796d3bc..2b15a4ff83ed1 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -20,6 +20,7 @@ use core::prelude::*; use fmt; +use ffi::OsString; use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write}; use path::{Path, PathBuf}; use sys::fs2 as fs_imp; @@ -146,6 +147,20 @@ pub struct OpenOptions(fs_imp::OpenOptions); #[stable(feature = "rust1", since = "1.0.0")] pub struct Permissions(fs_imp::FilePermissions); +/// An structure representing a type of file with accessors for each file type. +#[unstable(feature = "file_type", reason = "recently added API")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FileType(fs_imp::FileType); + +/// A builder used to create directories in various manners. +/// +/// This builder also supports platform-specific options. +#[unstable(feature = "dir_builder", reason = "recently added API")] +pub struct DirBuilder { + inner: fs_imp::DirBuilder, + recursive: bool, +} + impl File { /// Attempts to open a file in read-only mode. /// @@ -485,6 +500,12 @@ impl AsInnerMut for OpenOptions { } impl Metadata { + /// Returns the file type for this metadata. + #[unstable(feature = "file_type", reason = "recently added API")] + pub fn file_type(&self) -> FileType { + FileType(self.0.file_type()) + } + /// Returns whether this metadata is for a directory. /// /// # Examples @@ -500,7 +521,7 @@ impl Metadata { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_dir(&self) -> bool { self.0.is_dir() } + pub fn is_dir(&self) -> bool { self.file_type().is_dir() } /// Returns whether this metadata is for a regular file. /// @@ -517,7 +538,7 @@ impl Metadata { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_file(&self) -> bool { self.0.is_file() } + pub fn is_file(&self) -> bool { self.file_type().is_file() } /// Returns the size of the file, in bytes, this metadata is for. /// @@ -562,7 +583,11 @@ impl Metadata { reason = "the return type of u64 is not quite appropriate for \ this method and may change if the standard library \ gains a type to represent a moment in time")] - pub fn accessed(&self) -> u64 { self.0.accessed() } + #[deprecated(since = "1.1.0", + reason = "use os::platform::fs::MetadataExt extension traits")] + pub fn accessed(&self) -> u64 { + self.adjust_time(self.0.accessed()) + } /// Returns the most recent modification time for a file. /// @@ -571,7 +596,21 @@ impl Metadata { reason = "the return type of u64 is not quite appropriate for \ this method and may change if the standard library \ gains a type to represent a moment in time")] - pub fn modified(&self) -> u64 { self.0.modified() } + #[deprecated(since = "1.1.0", + reason = "use os::platform::fs::MetadataExt extension traits")] + pub fn modified(&self) -> u64 { + self.adjust_time(self.0.modified()) + } + + fn adjust_time(&self, val: u64) -> u64 { + // FILETIME (what `val` represents) is in 100ns intervals and there are + // 10000 intervals in a millisecond. + if cfg!(windows) {val / 10000} else {val} + } +} + +impl AsInner for Metadata { + fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } impl Permissions { @@ -624,6 +663,18 @@ impl Permissions { } } +#[unstable(feature = "file_type", reason = "recently added API")] +impl FileType { + /// Test whether this file type represents a directory. + pub fn is_dir(&self) -> bool { self.0.is_dir() } + + /// Test whether this file type represents a regular file. + pub fn is_file(&self) -> bool { self.0.is_file() } + + /// Test whether this file type represents a symbolic link. + pub fn is_symlink(&self) -> bool { self.0.is_symlink() } +} + impl FromInner for Permissions { fn from_inner(f: fs_imp::FilePermissions) -> Permissions { Permissions(f) @@ -674,6 +725,47 @@ impl DirEntry { /// The exact text, of course, depends on what files you have in `.`. #[stable(feature = "rust1", since = "1.0.0")] pub fn path(&self) -> PathBuf { self.0.path() } + + /// Return the metadata for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform behavior + /// + /// On Windows this function is cheap to call (no extra system calls + /// needed), but on Unix platforms this function is the equivalent of + /// calling `symlink_metadata` on the path. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn metadata(&self) -> io::Result { + self.0.metadata().map(Metadata) + } + + /// Return the file type for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform behavior + /// + /// On Windows and most Unix platforms this function is free (no extra + /// system calls needed), but some Unix platforms may require the equivalent + /// call to `symlink_metadata` to learn about the target file type. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn file_type(&self) -> io::Result { + self.0.file_type().map(FileType) + } + + /// Returns the bare file name of this directory entry without any other + /// leading path component. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +impl AsInner for DirEntry { + fn as_inner(&self) -> &fs_imp::DirEntry { &self.0 } } /// Removes a file from the underlying filesystem. @@ -731,6 +823,25 @@ pub fn metadata>(path: P) -> io::Result { fs_imp::stat(path.as_ref()).map(Metadata) } +/// Query the metadata about a file without following symlinks. +/// +/// # Examples +/// +/// ```rust +/// #![feature(symlink_metadata)] +/// # fn foo() -> std::io::Result<()> { +/// use std::fs; +/// +/// let attr = try!(fs::symlink_metadata("/some/file/path.txt")); +/// // inspect attr ... +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "symlink_metadata", reason = "recently added API")] +pub fn symlink_metadata>(path: P) -> io::Result { + fs_imp::lstat(path.as_ref()).map(Metadata) +} + /// Rename a file or directory to a new name. /// /// # Errors @@ -869,6 +980,13 @@ pub fn read_link>(path: P) -> io::Result { fs_imp::readlink(path.as_ref()) } +/// Returns the canonical form of a path with all intermediate components +/// normalized and symbolic links resolved. +#[unstable(feature = "fs_canonicalize", reason = "recently added API")] +pub fn canonicalize>(path: P) -> io::Result { + fs_imp::canonicalize(path.as_ref()) +} + /// Creates a new, empty directory at the provided path /// /// # Errors @@ -888,7 +1006,7 @@ pub fn read_link>(path: P) -> io::Result { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn create_dir>(path: P) -> io::Result<()> { - fs_imp::mkdir(path.as_ref()) + DirBuilder::new().create(path.as_ref()) } /// Recursively create a directory and all of its parent components if they @@ -913,10 +1031,7 @@ pub fn create_dir>(path: P) -> io::Result<()> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn create_dir_all>(path: P) -> io::Result<()> { - let path = path.as_ref(); - if path == Path::new("") || path.is_dir() { return Ok(()) } - if let Some(p) = path.parent() { try!(create_dir_all(p)) } - create_dir(path) + DirBuilder::new().recursive(true).create(path.as_ref()) } /// Removes an existing, empty directory. @@ -966,19 +1081,14 @@ pub fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref(); for child in try!(read_dir(path)) { let child = try!(child).path(); - let stat = try!(lstat(&*child)); + let stat = try!(symlink_metadata(&*child)); if stat.is_dir() { try!(remove_dir_all(&*child)); } else { try!(remove_file(&*child)); } } - return remove_dir(path); - - #[cfg(unix)] - fn lstat(path: &Path) -> io::Result { fs_imp::lstat(path) } - #[cfg(windows)] - fn lstat(path: &Path) -> io::Result { fs_imp::stat(path) } + remove_dir(path) } /// Returns an iterator over the entries within a directory. @@ -1073,11 +1183,37 @@ impl Iterator for WalkDir { pub trait PathExt { /// Gets information on the file, directory, etc at this path. /// - /// Consult the `fs::stat` documentation for more info. + /// Consult the `fs::metadata` documentation for more info. /// - /// This call preserves identical runtime/error semantics with `file::stat`. + /// This call preserves identical runtime/error semantics with + /// `fs::metadata`. fn metadata(&self) -> io::Result; + /// Gets information on the file, directory, etc at this path. + /// + /// Consult the `fs::symlink_metadata` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with + /// `fs::symlink_metadata`. + fn symlink_metadata(&self) -> io::Result; + + /// Returns the canonical form of a path, normalizing all components and + /// eliminate all symlinks. + /// + /// This call preserves identical runtime/error semantics with + /// `fs::canonicalize`. + fn canonicalize(&self) -> io::Result; + + /// Reads the symlink at this path. + /// + /// For more information see `fs::read_link`. + fn read_link(&self) -> io::Result; + + /// Reads the directory at this path. + /// + /// For more information see `fs::read_dir`. + fn read_dir(&self) -> io::Result; + /// Boolean value indicator whether the underlying file exists on the local /// filesystem. Returns false in exactly the cases where `fs::stat` fails. fn exists(&self) -> bool; @@ -1098,12 +1234,16 @@ pub trait PathExt { impl PathExt for Path { fn metadata(&self) -> io::Result { metadata(self) } - + fn symlink_metadata(&self) -> io::Result { symlink_metadata(self) } + fn canonicalize(&self) -> io::Result { canonicalize(self) } + fn read_link(&self) -> io::Result { read_link(self) } + fn read_dir(&self) -> io::Result { read_dir(self) } fn exists(&self) -> bool { metadata(self).is_ok() } fn is_file(&self) -> bool { metadata(self).map(|s| s.is_file()).unwrap_or(false) } + fn is_dir(&self) -> bool { metadata(self).map(|s| s.is_dir()).unwrap_or(false) } @@ -1152,6 +1292,52 @@ pub fn set_permissions>(path: P, perm: Permissions) -> io::Result fs_imp::set_perm(path.as_ref(), perm.0) } +impl DirBuilder { + /// Creates a new set of options with default mode/security settings for all + /// platforms and also non-recursive. + pub fn new() -> DirBuilder { + DirBuilder { + inner: fs_imp::DirBuilder::new(), + recursive: false, + } + } + + /// Indicate that directories create should be created recursively, creating + /// all parent directories if they do not exist with the same security and + /// permissions settings. + /// + /// This option defaults to `false` + pub fn recursive(&mut self, recursive: bool) -> &mut Self { + self.recursive = recursive; + self + } + + /// Create the specified directory with the options configured in this + /// builder. + pub fn create>(&self, path: P) -> io::Result<()> { + let path = path.as_ref(); + if self.recursive { + self.create_dir_all(path) + } else { + self.inner.mkdir(path) + } + } + + fn create_dir_all(&self, path: &Path) -> io::Result<()> { + if path == Path::new("") || path.is_dir() { return Ok(()) } + if let Some(p) = path.parent() { + try!(self.create_dir_all(p)) + } + self.inner.mkdir(path) + } +} + +impl AsInnerMut for DirBuilder { + fn as_inner_mut(&mut self) -> &mut fs_imp::DirBuilder { + &mut self.inner + } +} + #[cfg(test)] mod tests { #![allow(deprecated)] //rand @@ -1924,4 +2110,74 @@ mod tests { let path = tmpdir.join("file"); check!(fs::create_dir_all(&path.join("a/"))); } + + #[test] + #[cfg(not(windows))] + fn realpath_works() { + let tmpdir = tmpdir(); + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + let file = tmpdir.join("test"); + let dir = tmpdir.join("test2"); + let link = dir.join("link"); + let linkdir = tmpdir.join("test3"); + + File::create(&file).unwrap(); + fs::create_dir(&dir).unwrap(); + fs::soft_link(&file, &link).unwrap(); + fs::soft_link(&dir, &linkdir).unwrap(); + + assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); + + assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); + assert_eq!(fs::canonicalize(&file).unwrap(), file); + assert_eq!(fs::canonicalize(&link).unwrap(), file); + assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); + assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); + } + + #[test] + #[cfg(not(windows))] + fn realpath_works_tricky() { + let tmpdir = tmpdir(); + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + + let a = tmpdir.join("a"); + let b = a.join("b"); + let c = b.join("c"); + let d = a.join("d"); + let e = d.join("e"); + let f = a.join("f"); + + fs::create_dir_all(&b).unwrap(); + fs::create_dir_all(&d).unwrap(); + File::create(&f).unwrap(); + fs::soft_link("../d/e", &c).unwrap(); + fs::soft_link("../f", &e).unwrap(); + + assert_eq!(fs::canonicalize(&c).unwrap(), f); + assert_eq!(fs::canonicalize(&e).unwrap(), f); + } + + #[test] + fn dir_entry_methods() { + let tmpdir = tmpdir(); + + fs::create_dir_all(&tmpdir.join("a")).unwrap(); + File::create(&tmpdir.join("b")).unwrap(); + + for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { + let fname = file.file_name(); + match fname.to_str() { + Some("a") => { + assert!(file.file_type().unwrap().is_dir()); + assert!(file.metadata().unwrap().is_dir()); + } + Some("b") => { + assert!(file.file_type().unwrap().is_file()); + assert!(file.metadata().unwrap().is_file()); + } + f => panic!("unknown file name: {:?}", f), + } + } + } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 8473b24172edd..6a84c6ace47b4 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -107,26 +107,27 @@ #![doc(test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))] #![feature(alloc)] +#![feature(allow_internal_unstable)] +#![feature(associated_consts)] #![feature(box_syntax)] #![feature(collections)] #![feature(core)] +#![feature(debug_builders)] +#![feature(into_cow)] #![feature(lang_items)] #![feature(libc)] #![feature(linkage, thread_local, asm)] +#![feature(macro_reexport)] #![feature(optin_builtin_traits)] #![feature(rand)] +#![feature(slice_patterns)] #![feature(staged_api)] +#![feature(std_misc)] +#![feature(str_char)] #![feature(unboxed_closures)] #![feature(unicode)] -#![feature(unsafe_no_drop_flag, filling_drop)] -#![feature(macro_reexport)] #![feature(unique)] -#![feature(allow_internal_unstable)] -#![feature(str_char)] -#![feature(into_cow)] -#![feature(std_misc)] -#![feature(slice_patterns)] -#![feature(debug_builders)] +#![feature(unsafe_no_drop_flag, filling_drop)] #![feature(zero_one)] #![cfg_attr(test, feature(float_from_str_radix))] #![cfg_attr(test, feature(test, rustc_private, std_misc))] @@ -307,13 +308,12 @@ mod std { pub use sync; // used for select!() pub use error; // used for try!() pub use fmt; // used for any formatting strings - pub use option; // used for bitflags!{} + pub use option; // used for thread_local!{} pub use rt; // used for panic!() pub use vec; // used for vec![] pub use cell; // used for tls! pub use thread; // used for thread_local! pub use marker; // used for tls! - pub use ops; // used for bitflags! // The test runner calls ::std::env::args() but really wants realstd #[cfg(test)] pub use realstd::env as env; diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 2e34e46726fb2..b0bf9d0f80626 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -41,7 +41,7 @@ pub enum SocketAddr { #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV4 { inner: libc::sockaddr_in } -/// An IPv6 socket address +/// An IPv6 socket address. #[derive(Copy)] #[stable(feature = "rust1", since = "1.0.0")] pub struct SocketAddrV6 { inner: libc::sockaddr_in6 } @@ -56,7 +56,7 @@ impl SocketAddr { } } - /// Gets the IP address associated with this socket address. + /// Returns the IP address associated with this socket address. #[unstable(feature = "ip_addr", reason = "recent addition")] pub fn ip(&self) -> IpAddr { match *self { @@ -65,7 +65,7 @@ impl SocketAddr { } } - /// Gets the port number associated with this socket address + /// Returns the port number associated with this socket address. #[stable(feature = "rust1", since = "1.0.0")] pub fn port(&self) -> u16 { match *self { @@ -89,7 +89,7 @@ impl SocketAddrV4 { } } - /// Gets the IP address associated with this socket address. + /// Returns the IP address associated with this socket address. #[stable(feature = "rust1", since = "1.0.0")] pub fn ip(&self) -> &Ipv4Addr { unsafe { @@ -97,7 +97,7 @@ impl SocketAddrV4 { } } - /// Gets the port number associated with this socket address + /// Returns the port number associated with this socket address. #[stable(feature = "rust1", since = "1.0.0")] pub fn port(&self) -> u16 { ntoh(self.inner.sin_port) } } @@ -120,7 +120,7 @@ impl SocketAddrV6 { } } - /// Gets the IP address associated with this socket address. + /// Returns the IP address associated with this socket address. #[stable(feature = "rust1", since = "1.0.0")] pub fn ip(&self) -> &Ipv6Addr { unsafe { @@ -128,16 +128,16 @@ impl SocketAddrV6 { } } - /// Gets the port number associated with this socket address + /// Returns the port number associated with this socket address. #[stable(feature = "rust1", since = "1.0.0")] pub fn port(&self) -> u16 { ntoh(self.inner.sin6_port) } - /// Gets scope ID associated with this address, corresponding to the + /// Returns scope ID associated with this address, corresponding to the /// `sin6_flowinfo` field in C. #[stable(feature = "rust1", since = "1.0.0")] pub fn flowinfo(&self) -> u32 { ntoh(self.inner.sin6_flowinfo) } - /// Gets scope ID associated with this address, corresponding to the + /// Returns scope ID associated with this address, corresponding to the /// `sin6_scope_id` field in C. #[stable(feature = "rust1", since = "1.0.0")] pub fn scope_id(&self) -> u32 { ntoh(self.inner.sin6_scope_id) } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 065126c6fdbb5..9fd69840f7f05 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -60,7 +60,7 @@ pub enum Ipv6MulticastScope { impl Ipv4Addr { /// Creates a new IPv4 address from four eight-bit octets. /// - /// The result will represent the IP address a.b.c.d + /// The result will represent the IP address `a`.`b`.`c`.`d`. #[stable(feature = "rust1", since = "1.0.0")] pub fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { Ipv4Addr { @@ -73,19 +73,19 @@ impl Ipv4Addr { } } - /// Returns the four eight-bit integers that make up this address + /// Returns the four eight-bit integers that make up this address. #[stable(feature = "rust1", since = "1.0.0")] pub fn octets(&self) -> [u8; 4] { let bits = ntoh(self.inner.s_addr); [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] } - /// Returns true for the special 'unspecified' address 0.0.0.0 + /// Returns true for the special 'unspecified' address 0.0.0.0. pub fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } - /// Returns true if this is a loopback address (127.0.0.0/8) + /// Returns true if this is a loopback address (127.0.0.0/8). pub fn is_loopback(&self) -> bool { self.octets()[0] == 127 } @@ -106,7 +106,7 @@ impl Ipv4Addr { } } - /// Returns true if the address is link-local (169.254.0.0/16) + /// Returns true if the address is link-local (169.254.0.0/16). pub fn is_link_local(&self) -> bool { self.octets()[0] == 169 && self.octets()[1] == 254 } @@ -116,7 +116,7 @@ impl Ipv4Addr { /// Non-globally-routable networks include the private networks (10.0.0.0/8, /// 172.16.0.0/12 and 192.168.0.0/16), the loopback network (127.0.0.0/8), /// the link-local network (169.254.0.0/16), the broadcast address (255.255.255.255/32) and - /// the test networks used for documentation (192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24) + /// the test networks used for documentation (192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24). pub fn is_global(&self) -> bool { !self.is_private() && !self.is_loopback() && !self.is_link_local() && !self.is_broadcast() && !self.is_documentation() @@ -131,13 +131,13 @@ impl Ipv4Addr { /// Returns true if this is a broadcast address. /// - /// A broadcast address has all octets set to 255 as defined in RFC 919 + /// A broadcast address has all octets set to 255 as defined in RFC 919. pub fn is_broadcast(&self) -> bool { self.octets()[0] == 255 && self.octets()[1] == 255 && self.octets()[2] == 255 && self.octets()[3] == 255 } - /// Returns true if this address is in a range designated for documentation + /// Returns true if this address is in a range designated for documentation. /// /// This is defined in RFC 5737 /// - 192.0.2.0/24 (TEST-NET-1) @@ -152,7 +152,7 @@ impl Ipv4Addr { } } - /// Converts this address to an IPv4-compatible IPv6 address + /// Converts this address to an IPv4-compatible IPv6 address. /// /// a.b.c.d becomes ::a.b.c.d #[stable(feature = "rust1", since = "1.0.0")] @@ -162,7 +162,7 @@ impl Ipv4Addr { ((self.octets()[2] as u16) << 8) | self.octets()[3] as u16) } - /// Converts this address to an IPv4-mapped IPv6 address + /// Converts this address to an IPv4-mapped IPv6 address. /// /// a.b.c.d becomes ::ffff:a.b.c.d #[stable(feature = "rust1", since = "1.0.0")] @@ -247,7 +247,7 @@ impl FromInner for Ipv4Addr { impl Ipv6Addr { /// Creates a new IPv6 address from eight 16-bit segments. /// - /// The result will represent the IP address a:b:c:d:e:f:g:h + /// The result will represent the IP address a:b:c:d:e:f:g:h. #[stable(feature = "rust1", since = "1.0.0")] pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { @@ -259,7 +259,7 @@ impl Ipv6Addr { } } - /// Returns the eight 16-bit segments that make up this address + /// Returns the eight 16-bit segments that make up this address. #[stable(feature = "rust1", since = "1.0.0")] pub fn segments(&self) -> [u16; 8] { [ntoh(self.inner.s6_addr[0]), @@ -272,12 +272,12 @@ impl Ipv6Addr { ntoh(self.inner.s6_addr[7])] } - /// Returns true for the special 'unspecified' address :: + /// Returns true for the special 'unspecified' address ::. pub fn is_unspecified(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 0] } - /// Returns true if this is a loopback address (::1) + /// Returns true if this is a loopback address (::1). pub fn is_loopback(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 1] } @@ -295,25 +295,25 @@ impl Ipv6Addr { } } - /// Returns true if this is a unique local address (IPv6) + /// Returns true if this is a unique local address (IPv6). /// - /// Unique local addresses are defined in RFC4193 and have the form fc00::/7 + /// Unique local addresses are defined in RFC4193 and have the form fc00::/7. pub fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } - /// Returns true if the address is unicast and link-local (fe80::/10) + /// Returns true if the address is unicast and link-local (fe80::/10). pub fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } /// Returns true if this is a deprecated unicast site-local address (IPv6 - /// fec0::/10) + /// fec0::/10). pub fn is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } - /// Returns true if the address is a globally routable unicast address + /// Returns true if the address is a globally routable unicast address. /// /// Non-globally-routable unicast addresses include the loopback address, /// the link-local addresses, the deprecated site-local addresses and the diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index 3bfc764e540b2..2e7c0a2c80e59 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -8,10 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Networking primitives for TCP/UDP communication -//! -//! > **NOTE**: This module is very much a work in progress and is under active -//! > development. +//! Networking primitives for TCP/UDP communication. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index e48d0e6008b87..130e1eee8f924 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -125,7 +125,7 @@ impl TcpStream { self.0.duplicate().map(TcpStream) } - /// Sets the nodelay flag on this connection to the boolean specified + /// Sets the nodelay flag on this connection to the boolean specified. pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.0.set_nodelay(nodelay) } diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 1955b895300ea..0b04ecb1b7228 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -50,8 +50,8 @@ pub struct UdpSocket(net_imp::UdpSocket); impl UdpSocket { /// Creates a UDP socket from the given address. /// - /// Address type can be any implementor of `ToSocketAddr` trait. See its - /// documentation for concrete examples. + /// The address type can be any implementor of `ToSocketAddr` trait. See + /// its documentation for concrete examples. #[stable(feature = "rust1", since = "1.0.0")] pub fn bind(addr: A) -> io::Result { super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) @@ -64,8 +64,8 @@ impl UdpSocket { self.0.recv_from(buf) } - /// Sends data on the socket to the given address. Returns nothing on - /// success. + /// Sends data on the socket to the given address. On success, returns the + /// number of bytes written. /// /// Address type can be any implementor of `ToSocketAddrs` trait. See its /// documentation for concrete examples. @@ -95,34 +95,34 @@ impl UdpSocket { self.0.duplicate().map(UdpSocket) } - /// Sets the broadcast flag on or off + /// Sets the broadcast flag on or off. pub fn set_broadcast(&self, on: bool) -> io::Result<()> { self.0.set_broadcast(on) } - /// Sets the multicast loop flag to the specified value + /// Sets the multicast loop flag to the specified value. /// /// This lets multicast packets loop back to local sockets (if enabled) pub fn set_multicast_loop(&self, on: bool) -> io::Result<()> { self.0.set_multicast_loop(on) } - /// Joins a multicast IP address (becomes a member of it) + /// Joins a multicast IP address (becomes a member of it). pub fn join_multicast(&self, multi: &IpAddr) -> io::Result<()> { self.0.join_multicast(multi) } - /// Leaves a multicast IP address (drops membership from it) + /// Leaves a multicast IP address (drops membership from it). pub fn leave_multicast(&self, multi: &IpAddr) -> io::Result<()> { self.0.leave_multicast(multi) } - /// Sets the multicast TTL + /// Sets the multicast TTL. pub fn set_multicast_time_to_live(&self, ttl: i32) -> io::Result<()> { self.0.multicast_time_to_live(ttl) } - /// Sets this socket's TTL + /// Sets this socket's TTL. pub fn set_time_to_live(&self, ttl: i32) -> io::Result<()> { self.0.time_to_live(ttl) } diff --git a/src/libstd/os.rs b/src/libstd/os/android/mod.rs similarity index 63% rename from src/libstd/os.rs rename to src/libstd/os/android/mod.rs index ee0f04cb9911c..346a903c4d9a9 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os/android/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! OS-specific functionality +//! Android-specific definitions -#![stable(feature = "os", since = "1.0.0")] +#![unstable(feature = "raw_ext", reason = "recently added API")] -#[cfg(unix)] pub use sys::ext as unix; -#[cfg(windows)] pub use sys::ext as windows; +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/android/raw.rs b/src/libstd/os/android/raw.rs new file mode 100644 index 0000000000000..538ed7c4688c7 --- /dev/null +++ b/src/libstd/os/android/raw.rs @@ -0,0 +1,46 @@ +// 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. + +//! Android-specific raw type definitions + +use os::raw::{c_uint, c_uchar, c_ulonglong, c_longlong, c_ulong}; +use os::unix::raw::{uid_t, gid_t}; + +pub type blkcnt_t = u32; +pub type blksize_t = u32; +pub type dev_t = u32; +pub type ino_t = u32; +pub type mode_t = u16; +pub type nlink_t = u16; +pub type off_t = i32; +pub type time_t = i32; + +#[repr(C)] +pub struct stat { + pub st_dev: c_ulonglong, + pub __pad0: [c_uchar; 4], + pub __st_ino: ino_t, + pub st_mode: c_uint, + pub st_nlink: c_uint, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: c_ulonglong, + pub __pad3: [c_uchar; 4], + pub st_size: c_longlong, + pub st_blksize: blksize_t, + pub st_blocks: c_ulonglong, + pub st_atime: time_t, + pub st_atime_nsec: c_ulong, + pub st_mtime: time_t, + pub st_mtime_nsec: c_ulong, + pub st_ctime: time_t, + pub st_ctime_nsec: c_ulong, + pub st_ino: c_ulonglong, +} diff --git a/src/libstd/os/bitrig/mod.rs b/src/libstd/os/bitrig/mod.rs new file mode 100644 index 0000000000000..01ea542b3b713 --- /dev/null +++ b/src/libstd/os/bitrig/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! Bitrig-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/bitrig/raw.rs b/src/libstd/os/bitrig/raw.rs new file mode 100644 index 0000000000000..aebc21aa71856 --- /dev/null +++ b/src/libstd/os/bitrig/raw.rs @@ -0,0 +1,48 @@ +// 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. + +//! Bitrig-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = u32; +pub type dev_t = i32; +pub type fflags_t = u32; // type not declared, but struct stat have u_int32_t +pub type ino_t = u64; +pub type mode_t = u32; +pub type nlink_t = u32; +pub type off_t = i64; +pub type time_t = i64; + +#[repr(C)] +pub struct stat { + pub st_mode: mode_t, + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: fflags_t, + pub st_gen: u32, + pub st_birthtime: time_t, + pub st_birthtime_nsec: c_long, +} diff --git a/src/libstd/os/dragonfly/mod.rs b/src/libstd/os/dragonfly/mod.rs new file mode 100644 index 0000000000000..677f8b706cdbd --- /dev/null +++ b/src/libstd/os/dragonfly/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! Dragonfly-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/dragonfly/raw.rs b/src/libstd/os/dragonfly/raw.rs new file mode 100644 index 0000000000000..22c811ead4335 --- /dev/null +++ b/src/libstd/os/dragonfly/raw.rs @@ -0,0 +1,50 @@ +// 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. + +//! Dragonfly-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{pid_t, uid_t, gid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = u32; +pub type dev_t = u32; +pub type fflags_t = u32; +pub type ino_t = u64; +pub type mode_t = u16; +pub type nlink_t = u16; +pub type off_t = i64; +pub type time_t = i64; + +#[repr(C)] +pub struct stat { + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_dev: dev_t, + pub st_mode: mode_t, + pub st_padding1: u16, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: fflags_t, + pub st_gen: uint32_t, + pub st_lspare: int32_t, + pub st_qspare1: int64_t, + pub st_qspare2: int64_t, +} diff --git a/src/libstd/os/freebsd/mod.rs b/src/libstd/os/freebsd/mod.rs new file mode 100644 index 0000000000000..73b6fd211371c --- /dev/null +++ b/src/libstd/os/freebsd/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! FreeBSD-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/freebsd/raw.rs b/src/libstd/os/freebsd/raw.rs new file mode 100644 index 0000000000000..a810eff45d32e --- /dev/null +++ b/src/libstd/os/freebsd/raw.rs @@ -0,0 +1,50 @@ +// 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. + +//! FreeBSD-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t, pid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = i64; +pub type dev_t = u32; +pub type fflags_t = u32; +pub type ino_t = u32; +pub type mode_t = u16; +pub type nlink_t = u16; +pub type off_t = i64; +pub type time_t = i64; + +#[repr(C)] +pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: fflags_t, + pub st_gen: u32, + pub st_lspare: i32, + pub st_birthtime: time_t, + pub st_birthtime_nsec: c_long, + pub __unused: [u8; 2], +} diff --git a/src/libstd/os/ios/mod.rs b/src/libstd/os/ios/mod.rs new file mode 100644 index 0000000000000..d471cf12fe63e --- /dev/null +++ b/src/libstd/os/ios/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! iOS-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/ios/raw.rs b/src/libstd/os/ios/raw.rs new file mode 100644 index 0000000000000..3266b3846d899 --- /dev/null +++ b/src/libstd/os/ios/raw.rs @@ -0,0 +1,49 @@ +// 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. + +//! iOS-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t, pid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = i32; +pub type dev_t = i32; +pub type ino_t = u64; +pub type mode_t = u16; +pub type nlink_t = u16; +pub type off_t = i64; +pub type time_t = c_long; + +#[repr(C)] +pub struct stat { + pub st_dev: dev_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_ino: ino_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_birthtime: time_t, + pub st_birthtime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: u32, + pub st_gen: u32, + pub st_lspare: i32, + pub st_qspare: [i64; 2], +} diff --git a/src/libstd/os/linux/mod.rs b/src/libstd/os/linux/mod.rs new file mode 100644 index 0000000000000..43376a1baeb92 --- /dev/null +++ b/src/libstd/os/linux/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! Linux-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs new file mode 100644 index 0000000000000..adce5f22ebc10 --- /dev/null +++ b/src/libstd/os/linux/raw.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. + +//! Linux-specific raw type definitions + +pub type dev_t = u64; +pub type mode_t = u32; + +#[doc(inline)] +pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; + +#[cfg(any(target_arch = "x86", + target_arch = "le32", + target_arch = "powerpc", + target_arch = "arm"))] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_short}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i32; + pub type blksize_t = i32; + pub type ino_t = u32; + pub type nlink_t = u32; + pub type off_t = i32; + pub type time_t = i32; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub __pad1: c_short, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub __pad2: c_short, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused4: c_long, + pub __unused5: c_long, + } +} + +#[cfg(any(target_arch = "mips", + target_arch = "mipsel"))] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::c_long; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i32; + pub type blksize_t = i32; + pub type ino_t = u32; + pub type nlink_t = u32; + pub type off_t = i32; + pub type time_t = i32; + + #[repr(C)] + pub struct stat { + pub st_dev: c_ulong, + pub st_pad1: [c_long; 3], + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: c_ulong, + pub st_pad2: [c_long; 2], + pub st_size: off_t, + pub st_pad3: c_long, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_pad5: [c_long; 14], + } +} + +#[cfg(target_arch = "aarch64")] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_int}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i64; + pub type blksize_t = i32; + pub type ino_t = u64; + pub type nlink_t = u32; + pub type off_t = i64; + pub type time_t = i64; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub __pad1: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub __pad2: c_int, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused: [c_int; 2], + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_int}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i64; + pub type blksize_t = i64; + pub type ino_t = u64; + pub type nlink_t = u64; + pub type off_t = i64; + pub type time_t = i64; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_mode: mode_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub __pad0: c_int, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused: [c_long; 3], + } +} diff --git a/src/libstd/os/macos/mod.rs b/src/libstd/os/macos/mod.rs new file mode 100644 index 0000000000000..bc5ff5b25d2fa --- /dev/null +++ b/src/libstd/os/macos/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! MacOS-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/macos/raw.rs b/src/libstd/os/macos/raw.rs new file mode 100644 index 0000000000000..03fcb768c119a --- /dev/null +++ b/src/libstd/os/macos/raw.rs @@ -0,0 +1,49 @@ +// 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. + +//! MacOS-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = i32; +pub type dev_t = i32; +pub type ino_t = u64; +pub type mode_t = u16; +pub type nlink_t = u16; +pub type off_t = i64; +pub type time_t = c_long; + +#[repr(C)] +pub struct stat { + pub st_dev: dev_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_ino: ino_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_birthtime: time_t, + pub st_birthtime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: u32, + pub st_gen: u32, + pub st_lspare: i32, + pub st_qspare: [i64; 2], +} diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs new file mode 100644 index 0000000000000..cc4b1c944e786 --- /dev/null +++ b/src/libstd/os/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! OS-specific functionality + +#![stable(feature = "os", since = "1.0.0")] +#![allow(missing_docs, bad_style)] + +#[cfg(unix)] pub use sys::ext as unix; +#[cfg(windows)] pub use sys::ext as windows; + +#[cfg(target_os = "android")] pub mod android; +#[cfg(target_os = "bitrig")] pub mod bitrig; +#[cfg(target_os = "dragonfly")] pub mod dragonfly; +#[cfg(target_os = "freebsd")] pub mod freebsd; +#[cfg(target_os = "ios")] pub mod ios; +#[cfg(target_os = "linux")] pub mod linux; +#[cfg(target_os = "macos")] pub mod macos; +#[cfg(target_os = "nacl")] pub mod nacl; +#[cfg(target_os = "openbsd")] pub mod openbsd; + +pub mod raw; diff --git a/src/libstd/os/nacl/mod.rs b/src/libstd/os/nacl/mod.rs new file mode 100644 index 0000000000000..6baed03951467 --- /dev/null +++ b/src/libstd/os/nacl/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! Nacl-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs new file mode 100644 index 0000000000000..9defa8301ea34 --- /dev/null +++ b/src/libstd/os/nacl/raw.rs @@ -0,0 +1,169 @@ +// 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. + +//! Nacl-specific raw type definitions + +pub type dev_t = u64; +pub type mode_t = u32; + +pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; + +#[cfg(any(target_arch = "x86", + target_arch = "le32", + target_arch = "powerpc", + target_arch = "arm"))] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_short}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i32; + pub type blksize_t = i32; + pub type ino_t = u32; + pub type nlink_t = u32; + pub type off_t = i32; + pub type time_t = i32; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub __pad1: c_short, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub __pad2: c_short, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused4: c_long, + pub __unused5: c_long, + } +} + +#[cfg(any(target_arch = "mips", + target_arch = "mipsel"))] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::c_long; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i32; + pub type blksize_t = i32; + pub type ino_t = u32; + pub type nlink_t = u32; + pub type off_t = i32; + pub type time_t = i32; + + #[repr(C)] + pub struct stat { + pub st_dev: c_ulong, + pub st_pad1: [c_long; 3], + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: c_ulong, + pub st_pad2: [c_long; 2], + pub st_size: off_t, + pub st_pad3: c_long, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_pad5: [c_long; 14], + } +} + +#[cfg(target_arch = "aarch64")] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_int}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i64; + pub type blksize_t = i32; + pub type ino_t = u64; + pub type nlink_t = u32; + pub type off_t = i64; + pub type time_t = i64; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub __pad1: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub __pad2: c_int, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused: [c_int; 2], + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use super::{dev_t, mode_t}; + use os::raw::{c_long, c_int}; + use os::unix::raw::{gid_t, uid_t}; + + pub type blkcnt_t = i64; + pub type blksize_t = i64; + pub type ino_t = u64; + pub type nlink_t = u64; + pub type off_t = i64; + pub type time_t = i64; + + #[repr(C)] + pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_mode: mode_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub __pad0: c_int, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub __unused: [c_long; 3], + } +} diff --git a/src/libstd/os/openbsd/mod.rs b/src/libstd/os/openbsd/mod.rs new file mode 100644 index 0000000000000..1b1a10055902b --- /dev/null +++ b/src/libstd/os/openbsd/mod.rs @@ -0,0 +1,19 @@ +// 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. + +//! OpenBSD-specific definitions + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub mod raw; + +pub mod fs { + pub use sys::fs2::MetadataExt; +} diff --git a/src/libstd/os/openbsd/raw.rs b/src/libstd/os/openbsd/raw.rs new file mode 100644 index 0000000000000..632a8c336b78d --- /dev/null +++ b/src/libstd/os/openbsd/raw.rs @@ -0,0 +1,48 @@ +// 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. + +//! OpenBSD-specific raw type definitions + +use os::raw::c_long; +use os::unix::raw::{uid_t, gid_t, pid_t}; + +pub type blkcnt_t = i64; +pub type blksize_t = u32; +pub type dev_t = i32; +pub type fflags_t = u32; // type not declared, but struct stat have u_int32_t +pub type ino_t = u64; +pub type mode_t = u32; +pub type nlink_t = u32; +pub type off_t = i64; +pub type time_t = i64; + +#[repr(C)] +pub struct stat { + pub st_mode: mode_t, + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_atime: time_t, + pub st_atime_nsec: c_long, + pub st_mtime: time_t, + pub st_mtime_nsec: c_long, + pub st_ctime: time_t, + pub st_ctime_nsec: c_long, + pub st_size: off_t, + pub st_blocks: blkcnt_t, + pub st_blksize: blksize_t, + pub st_flags: fflags_t, + pub st_gen: u32, + pub st_birthtime: time_t, + pub st_birthtime_nsec: c_long, +} diff --git a/src/libstd/os/raw.rs b/src/libstd/os/raw.rs new file mode 100644 index 0000000000000..44f4a1c828b54 --- /dev/null +++ b/src/libstd/os/raw.rs @@ -0,0 +1,92 @@ +// 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. + +//! Raw OS-specific types for the current platform/architecture + +#![unstable(feature = "raw_os", reason = "recently added API")] + +#[cfg(target_arch = "aarch64")] pub type c_char = u8; +#[cfg(not(target_arch = "aarch64"))] pub type c_char = i8; +pub type c_schar = i8; +pub type c_uchar = u8; +pub type c_short = i16; +pub type c_ushort = u16; +pub type c_int = i32; +pub type c_uint = u32; +#[cfg(any(target_pointer_width = "32", windows))] pub type c_long = i32; +#[cfg(any(target_pointer_width = "32", windows))] pub type c_ulong = u32; +#[cfg(all(target_pointer_width = "64", not(windows)))] pub type c_long = i64; +#[cfg(all(target_pointer_width = "64", not(windows)))] pub type c_ulong = u64; +pub type c_longlong = i64; +pub type c_ulonglong = u64; +pub type c_float = f32; +pub type c_double = f64; + +/// Type used to construct void pointers for use with C. +/// +/// This type is only useful as a pointer target. Do not use it as a +/// return type for FFI functions which have the `void` return type in +/// C. Use the unit type `()` or omit the return type instead. +// NB: For LLVM to recognize the void pointer type and by extension +// functions like malloc(), we need to have it represented as i8* in +// LLVM bitcode. The enum used here ensures this and prevents misuse +// of the "raw" type by only having private variants.. We need two +// variants, because the compiler complains about the repr attribute +// otherwise. +#[repr(u8)] +pub enum c_void { + #[doc(hidden)] __variant1, + #[doc(hidden)] __variant2, +} + +#[cfg(test)] +mod tests { + use any::TypeId; + use libc; + use mem; + + macro_rules! ok { + ($($t:ident)*) => {$( + assert!(TypeId::of::() == TypeId::of::(), + "{} is wrong", stringify!($t)); + )*} + } + + macro_rules! ok_size { + ($($t:ident)*) => {$( + assert!(mem::size_of::() == mem::size_of::(), + "{} is wrong", stringify!($t)); + )*} + } + + #[test] + fn same() { + use os::raw; + ok!(c_char c_schar c_uchar c_short c_ushort c_int c_uint c_long c_ulong + c_longlong c_ulonglong c_float c_double); + } + + #[cfg(unix)] + fn unix() { + { + use os::unix::raw; + ok!(uid_t gid_t dev_t ino_t mode_t nlink_t off_t blksize_t blkcnt_t); + } + { + use sys::platform::raw; + ok_size!(stat); + } + } + + #[cfg(windows)] + fn windows() { + use os::windows::raw; + } +} diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 2ce974c1271aa..2d7123692289d 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -59,7 +59,11 @@ impl Once { /// routine is currently running. /// /// When this function returns, it is guaranteed that some initialization - /// has run and completed (it may not be the closure specified). + /// has run and completed (it may not be the closure specified). It is also + /// guaranteed that any memory writes performed by the executed closure can + /// be reliably observed by other tasks at this point (there is a + /// happens-before relation between the closure and code executing after the + /// return). #[stable(feature = "rust1", since = "1.0.0")] pub fn call_once(&'static self, f: F) where F: FnOnce() { // Optimize common path: load is much cheaper than fetch_add. diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index aa4bf82120764..1e68eac5a6735 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -161,6 +161,8 @@ extern { pub fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char; pub fn setgroups(ngroups: libc::c_int, ptr: *const libc::c_void) -> libc::c_int; + pub fn realpath(pathname: *const libc::c_char, resolved: *mut libc::c_char) + -> *mut libc::c_char; } #[cfg(any(target_os = "macos", target_os = "ios"))] diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs deleted file mode 100644 index 66aaf26b09b72..0000000000000 --- a/src/libstd/sys/unix/ext.rs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Experimental extensions to `std` for Unix platforms. -//! -//! For now, this module is limited to extracting file descriptors, -//! but its functionality will grow over time. -//! -//! # Example -//! -//! ```no_run -//! use std::fs::File; -//! use std::os::unix::prelude::*; -//! -//! fn main() { -//! let f = File::create("foo.txt").unwrap(); -//! let fd = f.as_raw_fd(); -//! -//! // use fd with native unix bindings -//! } -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -/// Unix-specific extensions to general I/O primitives -#[stable(feature = "rust1", since = "1.0.0")] -pub mod io { - use fs; - use libc; - use net; - use sys_common::{net2, AsInner, FromInner}; - use sys; - - /// Raw file descriptors. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawFd = libc::c_int; - - /// A trait to extract the raw unix file descriptor from an underlying - /// object. - /// - /// This is only available on unix platforms and must be imported in order - /// to call the method. Windows platforms have a corresponding `AsRawHandle` - /// and `AsRawSocket` set of traits. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guarantee to be valid while - /// the original object has not yet been destroyed. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_fd(&self) -> RawFd; - } - - /// A trait to express the ability to construct an object from a raw file - /// descriptor. - #[unstable(feature = "from_raw_os", - reason = "recent addition to std::os::unix::io")] - pub trait FromRawFd { - /// Constructs a new instances of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for fs::File { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for fs::File { - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs2::File::from_inner(fd)) - } - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - let socket = sys::net::Socket::from_inner(fd); - net::TcpStream::from_inner(net2::TcpStream::from_inner(socket)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - let socket = sys::net::Socket::from_inner(fd); - net::TcpListener::from_inner(net2::TcpListener::from_inner(socket)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - let socket = sys::net::Socket::from_inner(fd); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(socket)) - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// OsString and OsStr -//////////////////////////////////////////////////////////////////////////////// - -/// Unix-specific extension to the primitives in the `std::ffi` module -#[stable(feature = "rust1", since = "1.0.0")] -pub mod ffi { - use ffi::{OsStr, OsString}; - use mem; - use prelude::v1::*; - use sys::os_str::Buf; - use sys_common::{FromInner, IntoInner, AsInner}; - - /// Unix-specific extensions to `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStringExt { - /// Creates an `OsString` from a byte vector. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_vec(vec: Vec) -> Self; - - /// Yields the underlying byte vector of this `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - fn into_vec(self) -> Vec; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStringExt for OsString { - fn from_vec(vec: Vec) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec { - self.into_inner().inner - } - } - - /// Unix-specific extensions to `OsStr`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStrExt { - #[stable(feature = "rust1", since = "1.0.0")] - fn from_bytes(slice: &[u8]) -> &Self; - - /// Gets the underlying byte view of the `OsStr` slice. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_bytes(&self) -> &[u8]; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } - } -} - -/// Unix-specific extensions to primitives in the `std::fs` module. -#[unstable(feature = "fs_ext", - reason = "may want a more useful mode abstraction")] -pub mod fs { - use sys; - use sys_common::{FromInner, AsInner, AsInnerMut}; - use fs::{Permissions, OpenOptions}; - use path::Path; - use convert::AsRef; - use io; - - /// Unix-specific extensions to `Permissions` - pub trait PermissionsExt { - fn mode(&self) -> i32; - fn set_mode(&mut self, mode: i32); - } - - impl PermissionsExt for Permissions { - fn mode(&self) -> i32 { self.as_inner().mode() } - - fn set_mode(&mut self, mode: i32) { - *self = FromInner::from_inner(FromInner::from_inner(mode)); - } - } - - /// Unix-specific extensions to `OpenOptions` - pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of a `File::open_opts` call then this - /// specified `mode` will be used as the permission bits for the new file. - fn mode(&mut self, mode: i32) -> &mut Self; - } - - impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: i32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); self - } - } - - /// Creates a new symbolic link on the filesystem. - /// - /// The `dst` path will be a symbolic link pointing to the `src` path. - /// - /// # Note - /// - /// On Windows, you must specify whether a symbolic link points to a file - /// or directory. Use `os::windows::fs::symlink_file` to create a - /// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a - /// symbolic link to a directory. Additionally, the process must have - /// `SeCreateSymbolicLinkPrivilege` in order to be able to create a - /// symbolic link. - /// - /// # Examples - /// - /// ``` - /// #![feature(fs_ext)] - /// use std::os::unix::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink("a.txt", "b.txt")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> - { - sys::fs2::symlink(src.as_ref(), dst.as_ref()) - } - -} - -//////////////////////////////////////////////////////////////////////////////// -// Process and Command -//////////////////////////////////////////////////////////////////////////////// - -/// Unix-specific extensions to primitives in the `std::process` module. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod process { - use prelude::v1::*; - use libc::{uid_t, gid_t}; - use process; - use sys; - use sys_common::{AsInnerMut, AsInner}; - - /// Unix-specific extensions to the `std::process::Command` builder - #[stable(feature = "rust1", since = "1.0.0")] - pub trait CommandExt { - /// Sets the child process's user id. This translates to a - /// `setuid` call in the child process. Failure in the `setuid` - /// call will cause the spawn to fail. - #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: uid_t) -> &mut process::Command; - - /// Similar to `uid`, but sets the group id of the child process. This has - /// the same semantics as the `uid` field. - #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: gid_t) -> &mut process::Command; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl CommandExt for process::Command { - fn uid(&mut self, id: uid_t) -> &mut process::Command { - self.as_inner_mut().uid = Some(id); - self - } - - fn gid(&mut self, id: gid_t) -> &mut process::Command { - self.as_inner_mut().gid = Some(id); - self - } - } - - /// Unix-specific extensions to `std::process::ExitStatus` - #[stable(feature = "rust1", since = "1.0.0")] - pub trait ExitStatusExt { - /// If the process was terminated by a signal, returns that signal. - #[stable(feature = "rust1", since = "1.0.0")] - fn signal(&self) -> Option; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl ExitStatusExt for process::ExitStatus { - fn signal(&self) -> Option { - match *self.as_inner() { - sys::process2::ExitStatus::Signal(s) => Some(s), - _ => None - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Prelude -//////////////////////////////////////////////////////////////////////////////// - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - pub use super::io::{RawFd, AsRawFd}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - pub use super::fs::{PermissionsExt, OpenOptionsExt}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::process::{CommandExt, ExitStatusExt}; -} diff --git a/src/libstd/sys/unix/ext/ffi.rs b/src/libstd/sys/unix/ext/ffi.rs new file mode 100644 index 0000000000000..825e74cabdebb --- /dev/null +++ b/src/libstd/sys/unix/ext/ffi.rs @@ -0,0 +1,62 @@ +// 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. + +//! Unix-specific extension to the primitives in the `std::ffi` module + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsStr, OsString}; +use mem; +use prelude::v1::*; +use sys::os_str::Buf; +use sys_common::{FromInner, IntoInner, AsInner}; + +/// Unix-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a byte vector. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec) -> Self; + + /// Yields the underlying byte vector of this `OsString`. + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec { + self.into_inner().inner + } +} + +/// Unix-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the `OsStr` slice. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs new file mode 100644 index 0000000000000..2e4ed38e50fe7 --- /dev/null +++ b/src/libstd/sys/unix/ext/fs.rs @@ -0,0 +1,207 @@ +// 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. + +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use prelude::v1::*; + +use fs::{self, Permissions, OpenOptions}; +use io; +use mem; +use os::raw::c_long; +use os::unix::raw; +use path::Path; +use sys::platform; +use sys; +use sys_common::{FromInner, AsInner, AsInnerMut}; + +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_READ: raw::mode_t = 0o400; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_WRITE: raw::mode_t = 0o200; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_EXECUTE: raw::mode_t = 0o100; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const USER_RWX: raw::mode_t = 0o700; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_READ: raw::mode_t = 0o040; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_WRITE: raw::mode_t = 0o020; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_EXECUTE: raw::mode_t = 0o010; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const GROUP_RWX: raw::mode_t = 0o070; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_READ: raw::mode_t = 0o004; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_WRITE: raw::mode_t = 0o002; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_EXECUTE: raw::mode_t = 0o001; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const OTHER_RWX: raw::mode_t = 0o007; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_READ: raw::mode_t = 0o444; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_WRITE: raw::mode_t = 0o222; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_EXECUTE: raw::mode_t = 0o111; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const ALL_RWX: raw::mode_t = 0o777; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const SETUID: raw::mode_t = 0o4000; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const SETGID: raw::mode_t = 0o2000; +#[unstable(feature = "fs_mode", reason = "recently added API")] +pub const STICKY_BIT: raw::mode_t = 0o1000; + +/// Unix-specific extensions to `Permissions` +#[unstable(feature = "fs_ext", + reason = "may want a more useful mode abstraction")] +pub trait PermissionsExt { + fn mode(&self) -> raw::mode_t; + fn set_mode(&mut self, mode: raw::mode_t); + fn from_mode(mode: raw::mode_t) -> Self; +} + +impl PermissionsExt for Permissions { + fn mode(&self) -> raw::mode_t { self.as_inner().mode() } + + fn set_mode(&mut self, mode: raw::mode_t) { + *self = FromInner::from_inner(FromInner::from_inner(mode)); + } + + fn from_mode(mode: raw::mode_t) -> Permissions { + FromInner::from_inner(FromInner::from_inner(mode)) + } +} + +/// Unix-specific extensions to `OpenOptions` +#[unstable(feature = "fs_ext", + reason = "may want a more useful mode abstraction")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + fn mode(&mut self, mode: raw::mode_t) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn mode(&mut self, mode: raw::mode_t) -> &mut OpenOptions { + self.as_inner_mut().mode(mode); self + } +} + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub struct Metadata(sys::fs2::FileAttr); + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + fn as_raw(&self) -> &Metadata; +} + +impl MetadataExt for fs::Metadata { + fn as_raw(&self) -> &Metadata { + let inner: &sys::fs2::FileAttr = self.as_inner(); + unsafe { mem::transmute(inner) } + } +} + +impl AsInner for Metadata { + fn as_inner(&self) -> &platform::raw::stat { self.0.as_inner() } +} + +// Hm, why are there casts here to the returned type, shouldn't the types always +// be the same? Right you are! Turns out, however, on android at least the types +// in the raw `stat` structure are not the same as the types being returned. Who +// knew! +// +// As a result to make sure this compiles for all platforms we do the manual +// casts and rely on manual lowering to `stat` if the raw type is desired. +#[unstable(feature = "metadata_ext", reason = "recently added API")] +impl Metadata { + pub fn dev(&self) -> raw::dev_t { self.0.raw().st_dev as raw::dev_t } + pub fn ino(&self) -> raw::ino_t { self.0.raw().st_ino as raw::ino_t } + pub fn mode(&self) -> raw::mode_t { self.0.raw().st_mode as raw::mode_t } + pub fn nlink(&self) -> raw::nlink_t { self.0.raw().st_nlink as raw::nlink_t } + pub fn uid(&self) -> raw::uid_t { self.0.raw().st_uid as raw::uid_t } + pub fn gid(&self) -> raw::gid_t { self.0.raw().st_gid as raw::gid_t } + pub fn rdev(&self) -> raw::dev_t { self.0.raw().st_rdev as raw::dev_t } + pub fn size(&self) -> raw::off_t { self.0.raw().st_size as raw::off_t } + pub fn atime(&self) -> raw::time_t { self.0.raw().st_atime } + pub fn atime_nsec(&self) -> c_long { self.0.raw().st_atime } + pub fn mtime(&self) -> raw::time_t { self.0.raw().st_mtime } + pub fn mtime_nsec(&self) -> c_long { self.0.raw().st_mtime } + pub fn ctime(&self) -> raw::time_t { self.0.raw().st_ctime } + pub fn ctime_nsec(&self) -> c_long { self.0.raw().st_ctime } + + pub fn blksize(&self) -> raw::blksize_t { + self.0.raw().st_blksize as raw::blksize_t + } + pub fn blocks(&self) -> raw::blkcnt_t { + self.0.raw().st_blocks as raw::blkcnt_t + } +} + +#[unstable(feature = "dir_entry_ext", reason = "recently added API")] +pub trait DirEntryExt { + fn ino(&self) -> raw::ino_t; +} + +impl DirEntryExt for fs::DirEntry { + fn ino(&self) -> raw::ino_t { self.as_inner().ino() } +} + +/// Creates a new symbolic link on the filesystem. +/// +/// The `dst` path will be a symbolic link pointing to the `src` path. +/// +/// # Note +/// +/// On Windows, you must specify whether a symbolic link points to a file +/// or directory. Use `os::windows::fs::symlink_file` to create a +/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a +/// symbolic link to a directory. Additionally, the process must have +/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a +/// symbolic link. +/// +/// # Examples +/// +/// ``` +/// use std::os::unix::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink("a.txt", "b.txt")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> +{ + sys::fs2::symlink(src.as_ref(), dst.as_ref()) +} + +#[unstable(feature = "dir_builder", reason = "recently added API")] +/// An extension trait for `fs::DirBuilder` for unix-specific options. +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + fn mode(&mut self, mode: raw::mode_t) -> &mut Self; +} + +impl DirBuilderExt for fs::DirBuilder { + fn mode(&mut self, mode: raw::mode_t) -> &mut fs::DirBuilder { + self.as_inner_mut().set_mode(mode); + self + } +} + diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs new file mode 100644 index 0000000000000..8cb4b4907f6dc --- /dev/null +++ b/src/libstd/sys/unix/ext/io.rs @@ -0,0 +1,108 @@ +// 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. + +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use net; +use os::raw; +use sys; +use sys_common::{net2, AsInner, FromInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = raw::c_int; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guarantee to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[unstable(feature = "from_raw_os", + reason = "recent addition to std::os::unix::io")] +pub trait FromRawFd { + /// Constructs a new instances of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs2::File::from_inner(fd)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let socket = sys::net::Socket::from_inner(fd); + net::TcpStream::from_inner(net2::TcpStream::from_inner(socket)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let socket = sys::net::Socket::from_inner(fd); + net::TcpListener::from_inner(net2::TcpListener::from_inner(socket)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let socket = sys::net::Socket::from_inner(fd); + net::UdpSocket::from_inner(net2::UdpSocket::from_inner(socket)) + } +} diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs new file mode 100644 index 0000000000000..6fde45a7301f9 --- /dev/null +++ b/src/libstd/sys/unix/ext/mod.rs @@ -0,0 +1,53 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create("foo.txt").unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod io; +pub mod ffi; +pub mod fs; +pub mod process; +pub mod raw; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + pub use super::io::{RawFd, AsRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] + pub use super::fs::{DirEntryExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::process::{CommandExt, ExitStatusExt}; +} diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs new file mode 100644 index 0000000000000..8c9d0a86583fb --- /dev/null +++ b/src/libstd/sys/unix/ext/process.rs @@ -0,0 +1,65 @@ +// 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. + +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use os::unix::raw::{uid_t, gid_t}; +use prelude::v1::*; +use process; +use sys; +use sys_common::{AsInnerMut, AsInner}; + +/// Unix-specific extensions to the `std::process::Command` builder +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user id. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: uid_t) -> &mut process::Command; + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: gid_t) -> &mut process::Command; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: uid_t) -> &mut process::Command { + self.as_inner_mut().uid = Some(id); + self + } + + fn gid(&mut self, id: gid_t) -> &mut process::Command { + self.as_inner_mut().gid = Some(id); + self + } +} + +/// Unix-specific extensions to `std::process::ExitStatus` +#[stable(feature = "rust1", since = "1.0.0")] +pub trait ExitStatusExt { + /// If the process was terminated by a signal, returns that signal. + #[stable(feature = "rust1", since = "1.0.0")] + fn signal(&self) -> Option; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn signal(&self) -> Option { + match *self.as_inner() { + sys::process2::ExitStatus::Signal(s) => Some(s), + _ => None + } + } +} diff --git a/src/libstd/sys/unix/ext/raw.rs b/src/libstd/sys/unix/ext/raw.rs new file mode 100644 index 0000000000000..8fe4b90456a4c --- /dev/null +++ b/src/libstd/sys/unix/ext/raw.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific primitives available on all unix platforms + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +pub type uid_t = u32; +pub type gid_t = u32; +pub type pid_t = i32; + +#[doc(inline)] +pub use sys::platform::raw::{dev_t, ino_t, mode_t, nlink_t, off_t, blksize_t}; +#[doc(inline)] +pub use sys::platform::raw::{blkcnt_t, time_t}; diff --git a/src/libstd/sys/unix/fs2.rs b/src/libstd/sys/unix/fs2.rs index 8eb84b26f22f2..350161c751cb8 100644 --- a/src/libstd/sys/unix/fs2.rs +++ b/src/libstd/sys/unix/fs2.rs @@ -21,14 +21,15 @@ use path::{Path, PathBuf}; use ptr; use sync::Arc; use sys::fd::FileDesc; +use sys::platform::raw; use sys::{c, cvt, cvt_r}; -use sys_common::FromInner; +use sys_common::{AsInner, FromInner}; use vec::Vec; pub struct File(FileDesc); pub struct FileAttr { - stat: libc::stat, + stat: raw::stat, } pub struct ReadDir { @@ -57,13 +58,12 @@ pub struct OpenOptions { #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { mode: mode_t } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FileType { mode: mode_t } + +pub struct DirBuilder { mode: mode_t } + impl FileAttr { - pub fn is_dir(&self) -> bool { - (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFDIR - } - pub fn is_file(&self) -> bool { - (self.stat.st_mode as mode_t) & libc::S_IFMT == libc::S_IFREG - } pub fn size(&self) -> u64 { self.stat.st_size as u64 } pub fn perm(&self) -> FilePermissions { FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } @@ -76,12 +76,35 @@ impl FileAttr { self.mktime(self.stat.st_mtime as u64, self.stat.st_mtime_nsec as u64) } + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } + + pub fn raw(&self) -> &raw::stat { &self.stat } + // times are in milliseconds (currently) fn mktime(&self, secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } } +impl AsInner for FileAttr { + fn as_inner(&self) -> &raw::stat { &self.stat } +} + +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + fn as_raw_stat(&self) -> &raw::stat; +} + +impl MetadataExt for ::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { &self.as_inner().stat } +} + +impl MetadataExt for ::os::unix::fs::Metadata { + fn as_raw_stat(&self) -> &raw::stat { self.as_inner() } +} + impl FilePermissions { pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } pub fn set_readonly(&mut self, readonly: bool) { @@ -91,11 +114,19 @@ impl FilePermissions { self.mode |= 0o222; } } - pub fn mode(&self) -> i32 { self.mode as i32 } + pub fn mode(&self) -> raw::mode_t { self.mode } } -impl FromInner for FilePermissions { - fn from_inner(mode: i32) -> FilePermissions { +impl FileType { + pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } + pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } + pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } + + fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } +} + +impl FromInner for FilePermissions { + fn from_inner(mode: raw::mode_t) -> FilePermissions { FilePermissions { mode: mode as mode_t } } } @@ -147,6 +178,33 @@ impl DirEntry { self.root.join(::from_bytes(self.name_bytes())) } + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result { + extern { + fn rust_dir_get_mode(ptr: *mut libc::dirent_t) -> c_int; + } + unsafe { + match rust_dir_get_mode(self.dirent()) { + -1 => lstat(&self.path()).map(|m| m.file_type()), + n => Ok(FileType { mode: n as mode_t }), + } + } + } + + pub fn ino(&self) -> raw::ino_t { + extern { + fn rust_dir_get_ino(ptr: *mut libc::dirent_t) -> raw::ino_t; + } + unsafe { rust_dir_get_ino(self.dirent()) } + } + fn name_bytes(&self) -> &[u8] { extern { fn rust_list_dir_val(ptr: *mut libc::dirent_t) -> *const c_char; @@ -191,7 +249,7 @@ impl OpenOptions { self.flag(libc::O_CREAT, create); } - pub fn mode(&mut self, mode: i32) { + pub fn mode(&mut self, mode: raw::mode_t) { self.mode = mode as mode_t; } @@ -228,8 +286,10 @@ impl File { pub fn into_fd(self) -> FileDesc { self.0 } pub fn file_attr(&self) -> io::Result { - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::fstat(self.0.raw(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::fstat(self.0.raw(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } @@ -284,6 +344,22 @@ impl File { pub fn fd(&self) -> &FileDesc { &self.0 } } +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = try!(cstr(p)); + try!(cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })); + Ok(()) + } + + pub fn set_mode(&mut self, mode: mode_t) { + self.mode = mode; + } +} + fn cstr(path: &Path) -> io::Result { path.as_os_str().to_cstring().ok_or( io::Error::new(io::ErrorKind::InvalidInput, "path contained a null")) @@ -343,12 +419,6 @@ impl fmt::Debug for File { } } -pub fn mkdir(p: &Path) -> io::Result<()> { - let p = try!(cstr(p)); - try!(cvt(unsafe { libc::mkdir(p.as_ptr(), 0o777) })); - Ok(()) -} - pub fn readdir(p: &Path) -> io::Result { let root = Arc::new(p.to_path_buf()); let p = try!(cstr(p)); @@ -420,15 +490,19 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { pub fn stat(p: &Path) -> io::Result { let p = try!(cstr(p)); - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::stat(p.as_ptr(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } pub fn lstat(p: &Path) -> io::Result { let p = try!(cstr(p)); - let mut stat: libc::stat = unsafe { mem::zeroed() }; - try!(cvt(unsafe { libc::lstat(p.as_ptr(), &mut stat) })); + let mut stat: raw::stat = unsafe { mem::zeroed() }; + try!(cvt(unsafe { + libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) + })); Ok(FileAttr { stat: stat }) } @@ -438,3 +512,17 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { try!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) })); Ok(()) } + +pub fn canonicalize(p: &Path) -> io::Result { + let path = try!(CString::new(p.as_os_str().as_bytes())); + let mut buf = vec![0u8; 16 * 1024]; + unsafe { + let r = c::realpath(path.as_ptr(), buf.as_mut_ptr() as *mut _); + if r.is_null() { + return Err(io::Error::last_os_error()) + } + } + let p = buf.iter().position(|i| *i == 0).unwrap(); + buf.truncate(p); + Ok(PathBuf::from(OsString::from_vec(buf))) +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index d99753a6a4c80..78b798d3bffff 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -18,6 +18,16 @@ use libc; use num::One; use ops::Neg; +#[cfg(target_os = "android")] pub use os::android as platform; +#[cfg(target_os = "bitrig")] pub use os::bitrig as platform; +#[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform; +#[cfg(target_os = "freebsd")] pub use os::freebsd as platform; +#[cfg(target_os = "ios")] pub use os::ios as platform; +#[cfg(target_os = "linux")] pub use os::linux as platform; +#[cfg(target_os = "macos")] pub use os::macos as platform; +#[cfg(target_os = "nacl")] pub use os::nacl as platform; +#[cfg(target_os = "openbsd")] pub use os::openbsd as platform; + pub mod backtrace; pub mod c; pub mod condvar; diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 385834a6226e0..d94dfdeeea494 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -283,7 +283,6 @@ mod arch { } } -#[repr(C)] struct Cleanup { handle: libc::HANDLE, SymCleanup: SymCleanupFn, diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs deleted file mode 100644 index dd747d202a04b..0000000000000 --- a/src/libstd/sys/windows/ext.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Experimental extensions to `std` for Windows. -//! -//! For now, this module is limited to extracting handles, file -//! descriptors, and sockets, but its functionality will grow over -//! time. - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod io { - use fs; - use libc; - use net; - use sys_common::{net2, AsInner, FromInner}; - use sys; - - /// Raw HANDLEs. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawHandle = libc::HANDLE; - - /// Raw SOCKETs. - #[stable(feature = "rust1", since = "1.0.0")] - pub type RawSocket = libc::SOCKET; - - /// Extract raw handles. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_handle(&self) -> RawHandle; - } - - /// Construct I/O objects from raw handles. - #[unstable(feature = "from_raw_os", - reason = "recent addition to the std::os::windows::io module")] - pub trait FromRawHandle { - /// Constructs a new I/O object from the specified raw handle. - /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_handle(handle: RawHandle) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawHandle for fs::File { - fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() - } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawHandle for fs::File { - unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { - fs::File::from_inner(sys::fs2::File::from_inner(handle)) - } - } - - /// Extract raw sockets. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait AsRawSocket { - /// Extracts the underlying raw socket from this object. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_socket(&self) -> RawSocket; - } - - /// Create I/O objects from raw sockets. - #[unstable(feature = "from_raw_os", reason = "recent addition to module")] - pub trait FromRawSocket { - /// Creates a new I/O object from the given raw socket. - /// - /// This function will **consume ownership** of the socket provided and - /// it will be closed when the returned object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_socket(sock: RawSocket) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::TcpStream { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::TcpListener { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - #[stable(feature = "rust1", since = "1.0.0")] - impl AsRawSocket for net::UdpSocket { - fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() - } - } - - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::TcpStream { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(sock); - net::TcpStream::from_inner(net2::TcpStream::from_inner(sock)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::TcpListener { - unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(sock); - net::TcpListener::from_inner(net2::TcpListener::from_inner(sock)) - } - } - #[unstable(feature = "from_raw_os", reason = "trait is unstable")] - impl FromRawSocket for net::UdpSocket { - unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(sock); - net::UdpSocket::from_inner(net2::UdpSocket::from_inner(sock)) - } - } -} - -/// Windows-specific extensions to the primitives in the `std::ffi` module. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod ffi { - use ffi::{OsString, OsStr}; - use sys::os_str::Buf; - use sys_common::wtf8::Wtf8Buf; - use sys_common::{FromInner, AsInner}; - - pub use sys_common::wtf8::EncodeWide; - - /// Windows-specific extensions to `OsString`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStringExt { - /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of - /// 16-bit code units. - /// - /// This is lossless: calling `.encode_wide()` on the resulting string - /// will always return the original code units. - #[stable(feature = "rust1", since = "1.0.0")] - fn from_wide(wide: &[u16]) -> Self; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStringExt for OsString { - fn from_wide(wide: &[u16]) -> OsString { - FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) - } - } - - /// Windows-specific extensions to `OsStr`. - #[stable(feature = "rust1", since = "1.0.0")] - pub trait OsStrExt { - /// Re-encodes an `OsStr` as a wide character sequence, - /// i.e. potentially ill-formed UTF-16. - /// - /// This is lossless. Note that the encoding does not include a final - /// null. - #[stable(feature = "rust1", since = "1.0.0")] - fn encode_wide(&self) -> EncodeWide; - } - - #[stable(feature = "rust1", since = "1.0.0")] - impl OsStrExt for OsStr { - fn encode_wide(&self) -> EncodeWide { - self.as_inner().inner.encode_wide() - } - } -} - -/// Windows-specific extensions for the primitives in `std::fs` -#[unstable(feature = "fs_ext", reason = "may require more thought/methods")] -pub mod fs { - use fs::OpenOptions; - use sys; - use sys_common::AsInnerMut; - use path::Path; - use convert::AsRef; - use io; - - /// Windows-specific extensions to `OpenOptions` - pub trait OpenOptionsExt { - /// Overrides the `dwDesiredAccess` argument to the call to `CreateFile` - /// with the specified value. - fn desired_access(&mut self, access: i32) -> &mut Self; - - /// Overrides the `dwCreationDisposition` argument to the call to - /// `CreateFile` with the specified value. - /// - /// This will override any values of the standard `create` flags, for - /// example. - fn creation_disposition(&mut self, val: i32) -> &mut Self; - - /// Overrides the `dwFlagsAndAttributes` argument to the call to - /// `CreateFile` with the specified value. - /// - /// This will override any values of the standard flags on the - /// `OpenOptions` structure. - fn flags_and_attributes(&mut self, val: i32) -> &mut Self; - - /// Overrides the `dwShareMode` argument to the call to `CreateFile` with - /// the specified value. - /// - /// This will override any values of the standard flags on the - /// `OpenOptions` structure. - fn share_mode(&mut self, val: i32) -> &mut Self; - } - - impl OpenOptionsExt for OpenOptions { - fn desired_access(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().desired_access(access); self - } - fn creation_disposition(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().creation_disposition(access); self - } - fn flags_and_attributes(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().flags_and_attributes(access); self - } - fn share_mode(&mut self, access: i32) -> &mut OpenOptions { - self.as_inner_mut().share_mode(access); self - } - } - - /// Creates a new file symbolic link on the filesystem. - /// - /// The `dst` path will be a file symbolic link pointing to the `src` - /// path. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(fs_ext)] - /// use std::os::windows::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink_file("a.txt", "b.txt")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink_file, Q: AsRef>(src: P, dst: Q) - -> io::Result<()> - { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false) - } - - /// Creates a new directory symlink on the filesystem. - /// - /// The `dst` path will be a directory symbolic link pointing to the `src` - /// path. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(fs_ext)] - /// use std::os::windows::fs; - /// - /// # fn foo() -> std::io::Result<()> { - /// try!(fs::symlink_file("a", "b")); - /// # Ok(()) - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symlink_dir, Q: AsRef> (src: P, dst: Q) - -> io::Result<()> - { - sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true) - } -} - -/// A prelude for conveniently writing platform-specific code. -/// -/// Includes all extension traits, and some important type definitions. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - pub use super::io::{RawSocket, RawHandle, AsRawSocket, AsRawHandle}; - #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - pub use super::fs::OpenOptionsExt; -} diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs new file mode 100644 index 0000000000000..3fa96f4dd13e9 --- /dev/null +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -0,0 +1,58 @@ +// 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. + +//! Windows-specific extensions to the primitives in the `std::ffi` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsString, OsStr}; +use sys::os_str::Buf; +use sys_common::wtf8::Wtf8Buf; +use sys_common::{FromInner, AsInner}; + +pub use sys_common::wtf8::EncodeWide; + +/// Windows-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a potentially ill-formed UTF-16 slice of + /// 16-bit code units. + /// + /// This is lossless: calling `.encode_wide()` on the resulting string + /// will always return the original code units. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_wide(wide: &[u16]) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_wide(wide: &[u16]) -> OsString { + FromInner::from_inner(Buf { inner: Wtf8Buf::from_wide(wide) }) + } +} + +/// Windows-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + /// Re-encodes an `OsStr` as a wide character sequence, + /// i.e. potentially ill-formed UTF-16. + /// + /// This is lossless. Note that the encoding does not include a final + /// null. + #[stable(feature = "rust1", since = "1.0.0")] + fn encode_wide(&self) -> EncodeWide; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn encode_wide(&self) -> EncodeWide { + self.as_inner().inner.encode_wide() + } +} diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs new file mode 100644 index 0000000000000..23c1fcf4b3c6e --- /dev/null +++ b/src/libstd/sys/windows/ext/fs.rs @@ -0,0 +1,150 @@ +// 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. + +//! Windows-specific extensions for the primitives in `std::fs` + +#![stable(feature = "rust1", since = "1.0.0")] + +use prelude::v1::*; + +use fs::{OpenOptions, Metadata}; +use io; +use path::Path; +use sys; +use sys_common::{AsInnerMut, AsInner}; + +/// Windows-specific extensions to `OpenOptions` +#[unstable(feature = "fs_ext", reason = "may require more thought/methods")] +pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to `CreateFile` + /// with the specified value. + fn desired_access(&mut self, access: i32) -> &mut Self; + + /// Overrides the `dwCreationDisposition` argument to the call to + /// `CreateFile` with the specified value. + /// + /// This will override any values of the standard `create` flags, for + /// example. + fn creation_disposition(&mut self, val: i32) -> &mut Self; + + /// Overrides the `dwFlagsAndAttributes` argument to the call to + /// `CreateFile` with the specified value. + /// + /// This will override any values of the standard flags on the + /// `OpenOptions` structure. + fn flags_and_attributes(&mut self, val: i32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to `CreateFile` with + /// the specified value. + /// + /// This will override any values of the standard flags on the + /// `OpenOptions` structure. + fn share_mode(&mut self, val: i32) -> &mut Self; +} + +impl OpenOptionsExt for OpenOptions { + fn desired_access(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().desired_access(access); self + } + fn creation_disposition(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().creation_disposition(access); self + } + fn flags_and_attributes(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().flags_and_attributes(access); self + } + fn share_mode(&mut self, access: i32) -> &mut OpenOptions { + self.as_inner_mut().share_mode(access); self + } +} + +/// Extension methods for `fs::Metadata` to access the raw fields contained +/// within. +#[unstable(feature = "metadata_ext", reason = "recently added API")] +pub trait MetadataExt { + /// Returns the value of the `dwFileAttributes` field of this metadata. + /// + /// This field contains the file system attribute information for a file + /// or directory. + fn file_attributes(&self) -> u32; + + /// Returns the value of the `ftCreationTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn creation_time(&self) -> u64; + + /// Returns the value of the `ftLastAccessTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn last_access_time(&self) -> u64; + + /// Returns the value of the `ftLastWriteTime` field of this metadata. + /// + /// The returned 64-bit value represents the number of 100-nanosecond + /// intervals since January 1, 1601 (UTC). + fn last_write_time(&self) -> u64; + + /// Returns the value of the `nFileSize{High,Low}` fields of this + /// metadata. + /// + /// The returned value does not have meaning for directories. + fn file_size(&self) -> u64; +} + +impl MetadataExt for Metadata { + fn file_attributes(&self) -> u32 { self.as_inner().attrs() } + fn creation_time(&self) -> u64 { self.as_inner().created() } + fn last_access_time(&self) -> u64 { self.as_inner().accessed() } + fn last_write_time(&self) -> u64 { self.as_inner().modified() } + fn file_size(&self) -> u64 { self.as_inner().size() } +} + +/// Creates a new file symbolic link on the filesystem. +/// +/// The `dst` path will be a file symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```ignore +/// use std::os::windows::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink_file("a.txt", "b.txt")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink_file, Q: AsRef>(src: P, dst: Q) + -> io::Result<()> { + sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), false) +} + +/// Creates a new directory symlink on the filesystem. +/// +/// The `dst` path will be a directory symbolic link pointing to the `src` +/// path. +/// +/// # Examples +/// +/// ```ignore +/// use std::os::windows::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(fs::symlink_file("a", "b")); +/// # Ok(()) +/// # } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub fn symlink_dir, Q: AsRef>(src: P, dst: Q) + -> io::Result<()> { + sys::fs2::symlink_inner(src.as_ref(), dst.as_ref(), true) +} diff --git a/src/libstd/sys/windows/ext/io.rs b/src/libstd/sys/windows/ext/io.rs new file mode 100644 index 0000000000000..b88a6316eee8b --- /dev/null +++ b/src/libstd/sys/windows/ext/io.rs @@ -0,0 +1,131 @@ +// 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. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use os::windows::raw; +use net; +use sys_common::{net2, AsInner, FromInner}; +use sys; + +/// Raw HANDLEs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawHandle = raw::HANDLE; + +/// Raw SOCKETs. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawSocket = raw::SOCKET; + +/// Extract raw handles. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawHandle { + /// Extracts the raw handle, without taking any ownership. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_handle(&self) -> RawHandle; +} + +/// Construct I/O objects from raw handles. +#[unstable(feature = "from_raw_os", + reason = "recent addition to the std::os::windows::io module")] +pub trait FromRawHandle { + /// Constructs a new I/O object from the specified raw handle. + /// + /// This function will **consume ownership** of the handle given, + /// passing responsibility for closing the handle to the returned + /// object. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_handle(handle: RawHandle) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawHandle for fs::File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle().raw() as RawHandle + } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawHandle for fs::File { + unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { + let handle = handle as ::libc::HANDLE; + fs::File::from_inner(sys::fs2::File::from_inner(handle)) + } +} + +/// Extract raw sockets. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawSocket { + /// Extracts the underlying raw socket from this object. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_socket(&self) -> RawSocket; +} + +/// Create I/O objects from raw sockets. +#[unstable(feature = "from_raw_os", reason = "recent addition to module")] +pub trait FromRawSocket { + /// Creates a new I/O object from the given raw socket. + /// + /// This function will **consume ownership** of the socket provided and + /// it will be closed when the returned object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + unsafe fn from_raw_socket(sock: RawSocket) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpStream { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::TcpListener { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawSocket for net::UdpSocket { + fn as_raw_socket(&self) -> RawSocket { + *self.as_inner().socket().as_inner() + } +} + +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::TcpStream { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { + let sock = sys::net::Socket::from_inner(sock); + net::TcpStream::from_inner(net2::TcpStream::from_inner(sock)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::TcpListener { + unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { + let sock = sys::net::Socket::from_inner(sock); + net::TcpListener::from_inner(net2::TcpListener::from_inner(sock)) + } +} +#[unstable(feature = "from_raw_os", reason = "trait is unstable")] +impl FromRawSocket for net::UdpSocket { + unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { + let sock = sys::net::Socket::from_inner(sock); + net::UdpSocket::from_inner(net2::UdpSocket::from_inner(sock)) + } +} diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs new file mode 100644 index 0000000000000..08dfa4cc87753 --- /dev/null +++ b/src/libstd/sys/windows/ext/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Windows. +//! +//! For now, this module is limited to extracting handles, file +//! descriptors, and sockets, but its functionality will grow over +//! time. + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod raw; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + pub use super::io::{RawSocket, RawHandle, AsRawSocket, AsRawHandle}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + pub use super::fs::{OpenOptionsExt, MetadataExt}; +} diff --git a/src/libstd/sys/windows/ext/raw.rs b/src/libstd/sys/windows/ext/raw.rs new file mode 100644 index 0000000000000..656e480ad0963 --- /dev/null +++ b/src/libstd/sys/windows/ext/raw.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Windows-specific primitives + +#![unstable(feature = "raw_ext", reason = "recently added API")] + +use os::raw; + +pub type HANDLE = *mut raw::c_void; +#[cfg(target_pointer_width = "32")] +pub type SOCKET = u32; +#[cfg(target_pointer_width = "64")] +pub type SOCKET = u64; diff --git a/src/libstd/sys/windows/fs2.rs b/src/libstd/sys/windows/fs2.rs index 5ac9a0ace5801..03a56e2958a6e 100644 --- a/src/libstd/sys/windows/fs2.rs +++ b/src/libstd/sys/windows/fs2.rs @@ -27,7 +27,16 @@ use sys_common::FromInner; use vec::Vec; pub struct File { handle: Handle } -pub struct FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA } + +pub struct FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA, + is_symlink: bool, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum FileType { + Dir, File, Symlink, ReparsePoint +} pub struct ReadDir { handle: FindNextFileHandle, @@ -56,11 +65,14 @@ pub struct OpenOptions { share_mode: Option, creation_disposition: Option, flags_and_attributes: Option, + security_attributes: usize, // *mut T doesn't have a Default impl } #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { attrs: libc::DWORD } +pub struct DirBuilder; + impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { @@ -111,8 +123,31 @@ impl DirEntry { } pub fn path(&self) -> PathBuf { + self.root.join(&self.file_name()) + } + + pub fn file_name(&self) -> OsString { let filename = super::truncate_utf16_at_nul(&self.data.cFileName); - self.root.join(&::from_wide(filename)) + OsString::from_wide(filename) + } + + pub fn file_type(&self) -> io::Result { + Ok(FileType::new(self.data.dwFileAttributes, + self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK)) + } + + pub fn metadata(&self) -> io::Result { + Ok(FileAttr { + data: c::WIN32_FILE_ATTRIBUTE_DATA { + dwFileAttributes: self.data.dwFileAttributes, + ftCreationTime: self.data.ftCreationTime, + ftLastAccessTime: self.data.ftLastAccessTime, + ftLastWriteTime: self.data.ftLastWriteTime, + nFileSizeHigh: self.data.nFileSizeHigh, + nFileSizeLow: self.data.nFileSizeLow, + }, + is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK, + }) } } @@ -135,6 +170,9 @@ impl OpenOptions { pub fn share_mode(&mut self, val: i32) { self.share_mode = Some(val as libc::DWORD); } + pub fn security_attributes(&mut self, attrs: libc::LPSECURITY_ATTRIBUTES) { + self.security_attributes = attrs as usize; + } fn get_desired_access(&self) -> libc::DWORD { self.desired_access.unwrap_or({ @@ -180,13 +218,20 @@ impl OpenOptions { } impl File { + fn open_reparse_point(path: &Path) -> io::Result { + let mut opts = OpenOptions::new(); + opts.read(true); + opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); + File::open(path, &opts) + } + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let path = to_utf16(path); let handle = unsafe { libc::CreateFileW(path.as_ptr(), opts.get_desired_access(), opts.get_share_mode(), - ptr::null_mut(), + opts.security_attributes as *mut _, opts.get_creation_disposition(), opts.get_flags_and_attributes(), ptr::null_mut()) @@ -224,7 +269,7 @@ impl File { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); try!(cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))); - Ok(FileAttr { + let mut attr = FileAttr { data: c::WIN32_FILE_ATTRIBUTE_DATA { dwFileAttributes: info.dwFileAttributes, ftCreationTime: info.ftCreationTime, @@ -232,8 +277,13 @@ impl File { ftLastWriteTime: info.ftLastWriteTime, nFileSizeHigh: info.nFileSizeHigh, nFileSizeLow: info.nFileSizeLow, - } - }) + }, + is_symlink: false, + }; + if attr.is_reparse_point() { + attr.is_symlink = self.is_symlink(); + } + Ok(attr) } } @@ -263,6 +313,43 @@ impl File { } pub fn handle(&self) -> &Handle { &self.handle } + + fn is_symlink(&self) -> bool { + self.readlink().is_ok() + } + + fn readlink(&self) -> io::Result { + let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + let mut bytes = 0; + + unsafe { + try!(cvt({ + c::DeviceIoControl(self.handle.raw(), + c::FSCTL_GET_REPARSE_POINT, + 0 as *mut _, + 0, + space.as_mut_ptr() as *mut _, + space.len() as libc::DWORD, + &mut bytes, + 0 as *mut _) + })); + let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; + if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { + return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) + } + let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = + &(*buf).rest as *const _ as *const _; + let path_buffer = &(*info).PathBuffer as *const _ as *const u16; + let subst_off = (*info).SubstituteNameOffset / 2; + let subst_ptr = path_buffer.offset(subst_off as isize); + let subst_len = (*info).SubstituteNameLength / 2; + let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); + + Ok(PathBuf::from(OsString::from_wide(subst))) + } + } + + pub fn into_handle(self) -> Handle { self.handle } } impl FromInner for File { @@ -285,27 +372,30 @@ pub fn to_utf16(s: &Path) -> Vec { } impl FileAttr { - pub fn is_dir(&self) -> bool { - self.data.dwFileAttributes & c::FILE_ATTRIBUTE_DIRECTORY != 0 - } - pub fn is_file(&self) -> bool { - !self.is_dir() - } pub fn size(&self) -> u64 { ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64) } + pub fn perm(&self) -> FilePermissions { FilePermissions { attrs: self.data.dwFileAttributes } } - pub fn accessed(&self) -> u64 { self.to_ms(&self.data.ftLastAccessTime) } - pub fn modified(&self) -> u64 { self.to_ms(&self.data.ftLastWriteTime) } + pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 } - fn to_ms(&self, ft: &libc::FILETIME) -> u64 { - // FILETIME is in 100ns intervals and there are 10000 intervals in a - // millisecond. - let bits = (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32); - bits / 10000 + pub fn file_type(&self) -> FileType { + FileType::new(self.data.dwFileAttributes, self.is_symlink) + } + + pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) } + pub fn accessed(&self) -> u64 { self.to_u64(&self.data.ftLastAccessTime) } + pub fn modified(&self) -> u64 { self.to_u64(&self.data.ftLastWriteTime) } + + fn to_u64(&self, ft: &libc::FILETIME) -> u64 { + (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) + } + + fn is_reparse_point(&self) -> bool { + self.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 } } @@ -323,12 +413,36 @@ impl FilePermissions { } } -pub fn mkdir(p: &Path) -> io::Result<()> { - let p = to_utf16(p); - try!(cvt(unsafe { - libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) - })); - Ok(()) +impl FileType { + fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType { + if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + if is_symlink { + FileType::Symlink + } else { + FileType::ReparsePoint + } + } else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 { + FileType::Dir + } else { + FileType::File + } + } + + pub fn is_dir(&self) -> bool { *self == FileType::Dir } + pub fn is_file(&self) -> bool { *self == FileType::File } + pub fn is_symlink(&self) -> bool { *self == FileType::Symlink } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { DirBuilder } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = to_utf16(p); + try!(cvt(unsafe { + libc::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) + })); + Ok(()) + } } pub fn readdir(p: &Path) -> io::Result { @@ -374,40 +488,8 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - opts.read(true); - opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT as i32); - let file = try!(File::open(p, &opts)); - - let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - let mut bytes = 0; - - unsafe { - try!(cvt({ - c::DeviceIoControl(file.handle.raw(), - c::FSCTL_GET_REPARSE_POINT, - 0 as *mut _, - 0, - space.as_mut_ptr() as *mut _, - space.len() as libc::DWORD, - &mut bytes, - 0 as *mut _) - })); - let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _; - if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK { - return Err(io::Error::new(io::ErrorKind::Other, "not a symlink")) - } - let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER = - &(*buf).rest as *const _ as *const _; - let path_buffer = &(*info).PathBuffer as *const _ as *const u16; - let subst_off = (*info).SubstituteNameOffset / 2; - let subst_ptr = path_buffer.offset(subst_off as isize); - let subst_len = (*info).SubstituteNameLength / 2; - let subst = slice::from_raw_parts(subst_ptr, subst_len as usize); - - Ok(PathBuf::from(OsString::from_wide(subst))) - } - + let file = try!(File::open_reparse_point(p)); + file.readlink() } pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { @@ -435,12 +517,28 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> { } pub fn stat(p: &Path) -> io::Result { - let p = to_utf16(p); + let attr = try!(lstat(p)); + if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + let opts = OpenOptions::new(); + let file = try!(File::open(p, &opts)); + file.file_attr() + } else { + Ok(attr) + } +} + +pub fn lstat(p: &Path) -> io::Result { + let utf16 = to_utf16(p); unsafe { let mut attr: FileAttr = mem::zeroed(); - try!(cvt(c::GetFileAttributesExW(p.as_ptr(), + try!(cvt(c::GetFileAttributesExW(utf16.as_ptr(), c::GetFileExInfoStandard, &mut attr.data as *mut _ as *mut _))); + if attr.is_reparse_point() { + attr.is_symlink = File::open_reparse_point(p).map(|f| { + f.is_symlink() + }).unwrap_or(false); + } Ok(attr) } } @@ -465,3 +563,17 @@ pub fn utimes(p: &Path, atime: u64, mtime: u64) -> io::Result<()> { })); Ok(()) } + +pub fn canonicalize(p: &Path) -> io::Result { + use sys::c::compat::kernel32::GetFinalPathNameByHandleW; + + let mut opts = OpenOptions::new(); + opts.read(true); + let f = try!(File::open(p, &opts)); + super::fill_utf16_buf(|buf, sz| unsafe { + GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, + libc::VOLUME_NAME_DOS) + }, |buf| { + PathBuf::from(OsString::from_wide(buf)) + }) +} diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index c3a30aae9e0e8..9481e180ce578 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -12,6 +12,7 @@ use prelude::v1::*; use io::ErrorKind; use io; +use libc::funcs::extra::kernel32::{GetCurrentProcess, DuplicateHandle}; use libc::{self, HANDLE}; use mem; use ptr; @@ -65,6 +66,18 @@ impl Handle { })); Ok(amt as usize) } + + pub fn duplicate(&self, access: libc::DWORD, inherit: bool, + options: libc::DWORD) -> io::Result { + let mut ret = 0 as libc::HANDLE; + try!(cvt(unsafe { + let cur_proc = GetCurrentProcess(); + DuplicateHandle(cur_proc, self.0, cur_proc, &mut ret, + access, inherit as libc::BOOL, + options) + })); + Ok(Handle::new(ret)) + } } impl Drop for Handle { diff --git a/src/libstd/sys/windows/process2.rs b/src/libstd/sys/windows/process2.rs index 2e5585d2f4389..5aad5f668dd41 100644 --- a/src/libstd/sys/windows/process2.rs +++ b/src/libstd/sys/windows/process2.rs @@ -13,17 +13,23 @@ use prelude::v1::*; use ascii::*; use collections::HashMap; use collections; +use env::split_paths; use env; use ffi::{OsString, OsStr}; use fmt; use fs; use io::{self, Error}; use libc::{self, c_void}; +use mem; use os::windows::ffi::OsStrExt; +use path::Path; use ptr; use sync::{StaticMutex, MUTEX_INIT}; +use sys::c; +use sys::fs2::{OpenOptions, File}; use sys::handle::Handle; use sys::pipe2::AnonPipe; +use sys::stdio; use sys::{self, cvt}; use sys_common::{AsInner, FromInner}; @@ -90,18 +96,12 @@ impl Command { // Processes //////////////////////////////////////////////////////////////////////////////// -// `CreateProcess` is racy! -// http://support.microsoft.com/kb/315939 -static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; - /// A value representing a child process. /// /// The lifetime of this value is linked to the lifetime of the actual /// process - the Process destructor calls self.finish() which waits /// for the process to terminate. pub struct Process { - /// A HANDLE to the process, which will prevent the pid being - /// re-used until the handle is closed. handle: Handle, } @@ -112,32 +112,17 @@ pub enum Stdio { } impl Process { - #[allow(deprecated)] pub fn spawn(cfg: &Command, - in_fd: Stdio, - out_fd: Stdio, - err_fd: Stdio) -> io::Result + in_handle: Stdio, + out_handle: Stdio, + err_handle: Stdio) -> io::Result { - use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; - use libc::consts::os::extra::{ - TRUE, FALSE, - STARTF_USESTDHANDLES, - INVALID_HANDLE_VALUE, - DUPLICATE_SAME_ACCESS - }; - use libc::funcs::extra::kernel32::{ - GetCurrentProcess, - DuplicateHandle, - CloseHandle, - CreateProcessW - }; - - use env::split_paths; - use mem; - use iter::Iterator; - - // To have the spawning semantics of unix/windows stay the same, we need to - // read the *child's* PATH if one is provided. See #15149 for more details. + use libc::{TRUE, STARTF_USESTDHANDLES}; + use libc::{DWORD, STARTUPINFO, CreateProcessW}; + + // To have the spawning semantics of unix/windows stay the same, we need + // to read the *child's* PATH if one is provided. See #15149 for more + // details. let program = cfg.env.as_ref().and_then(|env| { for (key, v) in env { if OsStr::new("PATH") != &**key { continue } @@ -156,118 +141,51 @@ impl Process { None }); - unsafe { - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::() as DWORD; - si.dwFlags = STARTF_USESTDHANDLES; - - let cur_proc = GetCurrentProcess(); - - let set_fd = |fd: &Stdio, slot: &mut HANDLE, - is_stdin: bool| { - match *fd { - Stdio::Inherit => {} - - // Similarly to unix, we don't actually leave holes for the - // stdio file descriptors, but rather open up /dev/null - // equivalents. These equivalents are drawn from libuv's - // windows process spawning. - Stdio::None => { - let access = if is_stdin { - libc::FILE_GENERIC_READ - } else { - libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES - }; - let size = mem::size_of::(); - let mut sa = libc::SECURITY_ATTRIBUTES { - nLength: size as libc::DWORD, - lpSecurityDescriptor: ptr::null_mut(), - bInheritHandle: 1, - }; - let mut filename: Vec = "NUL".utf16_units().collect(); - filename.push(0); - *slot = libc::CreateFileW(filename.as_ptr(), - access, - libc::FILE_SHARE_READ | - libc::FILE_SHARE_WRITE, - &mut sa, - libc::OPEN_EXISTING, - 0, - ptr::null_mut()); - if *slot == INVALID_HANDLE_VALUE { - return Err(Error::last_os_error()) - } - } - Stdio::Piped(ref pipe) => { - let orig = pipe.handle().raw(); - if DuplicateHandle(cur_proc, orig, cur_proc, slot, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - return Err(Error::last_os_error()) - } - } - } - Ok(()) - }; - - try!(set_fd(&in_fd, &mut si.hStdInput, true)); - try!(set_fd(&out_fd, &mut si.hStdOutput, false)); - try!(set_fd(&err_fd, &mut si.hStdError, false)); + let mut si = zeroed_startupinfo(); + si.cb = mem::size_of::() as DWORD; + si.dwFlags = STARTF_USESTDHANDLES; - let mut cmd_str = make_command_line(program.as_ref().unwrap_or(&cfg.program), - &cfg.args); - cmd_str.push(0); // add null terminator + let stdin = try!(in_handle.to_handle(c::STD_INPUT_HANDLE)); + let stdout = try!(out_handle.to_handle(c::STD_OUTPUT_HANDLE)); + let stderr = try!(err_handle.to_handle(c::STD_ERROR_HANDLE)); - let mut pi = zeroed_process_information(); - let mut create_err = None; - - // stolen from the libuv code. - let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; - if cfg.detach { - flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; - } + si.hStdInput = stdin.raw(); + si.hStdOutput = stdout.raw(); + si.hStdError = stderr.raw(); - with_envp(cfg.env.as_ref(), |envp| { - with_dirp(cfg.cwd.as_ref(), |dirp| { - let _lock = CREATE_PROCESS_LOCK.lock().unwrap(); - let created = CreateProcessW(ptr::null(), - cmd_str.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - TRUE, - flags, envp, dirp, - &mut si, &mut pi); - if created == FALSE { - create_err = Some(Error::last_os_error()); - } - }) - }); + let program = program.as_ref().unwrap_or(&cfg.program); + let mut cmd_str = make_command_line(program, &cfg.args); + cmd_str.push(0); // add null terminator - if !in_fd.inherited() { - assert!(CloseHandle(si.hStdInput) != 0); - } - if !out_fd.inherited() { - assert!(CloseHandle(si.hStdOutput) != 0); - } - if !err_fd.inherited() { - assert!(CloseHandle(si.hStdError) != 0); - } + // stolen from the libuv code. + let mut flags = libc::CREATE_UNICODE_ENVIRONMENT; + if cfg.detach { + flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP; + } - match create_err { - Some(err) => return Err(err), - None => {} - } + let (envp, _data) = make_envp(cfg.env.as_ref()); + let (dirp, _data) = make_dirp(cfg.cwd.as_ref()); + let mut pi = zeroed_process_information(); + try!(unsafe { + // `CreateProcess` is racy! + // http://support.microsoft.com/kb/315939 + static CREATE_PROCESS_LOCK: StaticMutex = MUTEX_INIT; + let _lock = CREATE_PROCESS_LOCK.lock(); + + cvt(CreateProcessW(ptr::null(), + cmd_str.as_mut_ptr(), + ptr::null_mut(), + ptr::null_mut(), + TRUE, flags, envp, dirp, + &mut si, &mut pi)) + }); - // We close the thread handle because we don't care about keeping the - // thread id valid, and we aren't keeping the thread handle around to be - // able to close it later. We don't close the process handle however - // because std::we want the process id to stay valid at least until the - // calling code closes the process handle. - assert!(CloseHandle(pi.hThread) != 0); + // We close the thread handle because we don't care about keeping + // the thread id valid, and we aren't keeping the thread handle + // around to be able to close it later. + drop(Handle::new(pi.hThread)); - Ok(Process { - handle: Handle::new(pi.hProcess) - }) - } + Ok(Process { handle: Handle::new(pi.hProcess) }) } pub unsafe fn kill(&self) -> io::Result<()> { @@ -276,45 +194,25 @@ impl Process { } pub fn wait(&self) -> io::Result { - use libc::consts::os::extra::{ - FALSE, - STILL_ACTIVE, - INFINITE, - WAIT_OBJECT_0, - }; - use libc::funcs::extra::kernel32::{ - GetExitCodeProcess, - WaitForSingleObject, - }; + use libc::{STILL_ACTIVE, INFINITE, WAIT_OBJECT_0}; + use libc::{GetExitCodeProcess, WaitForSingleObject}; unsafe { loop { let mut status = 0; - if GetExitCodeProcess(self.handle.raw(), &mut status) == FALSE { - let err = Err(Error::last_os_error()); - return err; - } + try!(cvt(GetExitCodeProcess(self.handle.raw(), &mut status))); if status != STILL_ACTIVE { return Ok(ExitStatus(status as i32)); } match WaitForSingleObject(self.handle.raw(), INFINITE) { WAIT_OBJECT_0 => {} - _ => { - let err = Err(Error::last_os_error()); - return err - } + _ => return Err(Error::last_os_error()), } } } } } -impl Stdio { - fn inherited(&self) -> bool { - match *self { Stdio::Inherit => true, _ => false } - } -} - #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitStatus(i32); @@ -415,9 +313,8 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> Vec { } } -fn with_envp(env: Option<&collections::HashMap>, cb: F) -> T - where F: FnOnce(*mut c_void) -> T, -{ +fn make_envp(env: Option<&collections::HashMap>) + -> (*mut c_void, Vec) { // On Windows we pass an "environment block" which is not a char**, but // rather a concatenation of null-terminated k=v\0 sequences, with a final // \0 to terminate. @@ -432,22 +329,57 @@ fn with_envp(env: Option<&collections::HashMap>, cb: F blk.push(0); } blk.push(0); - cb(blk.as_mut_ptr() as *mut c_void) + (blk.as_mut_ptr() as *mut c_void, blk) } - _ => cb(ptr::null_mut()) + _ => (ptr::null_mut(), Vec::new()) } } -fn with_dirp(d: Option<&OsString>, cb: F) -> T where - F: FnOnce(*const u16) -> T, -{ +fn make_dirp(d: Option<&OsString>) -> (*const u16, Vec) { match d { - Some(dir) => { - let mut dir_str: Vec = dir.encode_wide().collect(); - dir_str.push(0); - cb(dir_str.as_ptr()) - }, - None => cb(ptr::null()) + Some(dir) => { + let mut dir_str: Vec = dir.encode_wide().collect(); + dir_str.push(0); + (dir_str.as_ptr(), dir_str) + }, + None => (ptr::null(), Vec::new()) + } +} + +impl Stdio { + fn to_handle(&self, stdio_id: libc::DWORD) -> io::Result { + use libc::DUPLICATE_SAME_ACCESS; + + match *self { + Stdio::Inherit => { + stdio::get(stdio_id).and_then(|io| { + io.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + }) + } + Stdio::Piped(ref pipe) => { + pipe.handle().duplicate(0, true, DUPLICATE_SAME_ACCESS) + } + + // Similarly to unix, we don't actually leave holes for the + // stdio file descriptors, but rather open up /dev/null + // equivalents. These equivalents are drawn from libuv's + // windows process spawning. + Stdio::None => { + let size = mem::size_of::(); + let mut sa = libc::SECURITY_ATTRIBUTES { + nLength: size as libc::DWORD, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: 1, + }; + let mut opts = OpenOptions::new(); + opts.read(stdio_id == c::STD_INPUT_HANDLE); + opts.write(stdio_id != c::STD_INPUT_HANDLE); + opts.security_attributes(&mut sa); + File::open(Path::new("NUL"), &opts).map(|file| { + file.into_handle() + }) + } + } } } diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 91f6f328ff6e0..03547165f5d87 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -21,9 +21,9 @@ use sys::c; use sys::cvt; use sys::handle::Handle; -struct NoClose(Option); +pub struct NoClose(Option); -enum Output { +pub enum Output { Console(NoClose), Pipe(NoClose), } @@ -35,7 +35,7 @@ pub struct Stdin { pub struct Stdout(Output); pub struct Stderr(Output); -fn get(handle: libc::DWORD) -> io::Result { +pub fn get(handle: libc::DWORD) -> io::Result { let handle = unsafe { c::GetStdHandle(handle) }; if handle == libc::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) @@ -159,6 +159,16 @@ impl Drop for NoClose { } } +impl Output { + pub fn handle(&self) -> &Handle { + let nc = match *self { + Output::Console(ref c) => c, + Output::Pipe(ref c) => c, + }; + nc.0.as_ref().unwrap() + } +} + fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidInput, "text was not valid unicode") } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index ae480380f95eb..bcc70c2b8163e 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -263,7 +263,7 @@ impl Builder { /// /// The child thread may outlive the parent (unless the parent thread /// is the main thread; the whole process is terminated when the main - /// thread finishes.) The join handle can be used to block on + /// thread finishes). The join handle can be used to block on /// termination of the child thread, including recovering its panics. /// /// # Errors diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index d8beeb6a5503b..c22fba658373a 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -25,13 +25,14 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://doc.rust-lang.org/nightly/")] +#![feature(associated_consts)] #![feature(collections)] #![feature(core)] #![feature(libc)] #![feature(rustc_private)] #![feature(staged_api)] -#![feature(unicode)] #![feature(str_char)] +#![feature(unicode)] extern crate arena; extern crate fmt_macros; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f76de1f04ce2c..5f76c21492707 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -88,9 +88,9 @@ use std::slice; bitflags! { flags Restrictions: u8 { - const UNRESTRICTED = 0b0000, - const RESTRICTION_STMT_EXPR = 0b0001, - const RESTRICTION_NO_STRUCT_LITERAL = 0b0010, + const UNRESTRICTED = 0, + const RESTRICTION_STMT_EXPR = 1 << 0, + const RESTRICTION_NO_STRUCT_LITERAL = 1 << 1, } } @@ -339,7 +339,7 @@ impl<'a> Parser<'a> { buffer_start: 0, buffer_end: 0, tokens_consumed: 0, - restrictions: UNRESTRICTED, + restrictions: Restrictions::UNRESTRICTED, quote_depth: 0, obsolete_set: HashSet::new(), mod_path_stack: Vec::new(), @@ -2198,7 +2198,10 @@ impl<'a> Parser<'a> { if self.check(&token::OpenDelim(token::Brace)) { // This is a struct literal, unless we're prohibited // from parsing struct literals here. - if !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL) { + let prohibited = self.restrictions.contains( + Restrictions::RESTRICTION_NO_STRUCT_LITERAL + ); + if !prohibited { // It's a struct literal. try!(self.bump()); let mut fields = Vec::new(); @@ -2759,7 +2762,7 @@ impl<'a> Parser<'a> { } pub fn parse_assign_expr_with(&mut self, lhs: P) -> PResult> { - let restrictions = self.restrictions & RESTRICTION_NO_STRUCT_LITERAL; + let restrictions = self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL; let op_span = self.span; match self.token { token::Eq => { @@ -2814,7 +2817,7 @@ impl<'a> Parser<'a> { if self.token.can_begin_expr() { // parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. if self.token == token::OpenDelim(token::Brace) { - return !self.restrictions.contains(RESTRICTION_NO_STRUCT_LITERAL); + return !self.restrictions.contains(Restrictions::RESTRICTION_NO_STRUCT_LITERAL); } true } else { @@ -2828,7 +2831,7 @@ impl<'a> Parser<'a> { return self.parse_if_let_expr(); } let lo = self.last_span.lo; - let cond = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let thn = try!(self.parse_block()); let mut els: Option> = None; let mut hi = thn.span.hi; @@ -2846,7 +2849,7 @@ impl<'a> Parser<'a> { try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat_nopanic()); try!(self.expect(&token::Eq)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let thn = try!(self.parse_block()); let (hi, els) = if try!(self.eat_keyword(keywords::Else) ){ let expr = try!(self.parse_else_expr()); @@ -2905,7 +2908,7 @@ impl<'a> Parser<'a> { let lo = self.last_span.lo; let pat = try!(self.parse_pat_nopanic()); try!(self.expect_keyword(keywords::In)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let loop_block = try!(self.parse_block()); let hi = self.last_span.hi; @@ -2918,7 +2921,7 @@ impl<'a> Parser<'a> { return self.parse_while_let_expr(opt_ident); } let lo = self.last_span.lo; - let cond = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let body = try!(self.parse_block()); let hi = body.span.hi; return Ok(self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident))); @@ -2930,7 +2933,7 @@ impl<'a> Parser<'a> { try!(self.expect_keyword(keywords::Let)); let pat = try!(self.parse_pat_nopanic()); try!(self.expect(&token::Eq)); - let expr = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); let body = try!(self.parse_block()); let hi = body.span.hi; return Ok(self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident))); @@ -2945,7 +2948,7 @@ impl<'a> Parser<'a> { fn parse_match_expr(&mut self) -> PResult> { let lo = self.last_span.lo; - let discriminant = try!(self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL)); + let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); try!(self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace))); let mut arms: Vec = Vec::new(); while self.token != token::CloseDelim(token::Brace) { @@ -2966,7 +2969,7 @@ impl<'a> Parser<'a> { guard = Some(try!(self.parse_expr_nopanic())); } try!(self.expect(&token::FatArrow)); - let expr = try!(self.parse_expr_res(RESTRICTION_STMT_EXPR)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); let require_comma = !classify::expr_is_simple_block(&*expr) @@ -2988,7 +2991,7 @@ impl<'a> Parser<'a> { /// Parse an expression pub fn parse_expr_nopanic(&mut self) -> PResult> { - return self.parse_expr_res(UNRESTRICTED); + return self.parse_expr_res(Restrictions::UNRESTRICTED); } /// Parse an expression, subject to the given restrictions @@ -3564,7 +3567,7 @@ impl<'a> Parser<'a> { } // Remainder are line-expr stmts. - let e = try!(self.parse_expr_res(RESTRICTION_STMT_EXPR)); + let e = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID)) } } @@ -3573,7 +3576,7 @@ impl<'a> Parser<'a> { /// Is this expression a successfully-parsed statement? fn expr_is_complete(&mut self, e: &Expr) -> bool { - self.restrictions.contains(RESTRICTION_STMT_EXPR) && + self.restrictions.contains(Restrictions::RESTRICTION_STMT_EXPR) && !classify::expr_requires_semi_to_be_stmt(e) } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index db1a602b404f6..362439c146912 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -15,12 +15,13 @@ #include #if !defined(__WIN32__) -#include -#include #include +#include #include +#include +#include +#include #include -#include #else #include #include @@ -41,44 +42,31 @@ //include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64 #include "valgrind/valgrind.h" -#ifdef __APPLE__ -#if (TARGET_OS_IPHONE) -extern char **environ; -#endif -#endif - -#if defined(__FreeBSD__) || defined(__linux__) || defined(__ANDROID__) || \ - defined(__DragonFly__) || defined(__Bitrig__) || defined(__OpenBSD__) -extern char **environ; -#endif - -#if defined(__WIN32__) -char** -rust_env_pairs() { - return 0; -} -#else -char** -rust_env_pairs() { -#if defined(__APPLE__) && !(TARGET_OS_IPHONE) - char **environ = *_NSGetEnviron(); -#endif - return environ; -} -#endif - +#ifndef _WIN32 char* -#if defined(__WIN32__) -rust_list_dir_val(WIN32_FIND_DATA* entry_ptr) { - return entry_ptr->cFileName; -} -#else rust_list_dir_val(struct dirent* entry_ptr) { return entry_ptr->d_name; } + +int +rust_dir_get_mode(struct dirent* entry_ptr) { +#if defined(_DIRENT_HAVE_D_TYPE) + switch (entry_ptr->d_type) { + case DT_BLK: return S_IFBLK; + case DT_CHR: return S_IFCHR; + case DT_FIFO: return S_IFIFO; + case DT_LNK: return S_IFLNK; + case DT_REG: return S_IFREG; + case DT_SOCK: return S_IFSOCK; + } #endif + return -1; +} -#ifndef _WIN32 +ino_t +rust_dir_get_ino(struct dirent* entry_ptr) { + return entry_ptr->d_ino; +} DIR* rust_opendir(char *dirname) { @@ -94,21 +82,6 @@ int rust_dirent_t_size() { return sizeof(struct dirent); } - -#else - -void -rust_opendir() { -} - -void -rust_readdir() { -} - -void -rust_dirent_t_size() { -} - #endif uintptr_t @@ -173,26 +146,6 @@ rust_valgrind_stack_deregister(unsigned int id) { VALGRIND_STACK_DEREGISTER(id); } -#if defined(__WIN32__) - -void -rust_unset_sigprocmask() { - // empty stub for windows to keep linker happy -} - -#else - -void -rust_unset_sigprocmask() { - // this can't be safely converted to rust code because the - // representation of sigset_t is platform-dependent - sigset_t sset; - sigemptyset(&sset); - sigprocmask(SIG_SETMASK, &sset, NULL); -} - -#endif - #if defined(__DragonFly__) #include // In DragonFly __error() is an inline function and as such diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index fe0a7b454c17d..375c5fc746c5b 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -189,8 +189,10 @@ extern "C" LLVMValueRef LLVMBuildAtomicCmpXchg(LLVMBuilderRef B, failure_order )); } -extern "C" LLVMValueRef LLVMBuildAtomicFence(LLVMBuilderRef B, AtomicOrdering order) { - return wrap(unwrap(B)->CreateFence(order)); +extern "C" LLVMValueRef LLVMBuildAtomicFence(LLVMBuilderRef B, + AtomicOrdering order, + SynchronizationScope scope) { + return wrap(unwrap(B)->CreateFence(order, scope)); } extern "C" void LLVMSetDebug(int Enabled) { diff --git a/src/test/compile-fail/lint-no-drop-on-repr-extern.rs b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs new file mode 100644 index 0000000000000..2df57b08f283c --- /dev/null +++ b/src/test/compile-fail/lint-no-drop-on-repr-extern.rs @@ -0,0 +1,59 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Check we reject structs that mix a `Drop` impl with `#[repr(C)]`. +// +// As a special case, also check that we do not warn on such structs +// if they also are declared with `#[unsafe_no_drop_flag]` + +#![feature(unsafe_no_drop_flag)] +#![deny(drop_with_repr_extern)] + +#[repr(C)] struct As { x: Box } +#[repr(C)] enum Ae { Ae(Box), _None } + +struct Bs { x: Box } +enum Be { Be(Box), _None } + +#[repr(C)] struct Cs { x: Box } +//~^ NOTE the `#[repr(C)]` attribute is attached here + +impl Drop for Cs { fn drop(&mut self) { } } +//~^ ERROR implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]` + +#[repr(C)] enum Ce { Ce(Box), _None } +//~^ NOTE the `#[repr(C)]` attribute is attached here + +impl Drop for Ce { fn drop(&mut self) { } } +//~^ ERROR implementing Drop adds hidden state to types, possibly conflicting with `#[repr(C)]` + +#[unsafe_no_drop_flag] +#[repr(C)] struct Ds { x: Box } + +impl Drop for Ds { fn drop(&mut self) { } } + +#[unsafe_no_drop_flag] +#[repr(C)] enum De { De(Box), _None } + +impl Drop for De { fn drop(&mut self) { } } + +fn main() { + let a = As { x: Box::new(3) }; + let b = Bs { x: Box::new(3) }; + let c = Cs { x: Box::new(3) }; + let d = Ds { x: Box::new(3) }; + + println!("{:?}", (*a.x, *b.x, *c.x, *d.x)); + + let _a = Ae::Ae(Box::new(3)); + let _b = Be::Be(Box::new(3)); + let _c = Ce::Ce(Box::new(3)); + let _d = De::De(Box::new(3)); +} diff --git a/src/test/debuginfo/constant-debug-locs.rs b/src/test/debuginfo/constant-debug-locs.rs index 3d0af686586bd..5fc580755043d 100644 --- a/src/test/debuginfo/constant-debug-locs.rs +++ b/src/test/debuginfo/constant-debug-locs.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-android: FIXME(#10381) // min-lldb-version: 310 // compile-flags:-g diff --git a/src/test/debuginfo/constant-in-match-pattern.rs b/src/test/debuginfo/constant-in-match-pattern.rs index 58a1ba06b5452..785778e62f6ea 100644 --- a/src/test/debuginfo/constant-in-match-pattern.rs +++ b/src/test/debuginfo/constant-in-match-pattern.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-android: FIXME(#10381) // min-lldb-version: 310 // compile-flags:-g diff --git a/src/test/debuginfo/cross-crate-spans.rs b/src/test/debuginfo/cross-crate-spans.rs index 09ab30cbec1a2..ce6ef080c1f11 100644 --- a/src/test/debuginfo/cross-crate-spans.rs +++ b/src/test/debuginfo/cross-crate-spans.rs @@ -10,7 +10,6 @@ #![omit_gdb_pretty_printer_section] -// ignore-android: FIXME(#10381) // min-lldb-version: 310 // aux-build:cross_crate_spans.rs diff --git a/src/test/debuginfo/type-names.rs b/src/test/debuginfo/type-names.rs index 97b6bfacf80ee..4eae074120233 100644 --- a/src/test/debuginfo/type-names.rs +++ b/src/test/debuginfo/type-names.rs @@ -10,7 +10,7 @@ // ignore-tidy-linelength // ignore-lldb -// ignore-android: FIXME(#10381) +// ignore-android: FIXME(#24958) // compile-flags:-g diff --git a/src/test/debuginfo/vec-slices.rs b/src/test/debuginfo/vec-slices.rs index c806286deadaf..87fd2e9a65d03 100644 --- a/src/test/debuginfo/vec-slices.rs +++ b/src/test/debuginfo/vec-slices.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-android: FIXME(#10381) // ignore-windows // min-lldb-version: 310 diff --git a/src/test/run-pass/backtrace.rs b/src/test/run-pass/backtrace.rs index f4b62eb2e7c50..3f4849dbcb2b1 100644 --- a/src/test/run-pass/backtrace.rs +++ b/src/test/run-pass/backtrace.rs @@ -10,6 +10,7 @@ // no-pretty-expanded FIXME #15189 // ignore-windows FIXME #13259 +// ignore-android FIXME #17520 use std::env; use std::process::{Command, Stdio}; diff --git a/src/test/run-pass/intrinsics-integer.rs b/src/test/run-pass/intrinsics-integer.rs index f1d731c8b1d7a..8dbe927f06bf1 100644 --- a/src/test/run-pass/intrinsics-integer.rs +++ b/src/test/run-pass/intrinsics-integer.rs @@ -109,11 +109,6 @@ pub fn main() { assert_eq!(cttz32(100), 2); assert_eq!(cttz64(100), 2); - assert_eq!(cttz8(-1), 0); - assert_eq!(cttz16(-1), 0); - assert_eq!(cttz32(-1), 0); - assert_eq!(cttz64(-1), 0); - assert_eq!(bswap16(0x0A0B), 0x0B0A); assert_eq!(bswap32(0x0ABBCC0D), 0x0DCCBB0A); assert_eq!(bswap64(0x0122334455667708), 0x0877665544332201); diff --git a/src/test/run-pass/issue-24945-repeat-dash-opts.rs b/src/test/run-pass/issue-24945-repeat-dash-opts.rs new file mode 100644 index 0000000000000..33ac519a584f9 --- /dev/null +++ b/src/test/run-pass/issue-24945-repeat-dash-opts.rs @@ -0,0 +1,18 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test is just checking that we continue to accept `-g -g -O -O` +// as options to the compiler. + +// compile-flags:-g -g -O -O + +fn main() { + assert_eq!(1, 1); +} diff --git a/src/test/run-pass/issue24687-embed-debuginfo.rs b/src/test/run-pass/issue24687-embed-debuginfo.rs index ad30d53f1a65f..7754e9c3ad735 100644 --- a/src/test/run-pass/issue24687-embed-debuginfo.rs +++ b/src/test/run-pass/issue24687-embed-debuginfo.rs @@ -9,6 +9,7 @@ // except according to those terms. // aux-build:issue24687_lib.rs +// compile-flags:-g extern crate issue24687_lib as d; diff --git a/src/test/run-pass/nullable-pointer-opt-closures.rs b/src/test/run-pass/nullable-pointer-opt-closures.rs new file mode 100644 index 0000000000000..ac5634e6cdd16 --- /dev/null +++ b/src/test/run-pass/nullable-pointer-opt-closures.rs @@ -0,0 +1,43 @@ +// 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::mem; + +pub fn main() { + // By Ref Capture + let a = 10i32; + let b = Some(|| println!("{}", a)); + // When we capture by reference we can use any of the + // captures as the discriminant since they're all + // behind a pointer. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value Capture + let a = Box::new(12i32); + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since it's a `Box` we can use it + // as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::>()); + + // By Value Capture - Transitive case + let a = "Hello".to_string(); // String -> Vec -> Unique -> NonZero + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since down the chain it contains + // a `NonZero` field, we can use it as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value - No Optimization + let a = 14i32; + let b = Some(move || println!("{}", a)); + // We captured `a` by value but we can't use it as the discriminant + // thus we end up with an extra field for the discriminant + assert_eq!(mem::size_of_val(&b), mem::size_of::<(i32, i32)>()); +} diff --git a/src/test/run-pass/packed-struct-vec.rs b/src/test/run-pass/packed-struct-vec.rs index 9a327eb567266..4b32b881be738 100644 --- a/src/test/run-pass/packed-struct-vec.rs +++ b/src/test/run-pass/packed-struct-vec.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-android: FIXME(#9116) Bus error - use std::mem; #[repr(packed)]