提交 e629dba0 编写于 作者: B bors

Auto merge of #28256 - petrochenkov:conv, r=alexcrichton

This patch transforms functions of the form
```
fn f<Generic: AsRef<Concrete>>(arg: Generic) {
	let arg: &Concrete = arg.as_ref();
	// Code using arg
}
```
to the next form:
```
#[inline]
fn f<Generic: AsRef<Concrete>>(arg: Generic) {
	fn f_inner(arg: &Concrete) {
		// Code using arg
	}
	
	f_inner(arg.as_ref());
}
```

Therefore, most of the code is concrete and not duplicated during monomorphisation (unless inlined)
and only the tiny bit of conversion code is duplicated. This method was mentioned by @aturon in the
Conversion Traits RFC (https://github.com/rust-lang/rfcs/blame/master/text/0529-conversion-traits.md#L249) and similar techniques are not uncommon in C++ template libraries.

This patch goes to the extremes and applies the transformation even to smaller functions<sup>1</sup>
for purity of the experiment. *Some of them can be rolled back* if considered too ridiculous.

<sup>1</sup> However who knows how small are these functions are after inlining and everything.

The functions in question are mostly `fs`/`os` functions and not used especially often with variety
of argument types, so the code size reduction is rather small (but consistent). Here are the sizes
of stage2 artifacts before and after the patch:
https://gist.github.com/petrochenkov/e76a6b280f382da13c5d
https://gist.github.com/petrochenkov/6cc28727d5256dbdfed0

Note:
All the `inner` functions are concrete and unavailable for cross-crate inlining, some of them may
need `#[inline]` annotations in the future.

r? @aturon
......@@ -38,8 +38,12 @@ impl TempDir {
#[allow(deprecated)] // rand usage
pub fn new_in<P: AsRef<Path>>(tmpdir: P, prefix: &str)
-> io::Result<TempDir> {
Self::_new_in(tmpdir.as_ref(), prefix)
}
fn _new_in(tmpdir: &Path, prefix: &str) -> io::Result<TempDir> {
let storage;
let mut tmpdir = tmpdir.as_ref();
let mut tmpdir = tmpdir;
if !tmpdir.is_absolute() {
let cur_dir = try!(env::current_dir());
storage = cur_dir.join(tmpdir);
......
......@@ -174,6 +174,10 @@ fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn var<K: AsRef<OsStr>>(key: K) -> Result<String, VarError> {
_var(key.as_ref())
}
fn _var(key: &OsStr) -> Result<String, VarError> {
match var_os(key) {
Some(s) => s.into_string().map_err(VarError::NotUnicode),
None => Err(VarError::NotPresent)
......@@ -196,8 +200,12 @@ pub fn var<K: AsRef<OsStr>>(key: K) -> Result<String, VarError> {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
_var_os(key.as_ref())
}
fn _var_os(key: &OsStr) -> Option<OsString> {
let _g = ENV_LOCK.lock();
os_imp::getenv(key.as_ref())
os_imp::getenv(key)
}
/// Possible errors from the `env::var` method.
......@@ -263,8 +271,12 @@ fn description(&self) -> &str {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
_set_var(k.as_ref(), v.as_ref())
}
fn _set_var(k: &OsStr, v: &OsStr) {
let _g = ENV_LOCK.lock();
os_imp::setenv(k.as_ref(), v.as_ref())
os_imp::setenv(k, v)
}
/// Removes an environment variable from the environment of the currently running process.
......@@ -294,8 +306,12 @@ pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn remove_var<K: AsRef<OsStr>>(k: K) {
_remove_var(k.as_ref())
}
fn _remove_var(k: &OsStr) {
let _g = ENV_LOCK.lock();
os_imp::unsetenv(k.as_ref())
os_imp::unsetenv(k)
}
/// An iterator over `Path` instances for parsing an environment variable
......
......@@ -181,7 +181,10 @@ impl CString {
/// the position of the nul byte.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> {
let bytes = t.into();
Self::_new(t.into())
}
fn _new(bytes: Vec<u8>) -> Result<CString, NulError> {
match bytes.iter().position(|x| *x == 0) {
Some(i) => Err(NulError(i, bytes)),
None => Ok(unsafe { CString::from_vec_unchecked(bytes) }),
......
......@@ -73,18 +73,18 @@ pub fn new() -> OsString {
/// convert; non UTF-8 data will produce `None`.
#[unstable(feature = "convert", reason = "recently added", issue = "27704")]
pub fn from_bytes<B>(bytes: B) -> Option<OsString> where B: Into<Vec<u8>> {
#[cfg(unix)]
fn from_bytes_inner(vec: Vec<u8>) -> Option<OsString> {
use os::unix::ffi::OsStringExt;
Some(OsString::from_vec(vec))
}
Self::_from_bytes(bytes.into())
}
#[cfg(windows)]
fn from_bytes_inner(vec: Vec<u8>) -> Option<OsString> {
String::from_utf8(vec).ok().map(OsString::from)
}
#[cfg(unix)]
fn _from_bytes(vec: Vec<u8>) -> Option<OsString> {
use os::unix::ffi::OsStringExt;
Some(OsString::from_vec(vec))
}
from_bytes_inner(bytes.into())
#[cfg(windows)]
fn _from_bytes(vec: Vec<u8>) -> Option<OsString> {
String::from_utf8(vec).ok().map(OsString::from)
}
/// Converts to an `OsStr` slice.
......
......@@ -184,7 +184,7 @@ impl File {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().read(true).open(path)
OpenOptions::new().read(true).open(path.as_ref())
}
/// Opens a file in write-only mode.
......@@ -206,7 +206,7 @@ pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
OpenOptions::new().write(true).create(true).truncate(true).open(path)
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}
/// Attempts to sync all OS-internal metadata to disk.
......@@ -494,7 +494,10 @@ pub fn create(&mut self, create: bool) -> &mut OpenOptions {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
let path = path.as_ref();
self._open(path.as_ref())
}
fn _open(&self, path: &Path) -> io::Result<File> {
let inner = try!(fs_imp::File::open(path, &self.0));
Ok(File { inner: inner })
}
......@@ -1048,7 +1051,10 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref();
_remove_dir_all(path.as_ref())
}
fn _remove_dir_all(path: &Path) -> io::Result<()> {
for child in try!(read_dir(path)) {
let child = try!(child).path();
let stat = try!(symlink_metadata(&*child));
......@@ -1113,6 +1119,10 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
as symlinks differently",
issue = "27707")]
pub fn walk_dir<P: AsRef<Path>>(path: P) -> io::Result<WalkDir> {
_walk_dir(path.as_ref())
}
fn _walk_dir(path: &Path) -> io::Result<WalkDir> {
let start = try!(read_dir(path));
Ok(WalkDir { cur: Some(start), stack: Vec::new() })
}
......@@ -1272,7 +1282,10 @@ pub fn recursive(&mut self, recursive: bool) -> &mut Self {
/// Create the specified directory with the options configured in this
/// builder.
pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
let path = path.as_ref();
self._create(path.as_ref())
}
fn _create(&self, path: &Path) -> io::Result<()> {
if self.recursive {
self.create_dir_all(path)
} else {
......
......@@ -191,10 +191,14 @@ impl Error {
pub fn new<E>(kind: ErrorKind, error: E) -> Error
where E: Into<Box<error::Error+Send+Sync>>
{
Self::_new(kind, error.into())
}
fn _new(kind: ErrorKind, error: Box<error::Error+Send+Sync>) -> Error {
Error {
repr: Repr::Custom(Box::new(Custom {
kind: kind,
error: error.into(),
error: error,
}))
}
}
......
......@@ -965,8 +965,10 @@ pub fn as_path(&self) -> &Path {
/// * if `path` has a prefix but no root, it replaces `self`.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn push<P: AsRef<Path>>(&mut self, path: P) {
let path = path.as_ref();
self._push(path.as_ref())
}
fn _push(&mut self, path: &Path) {
// in general, a separator is needed if the rightmost byte is not a separator
let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false);
......@@ -1033,11 +1035,15 @@ pub fn pop(&mut self) -> bool {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) {
self._set_file_name(file_name.as_ref())
}
fn _set_file_name(&mut self, file_name: &OsStr) {
if self.file_name().is_some() {
let popped = self.pop();
debug_assert!(popped);
}
self.push(file_name.as_ref());
self.push(file_name);
}
/// Updates `self.extension()` to `extension`.
......@@ -1048,6 +1054,10 @@ pub fn set_file_name<S: AsRef<OsStr>>(&mut self, file_name: S) {
/// is added; otherwise it is replaced.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
self._set_extension(extension.as_ref())
}
fn _set_extension(&mut self, extension: &OsStr) -> bool {
if self.file_name().is_none() { return false; }
let mut stem = match self.file_stem() {
......@@ -1055,7 +1065,6 @@ pub fn set_extension<S: AsRef<OsStr>>(&mut self, extension: S) -> bool {
None => OsString::new(),
};
let extension = extension.as_ref();
if !os_str_as_u8_slice(extension).is_empty() {
stem.push(".");
stem.push(extension);
......@@ -1106,7 +1115,7 @@ fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> PathBuf {
impl<P: AsRef<Path>> iter::Extend<P> for PathBuf {
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
for p in iter {
self.push(p)
self.push(p.as_ref())
}
}
}
......@@ -1452,7 +1461,11 @@ pub fn file_name(&self) -> Option<&OsStr> {
issue = "23284")]
pub fn relative_from<'a, P: ?Sized + AsRef<Path>>(&'a self, base: &'a P) -> Option<&Path>
{
iter_after(self.components(), base.as_ref().components()).map(|c| c.as_path())
self._relative_from(base.as_ref())
}
fn _relative_from<'a>(&'a self, base: &'a Path) -> Option<&'a Path> {
iter_after(self.components(), base.components()).map(|c| c.as_path())
}
/// Determines whether `base` is a prefix of `self`.
......@@ -1472,7 +1485,11 @@ pub fn relative_from<'a, P: ?Sized + AsRef<Path>>(&'a self, base: &'a P) -> Opti
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
iter_after(self.components(), base.as_ref().components()).is_some()
self._starts_with(base.as_ref())
}
fn _starts_with(&self, base: &Path) -> bool {
iter_after(self.components(), base.components()).is_some()
}
/// Determines whether `child` is a suffix of `self`.
......@@ -1490,7 +1507,11 @@ pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn ends_with<P: AsRef<Path>>(&self, child: P) -> bool {
iter_after(self.components().rev(), child.as_ref().components().rev()).is_some()
self._ends_with(child.as_ref())
}
fn _ends_with(&self, child: &Path) -> bool {
iter_after(self.components().rev(), child.components().rev()).is_some()
}
/// Extracts the stem (non-extension) portion of `self.file_name()`.
......@@ -1552,6 +1573,10 @@ pub fn extension(&self) -> Option<&OsStr> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
self._join(path.as_ref())
}
fn _join(&self, path: &Path) -> PathBuf {
let mut buf = self.to_path_buf();
buf.push(path);
buf
......@@ -1571,6 +1596,10 @@ pub fn join<P: AsRef<Path>>(&self, path: P) -> PathBuf {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> PathBuf {
self._with_file_name(file_name.as_ref())
}
fn _with_file_name(&self, file_name: &OsStr) -> PathBuf {
let mut buf = self.to_path_buf();
buf.set_file_name(file_name);
buf
......@@ -1590,6 +1619,10 @@ pub fn with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> PathBuf {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
self._with_extension(extension.as_ref())
}
fn _with_extension(&self, extension: &OsStr) -> PathBuf {
let mut buf = self.to_path_buf();
buf.set_extension(extension);
buf
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册