about summary refs log tree commit diff
path: root/src/libstd/sys/windows
diff options
context:
space:
mode:
authorKonrad Borowski <konrad@borowski.pw>2018-12-23 16:47:11 +0100
committerGitHub <noreply@github.com>2018-12-23 16:47:11 +0100
commit8ac5380ea0204dbdcbc8108d259928b67d5f8ebb (patch)
tree174d912756fc2678af50d46ff457f7504750a975 /src/libstd/sys/windows
parentb4a306c1e648c84f289c63e984941b7faad10af1 (diff)
parentddab10a692aab2e2984b5c826ed9d78a57e94851 (diff)
downloadrust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.tar.gz
rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.zip
Merge branch 'master' into copied
Diffstat (limited to 'src/libstd/sys/windows')
-rw-r--r--src/libstd/sys/windows/args.rs258
-rw-r--r--src/libstd/sys/windows/c.rs3
-rw-r--r--src/libstd/sys/windows/env.rs14
-rw-r--r--src/libstd/sys/windows/ext/ffi.rs2
-rw-r--r--src/libstd/sys/windows/fs.rs2
-rw-r--r--src/libstd/sys/windows/os.rs8
-rw-r--r--src/libstd/sys/windows/path.rs7
-rw-r--r--src/libstd/sys/windows/pipe.rs10
-rw-r--r--src/libstd/sys/windows/process.rs2
-rw-r--r--src/libstd/sys/windows/stdio.rs4
-rw-r--r--src/libstd/sys/windows/thread.rs4
-rw-r--r--src/libstd/sys/windows/time.rs59
12 files changed, 257 insertions, 116 deletions
diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs
index 4784633edc1..9e9198e05ee 100644
--- a/src/libstd/sys/windows/args.rs
+++ b/src/libstd/sys/windows/args.rs
@@ -11,12 +11,14 @@
 #![allow(dead_code)] // runtime init functions not used during testing
 
 use os::windows::prelude::*;
+use sys::windows::os::current_exe;
 use sys::c;
-use slice;
-use ops::Range;
 use ffi::OsString;
-use libc::{c_int, c_void};
 use fmt;
+use vec;
+use core::iter;
+use slice;
+use path::PathBuf;
 
 pub unsafe fn init(_argc: isize, _argv: *const *const u8) { }
 
@@ -24,20 +26,146 @@ pub unsafe fn cleanup() { }
 
 pub fn args() -> Args {
     unsafe {
-        let mut nArgs: c_int = 0;
-        let lpCmdLine = c::GetCommandLineW();
-        let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
-
-        // szArcList can be NULL if CommandLinToArgvW failed,
-        // but in that case nArgs is 0 so we won't actually
-        // try to read a null pointer
-        Args { cur: szArgList, range: 0..(nArgs as isize) }
+        let lp_cmd_line = c::GetCommandLineW();
+        let parsed_args_list = parse_lp_cmd_line(
+            lp_cmd_line as *const u16,
+            || current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new()));
+
+        Args { parsed_args_list: parsed_args_list.into_iter() }
     }
 }
 
