From 298412a6e87be2213bbfc5e6fada9795c405ea13 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 3 Jun 2014 12:33:18 -0700 Subject: Improve error messages for io::fs --- src/libstd/io/fs.rs | 257 ++++++++++++++++++++++++++++++++++++++++----------- src/libstd/io/mod.rs | 51 ++++++++-- 2 files changed, 244 insertions(+), 64 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index c29c82ab2e9..1ac6fdc5ab1 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -56,9 +56,9 @@ use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; use io; +use io; use iter::Iterator; use kinds::Send; -use libc; use option::{Some, None, Option}; use owned::Box; use path::{Path, GenericPath}; @@ -138,7 +138,7 @@ impl File { Write => rtio::Write, ReadWrite => rtio::ReadWrite, }; - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_open(&path.to_c_str(), mode, access).map(|fd| { File { path: path.clone(), @@ -146,7 +146,11 @@ impl File { last_nread: -1 } }) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't open file", |e| { + format!("{}; path={}; mode={}; access={}", e, path.display(), + mode_string(mode), access_string(access)) + }) } /// Attempts to open a file in read-only mode. This function is equivalent to @@ -185,6 +189,7 @@ impl File { /// ``` pub fn create(path: &Path) -> IoResult { File::open_mode(path, Truncate, Write) + .update_desc("couldn't create file") } /// Returns the original path which was used to open this file. @@ -196,7 +201,9 @@ impl File { /// device. This will flush any internal buffers necessary to perform this /// operation. pub fn fsync(&mut self) -> IoResult<()> { - self.fd.fsync().map_err(IoError::from_rtio_error) + let err = self.fd.fsync().map_err(IoError::from_rtio_error); + err.update_err("couldn't fsync file", + |e| format!("{}; path={}", e, self.path.display())) } /// This function is similar to `fsync`, except that it may not synchronize @@ -204,7 +211,9 @@ impl File { /// must synchronize content, but don't need the metadata on disk. The goal /// of this method is to reduce disk operations. pub fn datasync(&mut self) -> IoResult<()> { - self.fd.datasync().map_err(IoError::from_rtio_error) + let err = self.fd.datasync().map_err(IoError::from_rtio_error); + err.update_err("couldn't datasync file", + |e| format!("{}; path={}", e, self.path.display())) } /// Either truncates or extends the underlying file, updating the size of @@ -216,7 +225,10 @@ impl File { /// will be extended to `size` and have all of the intermediate data filled /// in with 0s. pub fn truncate(&mut self, size: i64) -> IoResult<()> { - self.fd.truncate(size).map_err(IoError::from_rtio_error) + let err = self.fd.truncate(size).map_err(IoError::from_rtio_error); + err.update_err("couldn't truncate file", |e| { + format!("{}; path={}; size={}", e, self.path.display(), size) + }) } /// Tests whether this stream has reached EOF. @@ -229,10 +241,12 @@ impl File { /// Queries information about the underlying file. pub fn stat(&mut self) -> IoResult { - match self.fd.fstat() { + let err = match self.fd.fstat() { Ok(s) => Ok(from_rtio(s)), Err(e) => Err(IoError::from_rtio_error(e)), - } + }; + err.update_err("couldn't fstat file", + |e| format!("{}; path={}", e, self.path.display())) } } @@ -258,9 +272,11 @@ impl File { /// user lacks permissions to remove the file, or if some other filesystem-level /// error occurs. pub fn unlink(path: &Path) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_unlink(&path.to_c_str()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't unlink path", + |e| format!("{}; path={}", e, path.display())) } /// Given a path, query the file system to get information about a file, @@ -285,10 +301,12 @@ pub fn unlink(path: &Path) -> IoResult<()> { /// to perform a `stat` call on the given path or if there is no entry in the /// filesystem at the provided path. pub fn stat(path: &Path) -> IoResult { - match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) { + let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) { Ok(s) => Ok(from_rtio(s)), Err(e) => Err(IoError::from_rtio_error(e)), - } + }; + err.update_err("couldn't stat path", + |e| format!("{}; path={}", e, path.display())) } /// Perform the same operation as the `stat` function, except that this @@ -300,10 +318,12 @@ pub fn stat(path: &Path) -> IoResult { /// /// See `stat` pub fn lstat(path: &Path) -> IoResult { - match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) { + let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) { Ok(s) => Ok(from_rtio(s)), Err(e) => Err(IoError::from_rtio_error(e)), - } + }; + err.update_err("couldn't lstat path", + |e| format!("{}; path={}", e, path.display())) } fn from_rtio(s: rtio::FileStat) -> FileStat { @@ -359,9 +379,12 @@ fn from_rtio(s: rtio::FileStat) -> FileStat { /// permissions to view the contents, or if some other intermittent I/O error /// occurs. pub fn rename(from: &Path, to: &Path) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_rename(&from.to_c_str(), &to.to_c_str()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't rename path", |e| { + format!("{}; from={}; to={}", e, from.display(), to.display()) + }) } /// Copies the contents of one file to another. This function will also @@ -393,12 +416,17 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> { /// ensured to not exist, there is nothing preventing the destination from /// being created and then destroyed by this operation. pub fn copy(from: &Path, to: &Path) -> IoResult<()> { + fn update_err(result: IoResult, from: &Path, to: &Path) -> IoResult { + result.update_err("couldn't copy path", + |e| format!("{}; from={}; to={}", e, from.display(), to.display())) + } + if !from.is_file() { - return Err(IoError { + return update_err(Err(IoError { kind: io::MismatchedFileTypeForOperation, desc: "the source path is not an existing file", - detail: None, - }) + detail: None + }), from, to) } let mut reader = try!(File::open(from)); @@ -409,12 +437,12 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> { let amt = match reader.read(buf) { Ok(n) => n, Err(ref e) if e.kind == io::EndOfFile => { break } - Err(e) => return Err(e) + Err(e) => return update_err(Err(e), from, to) }; try!(writer.write(buf.slice_to(amt))); } - chmod(to, try!(from.stat()).perm) + chmod(to, try!(update_err(from.stat(), from, to)).perm) } /// Changes the permission mode bits found on a file or a directory. This @@ -439,33 +467,45 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> { /// Some possible error situations are not having the permission to /// change the attributes of a file or the file not existing. pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_chmod(&path.to_c_str(), mode.bits() as uint) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't chmod path", |e| { + format!("{}; path={}; mode={}", e, path.display(), mode) + }) } /// Change the user and group owners of a file at the specified path. pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_chown(&path.to_c_str(), uid, gid) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't chown path", |e| { + format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid) + }) } /// Creates a new hard link on the filesystem. The `dst` path will be a /// link pointing to the `src` path. Note that systems often require these /// two paths to both be located on the same filesystem. pub fn link(src: &Path, dst: &Path) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_link(&src.to_c_str(), &dst.to_c_str()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't link path", |e| { + format!("{}; src={}; dest={}", e, src.display(), dst.display()) + }) } /// Creates a new symbolic link on the filesystem. The `dst` path will be a /// symlink pointing to the `src` path. pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_symlink(&src.to_c_str(), &dst.to_c_str()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't symlink path", |e| { + format!("{}; src={}; dest={}", e, src.display(), dst.display()) + }) } /// Reads a symlink, returning the file that the symlink points to. @@ -475,9 +515,11 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { /// This function will return an error on failure. Failure conditions include /// reading a file that does not exist or reading a file which is not a symlink. pub fn readlink(path: &Path) -> IoResult { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { Ok(Path::new(try!(io.fs_readlink(&path.to_c_str())))) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't resolve symlink for path", + |e| format!("{}; path={}", e, path.display())) } /// Create a new, empty directory at the provided path @@ -498,9 +540,12 @@ pub fn readlink(path: &Path) -> IoResult { /// This call will return an error if the user lacks permissions to make a new /// directory at the provided path, or if the directory already exists. pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_mkdir(&path.to_c_str(), mode.bits() as uint) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't create directory", |e| { + format!("{}; path={}; mode={}", e, path.display(), mode) + }) } /// Remove an existing, empty directory @@ -520,9 +565,11 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { /// This call will return an error if the user lacks permissions to remove the /// directory at the provided path, or if the directory isn't empty. pub fn rmdir(path: &Path) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_rmdir(&path.to_c_str()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't remove directory", + |e| format!("{}; path={}", e, path.display())) } /// Retrieve a vector containing all entries within a provided directory @@ -557,11 +604,13 @@ pub fn rmdir(path: &Path) -> IoResult<()> { /// permissions to view the contents or if the `path` points at a non-directory /// file pub fn readdir(path: &Path) -> IoResult> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).move_iter().map(|a| { Path::new(a) }).collect()) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't read directory", + |e| format!("{}; path={}", e, path.display())) } /// Returns an iterator which will recursively walk the directory structure @@ -569,7 +618,11 @@ pub fn readdir(path: &Path) -> IoResult> { /// perform iteration in some top-down order. The contents of unreadable /// subdirectories are ignored. pub fn walk_dir(path: &Path) -> IoResult { - Ok(Directories { stack: try!(readdir(path)) }) + Ok(Directories { + stack: try!(readdir(path).update_err("couldn't walk directory", + |e| format!("{}; path={}", + e, path.display()))) + }) } /// An iterator which walks over a directory @@ -582,7 +635,12 @@ impl Iterator for Directories { match self.stack.pop() { Some(path) => { if path.is_dir() { - match readdir(&path) { + let result = readdir(&path) + .update_err("couldn't advance Directories iterator", + |e| format!("{}; path={}", + e, path.display())); + + match result { Ok(dirs) => { self.stack.push_all_move(dirs); } Err(..) => {} } @@ -614,7 +672,11 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { for c in comps { curpath.push(c); - match mkdir(&curpath, mode) { + let result = mkdir(&curpath, mode) + .update_err("couldn't recursively mkdir", + |e| format!("{}; path={}", e, path.display())); + + match result { Err(mkdir_err) => { // already exists ? if try!(stat(&curpath)).kind != io::TypeDirectory { @@ -639,8 +701,20 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { let mut rm_stack = Vec::new(); rm_stack.push(path.clone()); + fn rmdir_failed(err: &IoError, path: &Path) -> String { + format!("rmdir_recursive failed; path={}; cause={}", + path.display(), err) + } + + fn update_err(err: IoResult, path: &Path) -> IoResult { + err.update_err("couldn't recursively rmdir", + |e| rmdir_failed(e, path)) + } + while !rm_stack.is_empty() { - let children = try!(readdir(rm_stack.last().unwrap())); + let children = try!(readdir(rm_stack.last().unwrap()) + .update_detail(|e| rmdir_failed(e, path))); + let mut has_child_dir = false; // delete all regular files in the way and push subdirs @@ -648,17 +722,17 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { for child in children.move_iter() { // FIXME(#12795) we should use lstat in all cases let child_type = match cfg!(windows) { - true => try!(stat(&child)).kind, - false => try!(lstat(&child)).kind + true => try!(update_err(stat(&child), path)), + false => try!(update_err(lstat(&child), path)) }; - if child_type == io::TypeDirectory { + if child_type.kind == io::TypeDirectory { rm_stack.push(child); has_child_dir = true; } else { // we can carry on safely if the file is already gone // (eg: deleted by someone else since readdir) - match unlink(&child) { + match update_err(unlink(&child), path) { Ok(()) => (), Err(ref e) if e.kind == io::FileNotFound => (), Err(e) => return Err(e) @@ -668,7 +742,8 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { // if no subdir was found, let's pop and delete if !has_child_dir { - match rmdir(&rm_stack.pop().unwrap()) { + let result = update_err(rmdir(&rm_stack.pop().unwrap()), path); + match result { Ok(()) => (), Err(ref e) if e.kind == io::FileNotFound => (), Err(e) => return Err(e) @@ -685,18 +760,28 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { /// be in milliseconds. // FIXME(#10301) these arguments should not be u64 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> { - LocalIo::maybe_raise(|io| { + let err = LocalIo::maybe_raise(|io| { io.fs_utime(&path.to_c_str(), atime, mtime) - }).map_err(IoError::from_rtio_error) + }).map_err(IoError::from_rtio_error); + err.update_err("couldn't change_file_times", + |e| format!("{}; path={}", e, path.display())) } impl Reader for File { fn read(&mut self, buf: &mut [u8]) -> IoResult { - match self.fd.read(buf) { + fn update_err(result: IoResult, file: &File) -> IoResult { + result.update_err("couldn't read file", + |e| format!("{}; path={}", + e, file.path.display())) + } + + let result: IoResult = update_err(self.fd.read(buf), self); + + match result { Ok(read) => { self.last_nread = read; match read { - 0 => Err(io::standard_error(io::EndOfFile)), + 0 => update_err(Err(standard_error(io::EndOfFile)), self), _ => Ok(read as uint) } }, @@ -707,13 +792,17 @@ impl Reader for File { impl Writer for File { fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.fd.write(buf).map_err(IoError::from_rtio_error) + let err = self.fd.write(buf).map_err(IoError::from_rtio_error) + err.update_err("couldn't write to file", + |e| format!("{}; path={}", e, self.path.display())) } } impl Seek for File { fn tell(&self) -> IoResult { - self.fd.tell().map_err(IoError::from_rtio_error) + let err = self.fd.tell().map_err(IoError::from_rtio_error); + err.update_err("couldn't retrieve file cursor (`tell`)", + |e| format!("{}; path={}", e, self.path.display())) } fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { @@ -722,14 +811,16 @@ impl Seek for File { SeekCur => rtio::SeekCur, SeekEnd => rtio::SeekEnd, }; - match self.fd.seek(pos, style) { + let err = match self.fd.seek(pos, style) { Ok(_) => { // successful seek resets EOF indicator self.last_nread = -1; Ok(()) } Err(e) => Err(IoError::from_rtio_error(e)), - } + }; + err.update_err("couldn't seek in file", + |e| format!("{}; path={}", e, self.path.display())) } } @@ -779,6 +870,22 @@ impl path::Path { } } +fn mode_string(mode: FileMode) -> &'static str { + match mode { + super::Open => "open", + super::Append => "append", + super::Truncate => "truncate" + } +} + +fn access_string(access: FileAccess) -> &'static str { + match access { + super::Read => "read", + super::Write => "write", + super::ReadWrite => "readwrite" + } +} + #[cfg(test)] #[allow(unused_imports)] mod test { @@ -801,6 +908,14 @@ mod test { } ) ) + macro_rules! error( ($e:expr, $s:expr) => ( + match $e { + Ok(val) => fail!("Should have been an error, was {:?}", val), + Err(ref err) => assert!(err.to_str().as_slice().contains($s.as_slice()), + format!("`{}` did not contain `{}`", err, $s)) + } + ) ) + struct TempDir(Path); impl TempDir { @@ -856,13 +971,21 @@ mod test { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_that_does_not_exist.txt"); let result = File::open_mode(filename, Open, Read); - assert!(result.is_err()); + + error!(result, "couldn't open file"); + error!(result, "no such file or directory"); + error!(result, format!("path={}; mode=open; access=read", filename.display())); }) iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); - assert!(unlink(filename).is_err()); + + let result = unlink(filename); + + error!(result, "couldn't unlink path"); + error!(result, "no such file or directory"); + error!(result, format!("path={}", filename.display())); }) iotest!(fn file_test_io_non_positional_read() { @@ -1091,6 +1214,22 @@ mod test { assert!(dir.is_dir()) }) + iotest!(fn recursive_mkdir_failure() { + let tmpdir = tmpdir(); + let dir = tmpdir.join("d1"); + let file = dir.join("f1"); + + check!(mkdir_recursive(&dir, io::UserRWX)); + check!(File::create(&file)); + + let result = mkdir_recursive(&file, io::UserRWX); + + error!(result, "couldn't recursively mkdir"); + error!(result, "couldn't create directory"); + error!(result, "mode=FilePermission { bits: 448 }"); + error!(result, format!("path={}", file.display())); + }) + iotest!(fn recursive_mkdir_slash() { check!(mkdir_recursive(&Path::new("/"), io::UserRWX)); }) @@ -1147,6 +1286,12 @@ mod test { iotest!(fn copy_file_does_not_exist() { let from = Path::new("test/nonexistent-bogus-path"); let to = Path::new("test/other-bogus-path"); + + error!(copy(&from, &to), + format!("couldn't copy path (the source path is not an \ + existing file; from={}; to={})", + from.display(), to.display())); + match copy(&from, &to) { Ok(..) => fail!(), Err(..) => { diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index a1e0fa88978..a7f84899a62 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -232,7 +232,7 @@ use owned::Box; use result::{Ok, Err, Result}; use rt::rtio; use slice::{Vector, MutableVector, ImmutableVector}; -use str::{StrSlice, StrAllocating}; +use str::{Str, StrSlice, StrAllocating}; use str; use string::String; use uint; @@ -309,6 +309,7 @@ impl IoError { /// struct is filled with an allocated string describing the error /// in more detail, retrieved from the operating system. pub fn from_errno(errno: uint, detail: bool) -> IoError { + #[cfg(windows)] fn get_err(errno: i32) -> (IoErrorKind, &'static str) { match errno { @@ -388,8 +389,8 @@ impl IoError { IoError { kind: kind, desc: desc, - detail: if detail { - Some(os::error_string(errno)) + detail: if detail && kind == OtherIoError { + Some(os::error_string(errno).as_slice().chars().map(|c| c.to_lowercase()).collect()) } else { None }, @@ -420,10 +421,13 @@ impl IoError { impl fmt::Show for IoError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - try!(write!(fmt, "{}", self.desc)); - match self.detail { - Some(ref s) => write!(fmt, " ({})", *s), - None => Ok(()) + match *self { + IoError { kind: OtherIoError, desc: "unknown error", detail: Some(ref detail) } => + write!(fmt, "{}", detail), + IoError { detail: None, desc, .. } => + write!(fmt, "{}", desc), + IoError { detail: Some(ref detail), desc, .. } => + write!(fmt, "{} ({})", desc, detail) } } } @@ -484,6 +488,37 @@ pub enum IoErrorKind { NoProgress, } +/// A trait that lets you add a `detail` to an IoError easily +trait UpdateIoError { + /// Returns an IoError with updated description and detail + fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> Self; + + /// Returns an IoError with updated detail + fn update_detail(self, detail: |&IoError| -> String) -> Self; + + /// Returns an IoError with update description + fn update_desc(self, desc: &'static str) -> Self; +} + +impl UpdateIoError for IoResult { + fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> IoResult { + self.map_err(|mut e| { + let detail = detail(&e); + e.desc = desc; + e.detail = Some(detail); + e + }) + } + + fn update_detail(self, detail: |&IoError| -> String) -> IoResult { + self.map_err(|mut e| { e.detail = Some(detail(&e)); e }) + } + + fn update_desc(self, desc: &'static str) -> IoResult { + self.map_err(|mut e| { e.desc = desc; e }) + } +} + static NO_PROGRESS_LIMIT: uint = 1000; /// A trait for objects which are byte-oriented streams. Readers are defined by @@ -1577,7 +1612,7 @@ pub fn standard_error(kind: IoErrorKind) -> IoError { ConnectionAborted => "connection aborted", NotConnected => "not connected", BrokenPipe => "broken pipe", - PathAlreadyExists => "file exists", + PathAlreadyExists => "file already exists", PathDoesntExist => "no such file", MismatchedFileTypeForOperation => "mismatched file type", ResourceUnavailable => "resource unavailable", -- cgit 1.4.1-3-g733a5 From 03ec8e5cc91b3b6b6ab98ef70aa63a0965c5f6c1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 Jun 2014 10:24:04 -0700 Subject: std: Rebase better errors on master --- src/libstd/io/fs.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 1ac6fdc5ab1..008be8ffaae 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -52,13 +52,15 @@ fs::unlink(&path); use c_str::ToCStr; use clone::Clone; use collections::Collection; +use io::standard_error; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; -use io; +use io::UpdateIoError; use io; use iter::Iterator; use kinds::Send; +use libc; use option::{Some, None, Option}; use owned::Box; use path::{Path, GenericPath}; @@ -67,6 +69,7 @@ use result::{Err, Ok}; use rt::rtio::LocalIo; use rt::rtio; use slice::ImmutableVector; +use string::String; use vec::Vec; /// Unconstrained file access type that exposes read and write operations @@ -128,18 +131,18 @@ impl File { pub fn open_mode(path: &Path, mode: FileMode, access: FileAccess) -> IoResult { - let mode = match mode { + let rtio_mode = match mode { Open => rtio::Open, Append => rtio::Append, Truncate => rtio::Truncate, }; - let access = match access { + let rtio_access = match access { Read => rtio::Read, Write => rtio::Write, ReadWrite => rtio::ReadWrite, }; let err = LocalIo::maybe_raise(|io| { - io.fs_open(&path.to_c_str(), mode, access).map(|fd| { + io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| { File { path: path.clone(), fd: fd, @@ -775,7 +778,8 @@ impl Reader for File { e, file.path.display())) } - let result: IoResult = update_err(self.fd.read(buf), self); + let result = update_err(self.fd.read(buf) + .map_err(IoError::from_rtio_error), self); match result { Ok(read) => { @@ -785,14 +789,14 @@ impl Reader for File { _ => Ok(read as uint) } }, - Err(e) => Err(IoError::from_rtio_error(e)), + Err(e) => Err(e) } } } impl Writer for File { fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let err = self.fd.write(buf).map_err(IoError::from_rtio_error) + let err = self.fd.write(buf).map_err(IoError::from_rtio_error); err.update_err("couldn't write to file", |e| format!("{}; path={}", e, self.path.display())) } -- cgit 1.4.1-3-g733a5 From c9f3f47702602d196de414aa4f10bb2c2148ab9a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 12 Jun 2014 14:08:44 -0700 Subject: librustc: Forbid `transmute` from being called on types whose size is only known post-monomorphization, and report `transmute` errors before the code is generated for that `transmute`. This can break code that looked like: unsafe fn f(x: T) { let y: int = transmute(x); } Change such code to take a type parameter that has the same size as the type being transmuted to. Closes #12898. [breaking-change] --- src/libcore/intrinsics.rs | 14 ++ src/libcore/mem.rs | 25 +--- src/librustc/driver/driver.rs | 3 + src/librustc/lib.rs | 1 + src/librustc/middle/intrinsicck.rs | 155 +++++++++++++++++++++ src/librustc/middle/trans/base.rs | 6 + src/librustc/middle/trans/intrinsic.rs | 40 +++++- src/librustc/middle/ty.rs | 20 ++- src/librustc/plugin/load.rs | 6 +- src/librustdoc/plugins.rs | 7 +- src/libstd/dynamic_lib.rs | 2 +- src/test/compile-fail/transmute-different-sizes.rs | 27 ++++ src/test/compile-fail/transmute-type-parameters.rs | 48 +++++++ 13 files changed, 323 insertions(+), 31 deletions(-) create mode 100644 src/librustc/middle/intrinsicck.rs create mode 100644 src/test/compile-fail/transmute-different-sizes.rs create mode 100644 src/test/compile-fail/transmute-type-parameters.rs (limited to 'src/libstd') diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index d61416a68e0..33f2a49d6d5 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -307,6 +307,20 @@ extern "rust-intrinsic" { /// `forget` is unsafe because the caller is responsible for /// ensuring the argument is deallocated already. pub fn forget(_: T) -> (); + + /// Unsafely transforms a value of one type into a value of another type. + /// + /// Both types must have the same size and alignment, and this guarantee + /// is enforced at compile-time. + /// + /// # Example + /// + /// ```rust + /// use std::mem; + /// + /// let v: &[u8] = unsafe { mem::transmute("L") }; + /// assert!(v == [76u8]); + /// ``` pub fn transmute(e: T) -> U; /// Returns `true` if a type requires drop glue. diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 8933c95350d..237efcd0096 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -17,6 +17,8 @@ use ptr; use intrinsics; use intrinsics::{bswap16, bswap32, bswap64}; +pub use intrinsics::transmute; + /// Returns the size of a type in bytes. #[inline] #[stable] @@ -412,29 +414,6 @@ pub fn drop(_x: T) { } #[stable] pub unsafe fn forget(thing: T) { intrinsics::forget(thing) } -/// Unsafely transforms a value of one type into a value of another type. -/// -/// Both types must have the same size and alignment, and this guarantee is -/// enforced at compile-time. -/// -/// # Example -/// -/// ```rust -/// use std::mem; -/// -/// let v: &[u8] = unsafe { mem::transmute("L") }; -/// assert!(v == [76u8]); -/// ``` -#[inline] -#[unstable = "this function will be modified to reject invocations of it which \ - cannot statically prove that T and U are the same size. For \ - example, this function, as written today, will be rejected in \ - the future because the size of T and U cannot be statically \ - known to be the same"] -pub unsafe fn transmute(thing: T) -> U { - intrinsics::transmute(thing) -} - /// Interprets `src` as `&U`, and then reads `src` without moving the contained /// value. /// diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 880c1d6d510..2268ce84365 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -333,6 +333,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "privacy checking", maps, |(a, b)| middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate)); + time(time_passes, "intrinsic checking", (), |_| + middle::intrinsicck::check_crate(&ty_cx, krate)); + time(time_passes, "effect checking", (), |_| middle::effect::check_crate(&ty_cx, krate)); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index f79aaa40d21..1e39aaa3a5f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -85,6 +85,7 @@ pub mod middle { pub mod dependency_format; pub mod weak_lang_items; pub mod save; + pub mod intrinsicck; } pub mod front { diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs new file mode 100644 index 00000000000..93913f84271 --- /dev/null +++ b/src/librustc/middle/intrinsicck.rs @@ -0,0 +1,155 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use metadata::csearch; +use middle::def::DefFn; +use middle::subst::Subst; +use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn}; +use middle::ty; + +use syntax::abi::RustIntrinsic; +use syntax::ast::DefId; +use syntax::ast; +use syntax::ast_map::NodeForeignItem; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::visit::Visitor; +use syntax::visit; + +fn type_size_is_affected_by_type_parameters(tcx: &ty::ctxt, typ: ty::t) + -> bool { + let mut result = false; + ty::maybe_walk_ty(typ, |typ| { + match ty::get(typ).sty { + ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_ptr(_) | + ty::ty_rptr(..) | ty::ty_bare_fn(..) | ty::ty_closure(..) => { + false + } + ty::ty_param(_) => { + result = true; + // No need to continue; we now know the result. + false + } + ty::ty_enum(did, ref substs) => { + for enum_variant in (*ty::enum_variants(tcx, did)).iter() { + for argument_type in enum_variant.args.iter() { + let argument_type = argument_type.subst(tcx, substs); + result = result || + type_size_is_affected_by_type_parameters( + tcx, + argument_type); + } + } + + // Don't traverse substitutions. + false + } + ty::ty_struct(did, ref substs) => { + for field in ty::struct_fields(tcx, did, substs).iter() { + result = result || + type_size_is_affected_by_type_parameters(tcx, + field.mt.ty); + } + + // Don't traverse substitutions. + false + } + _ => true, + } + }); + result +} + +struct IntrinsicCheckingVisitor<'a> { + tcx: &'a ctxt, +} + +impl<'a> IntrinsicCheckingVisitor<'a> { + fn def_id_is_transmute(&self, def_id: DefId) -> bool { + if def_id.krate == ast::LOCAL_CRATE { + match self.tcx.map.get(def_id.node) { + NodeForeignItem(ref item) => { + token::get_ident(item.ident) == + token::intern_and_get_ident("transmute") + } + _ => false, + } + } else { + match csearch::get_item_path(self.tcx, def_id).last() { + None => false, + Some(ref last) => { + token::get_name(last.name()) == + token::intern_and_get_ident("transmute") + } + } + } + } + + fn check_transmute(&self, span: Span, from: ty::t, to: ty::t) { + if type_size_is_affected_by_type_parameters(self.tcx, from) { + self.tcx.sess.span_err(span, + "cannot transmute from a type that \ + contains type parameters"); + } + if type_size_is_affected_by_type_parameters(self.tcx, to) { + self.tcx.sess.span_err(span, + "cannot transmute to a type that contains \ + type parameters"); + } + + let restriction = TransmuteRestriction { + span: span, + from: from, + to: to, + }; + self.tcx.transmute_restrictions.borrow_mut().push(restriction); + } +} + +impl<'a> Visitor<()> for IntrinsicCheckingVisitor<'a> { + fn visit_expr(&mut self, expr: &ast::Expr, (): ()) { + match expr.node { + ast::ExprPath(..) => { + match ty::resolve_expr(self.tcx, expr) { + DefFn(did, _) if self.def_id_is_transmute(did) => { + let typ = ty::node_id_to_type(self.tcx, expr.id); + match ty::get(typ).sty { + ty_bare_fn(ref bare_fn_ty) + if bare_fn_ty.abi == RustIntrinsic => { + let from = *bare_fn_ty.sig.inputs.get(0); + let to = bare_fn_ty.sig.output; + self.check_transmute(expr.span, from, to); + } + _ => { + self.tcx + .sess + .span_bug(expr.span, + "transmute wasn't a bare fn?!"); + } + } + } + _ => {} + } + } + _ => {} + } + + visit::walk_expr(self, expr, ()); + } +} + +pub fn check_crate(tcx: &ctxt, krate: &ast::Crate) { + let mut visitor = IntrinsicCheckingVisitor { + tcx: tcx, + }; + + visit::walk_crate(&mut visitor, krate, ()); +} + diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 3e15d5a1a61..14c8d5454aa 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -59,6 +59,7 @@ use middle::trans::expr; use middle::trans::foreign; use middle::trans::glue; use middle::trans::inline; +use middle::trans::intrinsic; use middle::trans::machine; use middle::trans::machine::{llalign_of_min, llsize_of, llsize_of_real}; use middle::trans::meth; @@ -2329,6 +2330,11 @@ pub fn trans_crate(krate: ast::Crate, let ccx = CrateContext::new(llmod_id.as_slice(), tcx, exp_map2, Sha256::new(), link_meta, reachable); + + // First, verify intrinsics. + intrinsic::check_intrinsics(&ccx); + + // Next, translate the module. { let _icx = push_ctxt("text"); trans_mod(&ccx, &krate.module); diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 5f37365e743..648a2c335e6 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -390,7 +390,7 @@ pub fn trans_intrinsic(ccx: &CrateContext, ast_map::NodeExpr(e) => e.span, _ => fail!("transmute has non-expr arg"), }; - ccx.sess().span_fatal(sp, + ccx.sess().span_bug(sp, format!("transmute called on types with different sizes: \ {} ({} bit{}) to \ {} ({} bit{})", @@ -564,3 +564,41 @@ pub fn trans_intrinsic(ccx: &CrateContext, } fcx.cleanup(); } + +/// Performs late verification that intrinsics are used correctly. At present, +/// the only intrinsic that needs such verification is `transmute`. +pub fn check_intrinsics(ccx: &CrateContext) { + for transmute_restriction in ccx.tcx + .transmute_restrictions + .borrow() + .iter() { + let llfromtype = type_of::sizing_type_of(ccx, + transmute_restriction.from); + let lltotype = type_of::sizing_type_of(ccx, + transmute_restriction.to); + let from_type_size = machine::llbitsize_of_real(ccx, llfromtype); + let to_type_size = machine::llbitsize_of_real(ccx, lltotype); + if from_type_size != to_type_size { + ccx.sess() + .span_err(transmute_restriction.span, + format!("transmute called on types with different sizes: \ + {} ({} bit{}) to {} ({} bit{})", + ty_to_str(ccx.tcx(), transmute_restriction.from), + from_type_size as uint, + if from_type_size == 1 { + "" + } else { + "s" + }, + ty_to_str(ccx.tcx(), transmute_restriction.to), + to_type_size as uint, + if to_type_size == 1 { + "" + } else { + "s" + }).as_slice()); + } + } + ccx.sess().abort_if_errors(); +} + diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 485011e1cb3..02300fccdae 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -235,6 +235,17 @@ pub enum AutoRef { AutoBorrowObj(Region, ast::Mutability), } +/// A restriction that certain types must be the same size. The use of +/// `transmute` gives rise to these restrictions. +pub struct TransmuteRestriction { + /// The span from whence the restriction comes. + pub span: Span, + /// The type being transmuted from. + pub from: t, + /// The type being transmuted to. + pub to: t, +} + /// The data structure to keep track of all the information that typechecker /// generates so that so that it can be reused and doesn't have to be redone /// later on. @@ -357,6 +368,11 @@ pub struct ctxt { pub node_lint_levels: RefCell>, + + /// The types that must be asserted to be the same size for `transmute` + /// to be valid. We gather up these restrictions in the intrinsicck pass + /// and check them in trans. + pub transmute_restrictions: RefCell>, } pub enum tbox_flag { @@ -1118,6 +1134,7 @@ pub fn mk_ctxt(s: Session, vtable_map: RefCell::new(FnvHashMap::new()), dependency_formats: RefCell::new(HashMap::new()), node_lint_levels: RefCell::new(HashMap::new()), + transmute_restrictions: RefCell::new(Vec::new()), } } @@ -2711,8 +2728,7 @@ pub fn pat_ty(cx: &ctxt, pat: &ast::Pat) -> t { // // NB (2): This type doesn't provide type parameter substitutions; e.g. if you // ask for the type of "id" in "id(3)", it will return "fn(&int) -> int" -// instead of "fn(t) -> T with T = int". If this isn't what you want, see -// expr_ty_params_and_ty() below. +// instead of "fn(t) -> T with T = int". pub fn expr_ty(cx: &ctxt, expr: &ast::Expr) -> t { return node_id_to_type(cx, expr.id); } diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs index 97ffcf279ae..7d74b8c7296 100644 --- a/src/librustc/plugin/load.rs +++ b/src/librustc/plugin/load.rs @@ -126,9 +126,11 @@ impl<'a> PluginLoader<'a> { }; unsafe { - let registrar: PluginRegistrarFun = + let registrar = match lib.symbol(symbol.as_slice()) { - Ok(registrar) => registrar, + Ok(registrar) => { + mem::transmute::<*u8,PluginRegistrarFun>(registrar) + } // again fatal if we can't register macros Err(err) => self.sess.span_fatal(vi.span, err.as_slice()) }; diff --git a/src/librustdoc/plugins.rs b/src/librustdoc/plugins.rs index fee1d63a274..e5bced8038b 100644 --- a/src/librustdoc/plugins.rs +++ b/src/librustdoc/plugins.rs @@ -12,6 +12,7 @@ use clean; use dl = std::dynamic_lib; use serialize::json; +use std::mem; use std::string::String; pub type PluginJson = Option<(String, json::Json)>; @@ -45,9 +46,11 @@ impl PluginManager { let x = self.prefix.join(libname(name)); let lib_result = dl::DynamicLibrary::open(Some(&x)); let lib = lib_result.unwrap(); - let plugin = unsafe { lib.symbol("rustdoc_plugin_entrypoint") }.unwrap(); + unsafe { + let plugin = lib.symbol("rustdoc_plugin_entrypoint").unwrap(); + self.callbacks.push(mem::transmute::<*u8,PluginCallback>(plugin)); + } self.dylibs.push(lib); - self.callbacks.push(plugin); } /// Load a normal Rust function as a plugin. diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index fa6efc8a4b1..61f7997071e 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -134,7 +134,7 @@ impl DynamicLibrary { } /// Access the value at the symbol of the dynamic library - pub unsafe fn symbol(&self, symbol: &str) -> Result { + pub unsafe fn symbol(&self, symbol: &str) -> Result<*T, String> { // This function should have a lifetime constraint of 'a on // T but that feature is still unimplemented diff --git a/src/test/compile-fail/transmute-different-sizes.rs b/src/test/compile-fail/transmute-different-sizes.rs new file mode 100644 index 00000000000..5ea1c496c76 --- /dev/null +++ b/src/test/compile-fail/transmute-different-sizes.rs @@ -0,0 +1,27 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that `transmute` cannot be called on types of different size. + +use std::mem::transmute; + +unsafe fn f() { + let _: i8 = transmute(16i16); + //~^ ERROR transmute called on types with different sizes +} + +unsafe fn g(x: &T) { + let _: i8 = transmute(x); + //~^ ERROR transmute called on types with different sizes +} + +fn main() {} + + diff --git a/src/test/compile-fail/transmute-type-parameters.rs b/src/test/compile-fail/transmute-type-parameters.rs new file mode 100644 index 00000000000..53391a0e894 --- /dev/null +++ b/src/test/compile-fail/transmute-type-parameters.rs @@ -0,0 +1,48 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that `transmute` cannot be called on type parameters. + +use std::mem::transmute; + +unsafe fn f(x: T) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +unsafe fn g(x: (T, int)) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +unsafe fn h(x: [T, ..10]) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +struct Bad { + f: T, +} + +unsafe fn i(x: Bad) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +enum Worse { + A(T), + B, +} + +unsafe fn j(x: Worse) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +unsafe fn k(x: Option) { + let _: int = transmute(x); //~ ERROR cannot transmute +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From b7af25060a1b0451cb06085ba5893980bc4e5333 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Jun 2014 21:34:32 -0700 Subject: Rolling up PRs in the queue Closes #14797 (librustc: Fix the issue with labels shadowing variable names by making) Closes #14823 (Improve error messages for io::fs) Closes #14827 (libsyntax: Allow `+` to separate trait bounds from objects.) Closes #14834 (configure: Don't sync unused submodules) Closes #14838 (Remove typo on collections::treemap::UnionItems) Closes #14839 (Fix the unused struct field lint for struct variants) Closes #14840 (Clarify `Any` docs) Closes #14846 (rustc: [T, ..N] and [T, ..N+1] are not the same) Closes #14847 (Audit usage of NativeMutex) Closes #14850 (remove unnecessary PaX detection) Closes #14856 (librustc: Take in account mutability when casting array to raw ptr.) Closes #14859 (librustc: Forbid `transmute` from being called on types whose size is) Closes #14860 (Fix `quote_pat!` & parse outer attributes in `quote_item!`) --- src/libnative/io/c_win32.rs | 25 +++++++++++++--------- src/libstd/dynamic_lib.rs | 3 ++- src/libstd/io/fs.rs | 8 +++++-- src/libstd/rt/backtrace.rs | 8 +++---- src/test/compile-fail/issue-14845.rs | 2 +- src/test/compile-fail/transmute-different-sizes.rs | 2 ++ src/test/pretty/path-type-bounds.rs | 6 +++--- src/test/run-pass-fulldeps/quote-tokens.rs | 3 +++ src/test/run-pass/issue-14837.rs | 2 +- 9 files changed, 37 insertions(+), 22 deletions(-) (limited to 'src/libstd') diff --git a/src/libnative/io/c_win32.rs b/src/libnative/io/c_win32.rs index e855b8bd4f2..1b6525c6e38 100644 --- a/src/libnative/io/c_win32.rs +++ b/src/libnative/io/c_win32.rs @@ -77,15 +77,20 @@ pub mod compat { fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> LPVOID; } - // store_func() is idempotent, so using relaxed ordering for the atomics should be enough. - // This way, calling a function in this compatibility layer (after it's loaded) shouldn't - // be any slower than a regular DLL call. - unsafe fn store_func(ptr: *mut T, module: &str, symbol: &str, fallback: T) { + // store_func() is idempotent, so using relaxed ordering for the atomics + // should be enough. This way, calling a function in this compatibility + // layer (after it's loaded) shouldn't be any slower than a regular DLL + // call. + unsafe fn store_func(ptr: *mut uint, module: &str, symbol: &str, fallback: uint) { let module = module.to_utf16().append_one(0); symbol.with_c_str(|symbol| { let handle = GetModuleHandleW(module.as_ptr()); - let func: Option = transmute(GetProcAddress(handle, symbol)); - atomic_store_relaxed(ptr, func.unwrap_or(fallback)) + let func: uint = transmute(GetProcAddress(handle, symbol)); + atomic_store_relaxed(ptr, if func == 0 { + fallback + } else { + func + }) }) } @@ -109,10 +114,10 @@ pub mod compat { extern "system" fn thunk($($argname: $argtype),*) -> $rettype { unsafe { - ::io::c::compat::store_func(&mut ptr, - stringify!($module), - stringify!($symbol), - fallback); + ::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint, + stringify!($module), + stringify!($symbol), + fallback as uint); ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*) } } diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index 61f7997071e..76dbfa8c29e 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -158,6 +158,7 @@ mod test { use super::*; use prelude::*; use libc; + use mem; #[test] #[ignore(cfg(windows))] // FIXME #8818 @@ -174,7 +175,7 @@ mod test { let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe { match libm.symbol("cos") { Err(error) => fail!("Could not load function cos: {}", error), - Ok(cosine) => cosine + Ok(cosine) => mem::transmute::<*u8, _>(cosine) } }; diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 008be8ffaae..10dfec0f566 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -977,7 +977,9 @@ mod test { let result = File::open_mode(filename, Open, Read); error!(result, "couldn't open file"); - error!(result, "no such file or directory"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } error!(result, format!("path={}; mode=open; access=read", filename.display())); }) @@ -988,7 +990,9 @@ mod test { let result = unlink(filename); error!(result, "couldn't unlink path"); - error!(result, "no such file or directory"); + if cfg!(unix) { + error!(result, "no such file or directory"); + } error!(result, format!("path={}", filename.display())); }) diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index a1372b51d47..e2a963c5a87 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -873,12 +873,12 @@ mod imp { Err(..) => return Ok(()), }; - macro_rules! sym( ($e:expr, $t:ident) => ( - match unsafe { lib.symbol::<$t>($e) } { - Ok(f) => f, + macro_rules! sym( ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*u8, $t>(f), Err(..) => return Ok(()) } - ) ) + }) ) // Fetch the symbols necessary from dbghelp.dll let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); diff --git a/src/test/compile-fail/issue-14845.rs b/src/test/compile-fail/issue-14845.rs index d87eab7b6de..90366d09e2d 100644 --- a/src/test/compile-fail/issue-14845.rs +++ b/src/test/compile-fail/issue-14845.rs @@ -17,7 +17,7 @@ fn main() { let x = X { a: [0] }; let _f = &x.a as *mut u8; //~^ ERROR mismatched types: expected `*mut u8` but found `&[u8, .. 1]` - + let local = [0u8]; let _v = &local as *mut u8; //~^ ERROR mismatched types: expected `*mut u8` but found `&[u8, .. 1]` diff --git a/src/test/compile-fail/transmute-different-sizes.rs b/src/test/compile-fail/transmute-different-sizes.rs index 5ea1c496c76..abdfe983e3a 100644 --- a/src/test/compile-fail/transmute-different-sizes.rs +++ b/src/test/compile-fail/transmute-different-sizes.rs @@ -10,6 +10,8 @@ // Tests that `transmute` cannot be called on types of different size. +#![allow(warnings)] + use std::mem::transmute; unsafe fn f() { diff --git a/src/test/pretty/path-type-bounds.rs b/src/test/pretty/path-type-bounds.rs index 0fdbad67f16..f90937c34a6 100644 --- a/src/test/pretty/path-type-bounds.rs +++ b/src/test/pretty/path-type-bounds.rs @@ -14,11 +14,11 @@ trait Tr { } impl Tr for int { } -fn foo(x: Box) -> Box { x } +fn foo(x: Box) -> Box { x } fn main() { - let x: Box; + let x: Box; - box() 1 as Box; + box() 1 as Box; } diff --git a/src/test/run-pass-fulldeps/quote-tokens.rs b/src/test/run-pass-fulldeps/quote-tokens.rs index 96f5fca5a2d..4b2e29135ad 100644 --- a/src/test/run-pass-fulldeps/quote-tokens.rs +++ b/src/test/run-pass-fulldeps/quote-tokens.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-android +// ignore-pretty: does not work well with `--test` + #![feature(quote)] #![feature(managed_boxes)] diff --git a/src/test/run-pass/issue-14837.rs b/src/test/run-pass/issue-14837.rs index c207980cd6e..7876fe86d47 100644 --- a/src/test/run-pass/issue-14837.rs +++ b/src/test/run-pass/issue-14837.rs @@ -9,8 +9,8 @@ // except according to those terms. #![feature(struct_variant)] -#![deny(warnings)] +#[deny(dead_code)] pub enum Foo { Bar { pub baz: int -- cgit 1.4.1-3-g733a5