+/// Implements the Windows command-line argument parsing algorithm.
+///
+/// Microsoft's documentation for the Windows CLI argument format can be found at
+/// <https://docs.microsoft.com/en-us/previous-versions//17w5ykft(v=vs.85)>.
+///
+/// Windows includes a function to do this in shell32.dll,
+/// but linking with that DLL causes the process to be registered as a GUI application.
+/// GUI applications add a bunch of overhead, even if no windows are drawn. See
+/// <https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/>.
+///
+/// This function was tested for equivalence to the shell32.dll implementation in
+/// Windows 10 Pro v1803, using an exhaustive test suite available at
+/// <https://gist.github.com/notriddle/dde431930c392e428055b2dc22e638f5> or
+/// <https://paste.gg/p/anonymous/47d6ed5f5bd549168b1c69c799825223>.
+unsafe fn parse_lp_cmd_line<F: Fn() -> OsString>(lp_cmd_line: *const u16, exe_name: F)
+                                                 -> Vec<OsString> {
+    const BACKSLASH: u16 = '\\' as u16;
+    const QUOTE: u16 = '"' as u16;
+    const TAB: u16 = '\t' as u16;
+    const SPACE: u16 = ' ' as u16;
+    let mut ret_val = Vec::new();
+    if lp_cmd_line.is_null() || *lp_cmd_line == 0 {
+        ret_val.push(exe_name());
+        return ret_val;
+    }
+    let mut cmd_line = {
+        let mut end = 0;
+        while *lp_cmd_line.offset(end) != 0 {
+            end += 1;
+        }
+        slice::from_raw_parts(lp_cmd_line, end as usize)
+    };
+    // The executable name at the beginning is special.
+    cmd_line = match cmd_line[0] {
+        // The executable name ends at the next quote mark,
+        // no matter what.
+        QUOTE => {
+            let args = {
+                let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE);
+                if let Some(exe) = cut.next() {
+                    ret_val.push(OsString::from_wide(exe));
+                }
+                cut.next()
+            };
+            if let Some(args) = args {
+                args
+            } else {
+                return ret_val;
+            }
+        }
+        // Implement quirk: when they say whitespace here,
+        // they include the entire ASCII control plane:
+        // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW
+        // will consider the first argument to be an empty string. Excess whitespace at the
+        // end of lpCmdLine is ignored."
+        0...SPACE => {
+            ret_val.push(OsString::new());
+            &cmd_line[1..]
+        },
+        // The executable name ends at the next whitespace,
+        // no matter what.
+        _ => {
+            let args = {
+                let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE);
+                if let Some(exe) = cut.next() {
+                    ret_val.push(OsString::from_wide(exe));
+                }
+                cut.next()
+            };
+            if let Some(args) = args {
+                args
+            } else {
+                return ret_val;
+            }
+        }
+    };
+    let mut cur = Vec::new();
+    let mut in_quotes = false;
+    let mut was_in_quotes = false;
+    let mut backslash_count: usize = 0;
+    for &c in cmd_line {
+        match c {
+            // backslash
+            BACKSLASH => {
+                backslash_count += 1;
+                was_in_quotes = false;
+            },
+            QUOTE if backslash_count % 2 == 0 => {
+                cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2));
+                backslash_count = 0;
+                if was_in_quotes {
+                    cur.push('"' as u16);
+                    was_in_quotes = false;
+                } else {
+                    was_in_quotes = in_quotes;
+                    in_quotes = !in_quotes;
+                }
+            }
+            QUOTE if backslash_count % 2 != 0 => {
+                cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2));
+                backslash_count = 0;
+                was_in_quotes = false;
+                cur.push(b'"' as u16);
+            }
+            SPACE | TAB if !in_quotes => {
+                cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
+                if !cur.is_empty() || was_in_quotes {
+                    ret_val.push(OsString::from_wide(&cur[..]));
+                    cur.truncate(0);
+                }
+                backslash_count = 0;
+                was_in_quotes = false;
+            }
+            _ => {
+                cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
+                backslash_count = 0;
+                was_in_quotes = false;
+                cur.push(c);
+            }
+        }
+    }
+    cur.extend(iter::repeat(b'\\' as u16).take(backslash_count));
+    // include empty quoted strings at the end of the arguments list
+    if !cur.is_empty() || was_in_quotes || in_quotes {
+        ret_val.push(OsString::from_wide(&cur[..]));
+    }
+    ret_val
+}
+
 pub struct Args {
-    range: Range<isize>,
-    cur: *mut *mut u16,
+    parsed_args_list: vec::IntoIter<OsString>,
 }
 
 pub struct ArgsInnerDebug<'a> {
@@ -46,19 +174,7 @@ pub struct ArgsInnerDebug<'a> {
 
 impl<'a> fmt::Debug for ArgsInnerDebug<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.write_str("[")?;
-        let mut first = true;
-        for i in self.args.range.clone() {
-            if !first {
-                f.write_str(", ")?;
-            }
-            first = false;
-
-            // Here we do allocation which could be avoided.
-            fmt::Debug::fmt(&unsafe { os_string_from_ptr(*self.args.cur.offset(i)) }, f)?;
-        }
-        f.write_str("]")?;
-        Ok(())
+        self.args.parsed_args_list.as_slice().fmt(f)
     }
 }
 
@@ -70,38 +186,82 @@ impl Args {
     }
 }
 
-unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString {
-    let mut len = 0;
-    while *ptr.offset(len) != 0 { len += 1; }
-
-    // Push it onto the list.
-    let ptr = ptr as *const u16;
-    let buf = slice::from_raw_parts(ptr, len as usize);
-    OsStringExt::from_wide(buf)
-}
-
 impl Iterator for Args {
     type Item = OsString;
-    fn next(&mut self) -> Option<OsString> {
-        self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
-    }
-    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
+    fn next(&mut self) -> Option<OsString> { self.parsed_args_list.next() }
+    fn size_hint(&self) -> (usize, Option<usize>) { self.parsed_args_list.size_hint() }
 }
 
 impl DoubleEndedIterator for Args {
-    fn next_back(&mut self) -> Option<OsString> {
-        self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
-    }
+    fn next_back(&mut self) -> Option<OsString> { self.parsed_args_list.next_back() }
 }
 
 impl ExactSizeIterator for Args {
-    fn len(&self) -> usize { self.range.len() }
+    fn len(&self) -> usize { self.parsed_args_list.len() }
 }
 
-impl Drop for Args {
-    fn drop(&mut self) {
-        // self.cur can be null if CommandLineToArgvW previously failed,
-        // but LocalFree ignores NULL pointers
-        unsafe { c::LocalFree(self.cur as *mut c_void); }
+#[cfg(test)]
+mod tests {
+    use sys::windows::args::*;
+    use ffi::OsString;
+
+    fn chk(string: &str, parts: &[&str]) {
+        let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect();
+        wide.push(0);
+        let parsed = unsafe {
+            parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE"))
+        };
+        let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect();
+        assert_eq!(parsed.as_slice(), expected.as_slice());
+    }
+
+    #[test]
+    fn empty() {
+        chk("", &["TEST.EXE"]);
+        chk("\0", &["TEST.EXE"]);
+    }
+
+    #[test]
+    fn single_words() {
+        chk("EXE one_word", &["EXE", "one_word"]);
+        chk("EXE a", &["EXE", "a"]);
+        chk("EXE 😅", &["EXE", "😅"]);
+        chk("EXE 😅🤦", &["EXE", "😅🤦"]);
+    }
+
+    #[test]
+    fn official_examples() {
+        chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]);
+        chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]);
+        chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]);
+        chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]);
+    }
+
+    #[test]
+    fn whitespace_behavior() {
+        chk(r#" test"#, &["", "test"]);
+        chk(r#"  test"#, &["", "test"]);
+        chk(r#" test test2"#, &["", "test", "test2"]);
+        chk(r#" test  test2"#, &["", "test", "test2"]);
+        chk(r#"test test2 "#, &["test", "test2"]);
+        chk(r#"test  test2 "#, &["test", "test2"]);
+        chk(r#"test "#, &["test"]);
+    }
+
+    #[test]
+    fn genius_quotes() {
+        chk(r#"EXE "" """#, &["EXE", "", ""]);
+        chk(r#"EXE "" """"#, &["EXE", "", "\""]);
+        chk(
+            r#"EXE "this is """all""" in the same argument""#,
+            &["EXE", "this is \"all\" in the same argument"]
+        );
+        chk(r#"EXE "a"""#, &["EXE", "a\""]);
+        chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]);
+        // quotes cannot be escaped in command names
+        chk(r#""EXE" check"#, &["EXE", "check"]);
+        chk(r#""EXE check""#, &["EXE check"]);
+        chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]);
+        chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#,  "check"]);
     }
 }
diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs
index c84874a3e88..fa21f459a8a 100644
--- a/src/libstd/sys/windows/c.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -1035,9 +1035,6 @@ extern "system" {
 
     pub fn SetLastError(dwErrCode: DWORD);
     pub fn GetCommandLineW() -> *mut LPCWSTR;
-    pub fn LocalFree(ptr: *mut c_void);
-    pub fn CommandLineToArgvW(lpCmdLine: *mut LPCWSTR,
-                              pNumArgs: *mut c_int) -> *mut *mut u16;
     pub fn GetTempPathW(nBufferLength: DWORD,
                         lpBuffer: LPCWSTR) -> DWORD;
     pub fn OpenProcessToken(ProcessHandle: HANDLE,
diff --git a/src/libstd/sys/windows/env.rs b/src/libstd/sys/windows/env.rs
index e6d74895774..4523df04f24 100644
--- a/src/libstd/sys/windows/env.rs
+++ b/src/libstd/sys/windows/env.rs
@@ -9,11 +9,11 @@
 // except according to those terms.
 
 pub mod os {
-    pub const FAMILY: &'static str = "windows";
-    pub const OS: &'static str = "windows";
-    pub const DLL_PREFIX: &'static str = "";
-    pub const DLL_SUFFIX: &'static str = ".dll";
-    pub const DLL_EXTENSION: &'static str = "dll";
-    pub const EXE_SUFFIX: &'static str = ".exe";
-    pub const EXE_EXTENSION: &'static str = "exe";
+    pub const FAMILY: &str = "windows";
+    pub const OS: &str = "windows";
+    pub const DLL_PREFIX: &str = "";
+    pub const DLL_SUFFIX: &str = ".dll";
+    pub const DLL_EXTENSION: &str = "dll";
+    pub const EXE_SUFFIX: &str = ".exe";
+    pub const EXE_EXTENSION: &str = "exe";
 }
diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs
index bae0d02786a..0a13aeabe84 100644
--- a/src/libstd/sys/windows/ext/ffi.rs
+++ b/src/libstd/sys/windows/ext/ffi.rs
@@ -117,7 +117,7 @@ impl OsStringExt for OsString {
 /// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait OsStrExt {
-    /// Re-encodes an `OsStr` as a wide character sequence, i.e. potentially
+    /// Re-encodes an `OsStr` as a wide character sequence, i.e., potentially
     /// ill-formed UTF-16.
     ///
     /// This is lossless: calling [`OsString::from_wide`] and then
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 082d4689c7b..949060b34dd 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -443,7 +443,7 @@ impl FromInner<c::HANDLE> for File {
 
 impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        // FIXME(#24570): add more info here (e.g. mode)
+        // FIXME(#24570): add more info here (e.g., mode)
         let mut b = f.debug_struct("File");
         b.field("handle", &self.handle.raw());
         if let Ok(path) = get_path(&self) {
diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs
index 29ea82c2053..84ef62e5fe9 100644
--- a/src/libstd/sys/windows/os.rs
+++ b/src/libstd/sys/windows/os.rs
@@ -48,8 +48,8 @@ pub fn error_string(mut errnum: i32) -> String {
         // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx
         if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
             // format according to https://support.microsoft.com/en-us/help/259693
-            const NTDLL_DLL: &'static [u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _,
-                                                '.' as _, 'D' as _, 'L' as _, 'L' as _, 0];
+            const NTDLL_DLL: &[u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _,
+                                        '.' as _, 'D' as _, 'L' as _, 'L' as _, 0];
             module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
 
             if module != ptr::null_mut() {
@@ -67,7 +67,7 @@ pub fn error_string(mut errnum: i32) -> String {
                                     buf.len() as c::DWORD,
                                     ptr::null()) as usize;
         if res == 0 {
-            // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
+            // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
             let fm_err = errno();
             return format!("OS Error {} (FormatMessageW() returned error {})",
                            errnum, fm_err);
@@ -76,7 +76,7 @@ pub fn error_string(mut errnum: i32) -> String {
         match String::from_utf16(&buf[..res]) {
             Ok(mut msg) => {
                 // Trim trailing CRLF inserted by FormatMessageW
-                let len = msg.trim_right().len();
+                let len = msg.trim_end().len();
                 msg.truncate(len);
                 msg
             },
diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs
index 98d62a0c953..385ea8e0531 100644
--- a/src/libstd/sys/windows/path.rs
+++ b/src/libstd/sys/windows/path.rs
@@ -91,10 +91,7 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> {
     }
 
     fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> {
-        let first = match path.iter().position(|x| f(*x)) {
-            None => return None,
-            Some(x) => &path[..x],
-        };
+        let first = &path[..path.iter().position(|x| f(*x))?];
         path = &path[(first.len() + 1)..];
         let idx = path.iter().position(|x| f(*x));
         let second = &path[..idx.unwrap_or(path.len())];
@@ -102,5 +99,5 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> {
     }
 }
 
-pub const MAIN_SEP_STR: &'static str = "\\";
+pub const MAIN_SEP_STR: &str = "\\";
 pub const MAIN_SEP: char = '\\';
diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs
index 4b19519a57a..f9eed31f0e0 100644
--- a/src/libstd/sys/windows/pipe.rs
+++ b/src/libstd/sys/windows/pipe.rs
@@ -100,23 +100,23 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> {
                                              0,
                                              ptr::null_mut());
 
-            // We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're
+            // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're
             // also just doing a best effort at selecting a unique name. If
-            // ERROR_ACCESS_DENIED is returned then it could mean that we
+            // `ERROR_ACCESS_DENIED` is returned then it could mean that we
             // accidentally conflicted with an already existing pipe, so we try
             // again.
             //
             // Don't try again too much though as this could also perhaps be a
             // legit error.
-            // If ERROR_INVALID_PARAMETER is returned, this probably means we're
-            // running on pre-Vista version where PIPE_REJECT_REMOTE_CLIENTS is
+            // If `ERROR_INVALID_PARAMETER` is returned, this probably means we're
+            // running on pre-Vista version where `PIPE_REJECT_REMOTE_CLIENTS` is
             // not supported, so we continue retrying without it. This implies
             // reduced security on Windows versions older than Vista by allowing
             // connections to this pipe from remote machines.
             // Proper fix would increase the number of FFI imports and introduce
             // significant amount of Windows XP specific code with no clean
             // testing strategy
-            // for more info see https://github.com/rust-lang/rust/pull/37677
+            // For more info, see https://github.com/rust-lang/rust/pull/37677.
             if handle == c::INVALID_HANDLE_VALUE {
                 let err = io::Error::last_os_error();
                 let raw_os_err = err.raw_os_error();
diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs
index ff1ee0d26fe..03c1bb54af8 100644
--- a/src/libstd/sys/windows/process.rs
+++ b/src/libstd/sys/windows/process.rs
@@ -487,7 +487,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
             } else {
                 if x == '"' as u16 {
                     // Add n+1 backslashes to total 2n+1 before internal '"'.
-                    cmd.extend((0..(backslashes + 1)).map(|_| '\\' as u16));
+                    cmd.extend((0..=backslashes).map(|_| '\\' as u16));
                 }
                 backslashes = 0;
             }
diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs
index c3a94698a0f..61e0db87ebe 100644
--- a/src/libstd/sys/windows/stdio.rs
+++ b/src/libstd/sys/windows/stdio.rs
@@ -228,6 +228,6 @@ pub fn is_ebadf(err: &io::Error) -> bool {
 // been seen to be acceptable.
 pub const STDIN_BUF_SIZE: usize = 8 * 1024;
 
-pub fn stderr_prints_nothing() -> bool {
-    false
+pub fn panic_output() -> Option<impl io::Write> {
+    Stderr::new().ok()
 }
diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs
index 85588cc6c8e..621ae2fda58 100644
--- a/src/libstd/sys/windows/thread.rs
+++ b/src/libstd/sys/windows/thread.rs
@@ -28,7 +28,8 @@ pub struct Thread {
 }
 
 impl Thread {
-    pub unsafe fn new<'a>(stack: usize, p: Box<dyn FnBox() + 'a>)
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>)
                           -> io::Result<Thread> {
         let p = box p;
 
@@ -97,5 +98,4 @@ pub mod guard {
     pub type Guard = !;
     pub unsafe fn current() -> Option<Guard> { None }
     pub unsafe fn init() -> Option<Guard> { None }
-    pub unsafe fn deinit() {}
 }
diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs
index c809a0b98ac..bb2c97ea149 100644
--- a/src/libstd/sys/windows/time.rs
+++ b/src/libstd/sys/windows/time.rs
@@ -68,30 +68,27 @@ impl Instant {
         Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32)
     }
 
-    pub fn add_duration(&self, other: &Duration) -> Instant {
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
         let freq = frequency() as u64;
-        let t = other.as_secs().checked_mul(freq).and_then(|i| {
-            (self.t as u64).checked_add(i)
-        }).and_then(|i| {
-            i.checked_add(mul_div_u64(other.subsec_nanos() as u64, freq,
-                                      NANOS_PER_SEC))
-        }).expect("overflow when adding duration to time");
-        Instant {
+        let t = other.as_secs()
+            .checked_mul(freq)?
+            .checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC))?
+            .checked_add(self.t as u64)?;
+        Some(Instant {
             t: t as c::LARGE_INTEGER,
-        }
+        })
     }
 
-    pub fn sub_duration(&self, other: &Duration) -> Instant {
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
         let freq = frequency() as u64;
         let t = other.as_secs().checked_mul(freq).and_then(|i| {
             (self.t as u64).checked_sub(i)
         }).and_then(|i| {
-            i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq,
-                                      NANOS_PER_SEC))
-        }).expect("overflow when subtracting duration from time");
-        Instant {
+            i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC))
+        })?;
+        Some(Instant {
             t: t as c::LARGE_INTEGER,
-        }
+        })
     }
 }
 
@@ -127,20 +124,14 @@ impl SystemTime {
         }
     }
 
-    pub fn add_duration(&self, other: &Duration) -> SystemTime {
-        self.checked_add_duration(other).expect("overflow when adding duration to time")
-    }
-
     pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
-        checked_dur2intervals(other)
-            .and_then(|d| self.intervals().checked_add(d))
-            .map(|i| SystemTime::from_intervals(i))
+        let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
+        Some(SystemTime::from_intervals(intervals))
     }
 
-    pub fn sub_duration(&self, other: &Duration) -> SystemTime {
-        let intervals = self.intervals().checked_sub(dur2intervals(other))
-                            .expect("overflow when subtracting from time");
-        SystemTime::from_intervals(intervals)
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?;
+        Some(SystemTime::from_intervals(intervals))
     }
 }
 
@@ -184,16 +175,12 @@ impl Hash for SystemTime {
     }
 }
 
-fn checked_dur2intervals(d: &Duration) -> Option<i64> {
-    d.as_secs()
-        .checked_mul(INTERVALS_PER_SEC)
-        .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100))
-        .and_then(|i| i.try_into().ok())
-}
-
-fn dur2intervals(d: &Duration) -> i64 {
-    checked_dur2intervals(d)
-        .expect("overflow when converting duration to intervals")
+fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
+    dur.as_secs()
+        .checked_mul(INTERVALS_PER_SEC)?
+        .checked_add(dur.subsec_nanos() as u64 / 100)?
+        .try_into()
+        .ok()
 }
 
 fn intervals2dur(intervals: u64) -> Duration {