about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-04-27 22:49:05 +0000
committerbors <bors@rust-lang.org>2020-04-27 22:49:05 +0000
commitc354509343b0a3a9b935cc18b2d4aa07da8d4261 (patch)
tree0e1d9d4856c49bdd319b2a3b5c31e334da7940c4 /src
parent2d03399f53d28a8be645625376c0c9fbe601a01d (diff)
parentc890912da1742b5aae42c56e85758335de3af850 (diff)
downloadrust-c354509343b0a3a9b935cc18b2d4aa07da8d4261.tar.gz
rust-c354509343b0a3a9b935cc18b2d4aa07da8d4261.zip
Auto merge of #71620 - Dylan-DPC:rollup-9wgtisb, r=Dylan-DPC
Rollup of 7 pull requests

Successful merges:

 - #67841 (Add Read/Write::can_read/write_vectored)
 - #71524 (Minimize parameter of coerce_borrowed_pointer())
 - #71558 (Cleanup and document `-Z tls-model` )
 - #71578 (linkchecker: fix typo in main.rs)
 - #71596 (Fix broken link in `QPath` documentation)
 - #71604 (make recursive-zst test unleashed)
 - #71605 (No need to whitelist E0750 anymore)

Failed merges:

r? @ghost
Diffstat (limited to 'src')
-rw-r--r--src/doc/unstable-book/src/compiler-flags/tls-model.md25
-rw-r--r--src/librustc_codegen_llvm/back/write.rs7
-rw-r--r--src/librustc_codegen_llvm/context.rs23
-rw-r--r--src/librustc_codegen_llvm/lib.rs2
-rw-r--r--src/librustc_hir/hir.rs2
-rw-r--r--src/librustc_interface/tests.rs5
-rw-r--r--src/librustc_session/config.rs8
-rw-r--r--src/librustc_session/options.rs15
-rw-r--r--src/librustc_session/session.rs6
-rw-r--r--src/librustc_target/spec/cloudabi_base.rs4
-rw-r--r--src/librustc_target/spec/hermit_base.rs5
-rw-r--r--src/librustc_target/spec/hermit_kernel_base.rs5
-rw-r--r--src/librustc_target/spec/mod.rs54
-rw-r--r--src/librustc_target/spec/wasm32_base.rs4
-rw-r--r--src/librustc_typeck/check/coercion.rs21
-rw-r--r--src/libstd/fs.rs20
-rw-r--r--src/libstd/io/buffered.rs8
-rw-r--r--src/libstd/io/cursor.rs24
-rw-r--r--src/libstd/io/impls.rs35
-rw-r--r--src/libstd/io/mod.rs27
-rw-r--r--src/libstd/io/stdio.rs56
-rw-r--r--src/libstd/io/util.rs10
-rw-r--r--src/libstd/lib.rs1
-rw-r--r--src/libstd/net/tcp.rs20
-rw-r--r--src/libstd/process.rs14
-rw-r--r--src/libstd/sys/cloudabi/shims/fs.rs8
-rw-r--r--src/libstd/sys/cloudabi/shims/net.rs8
-rw-r--r--src/libstd/sys/cloudabi/shims/pipe.rs8
-rw-r--r--src/libstd/sys/hermit/fs.rs10
-rw-r--r--src/libstd/sys/hermit/net.rs10
-rw-r--r--src/libstd/sys/hermit/pipe.rs8
-rw-r--r--src/libstd/sys/hermit/stdio.rs15
-rw-r--r--src/libstd/sys/sgx/fd.rs10
-rw-r--r--src/libstd/sys/sgx/fs.rs8
-rw-r--r--src/libstd/sys/sgx/net.rs10
-rw-r--r--src/libstd/sys/sgx/pipe.rs8
-rw-r--r--src/libstd/sys/unix/ext/net.rs20
-rw-r--r--src/libstd/sys/unix/fd.rs10
-rw-r--r--src/libstd/sys/unix/fs.rs10
-rw-r--r--src/libstd/sys/unix/l4re.rs16
-rw-r--r--src/libstd/sys/unix/net.rs10
-rw-r--r--src/libstd/sys/unix/pipe.rs10
-rw-r--r--src/libstd/sys/unix/stdio.rs15
-rw-r--r--src/libstd/sys/vxworks/fd.rs10
-rw-r--r--src/libstd/sys/vxworks/fs.rs10
-rw-r--r--src/libstd/sys/vxworks/net.rs10
-rw-r--r--src/libstd/sys/vxworks/pipe.rs11
-rw-r--r--src/libstd/sys/wasi/fs.rs10
-rw-r--r--src/libstd/sys/wasi/net.rs8
-rw-r--r--src/libstd/sys/wasi/pipe.rs8
-rw-r--r--src/libstd/sys/wasi/stdio.rs15
-rw-r--r--src/libstd/sys/wasm/fs.rs8
-rw-r--r--src/libstd/sys/wasm/net.rs8
-rw-r--r--src/libstd/sys/wasm/pipe.rs8
-rw-r--r--src/libstd/sys/windows/fs.rs10
-rw-r--r--src/libstd/sys/windows/handle.rs10
-rw-r--r--src/libstd/sys/windows/net.rs10
-rw-r--r--src/libstd/sys/windows/pipe.rs10
-rw-r--r--src/libstd/sys_common/net.rs10
-rw-r--r--src/test/ui/consts/recursive-zst-static.default.stderr (renamed from src/test/ui/consts/recursive-zst-static.stderr)6
-rw-r--r--src/test/ui/consts/recursive-zst-static.rs3
-rw-r--r--src/test/ui/consts/recursive-zst-static.unleash.stderr21
-rw-r--r--src/tools/linkchecker/main.rs2
-rw-r--r--src/tools/tidy/src/error_codes_check.rs2
64 files changed, 723 insertions, 62 deletions
diff --git a/src/doc/unstable-book/src/compiler-flags/tls-model.md b/src/doc/unstable-book/src/compiler-flags/tls-model.md
new file mode 100644
index 00000000000..0aefaa7fb01
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/tls-model.md
@@ -0,0 +1,25 @@
+# `tls_model`
+
+The tracking issue for this feature is: None.
+
+------------------------
+
+Option `-Z tls-model` controls [TLS model](https://www.akkadia.org/drepper/tls.pdf) used to
+generate code for accessing `#[thread_local]` `static` items.
+
+Supported values for this option are:
+
+- `global-dynamic` - General Dynamic TLS Model (alternatively called Global Dynamic) is the most
+general option usable in all circumstances, even if the TLS data is defined in a shared library
+loaded at runtime and is accessed from code outside of that library.  
+This is the default for most targets.
+- `local-dynamic` - model usable if the TLS data is only accessed from the shared library or
+executable it is defined in. The TLS data may be in a library loaded after startup (via `dlopen`).
+- `initial-exec` - model usable if the TLS data is defined in the executable or in a shared library
+loaded at program startup.
+The TLS data must not be in a library loaded after startup (via `dlopen`).
+- `local-exec` - model usable only if the TLS data is defined directly in the executable,
+but not in a shared library, and is accessed only from that executable.
+
+`rustc` and LLVM may use a more optimized model than specified if they know that we are producing
+and executable rather than a library, or that the `static` item is private enough.
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index bf79c5b593e..3ec7ef831b5 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -43,13 +43,6 @@ pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[
     ("large", llvm::CodeModel::Large),
 ];
 
-pub const TLS_MODEL_ARGS: [(&str, llvm::ThreadLocalMode); 4] = [
-    ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic),
-    ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic),
-    ("initial-exec", llvm::ThreadLocalMode::InitialExec),
-    ("local-exec", llvm::ThreadLocalMode::LocalExec),
-];
-
 pub fn llvm_err(handler: &rustc_errors::Handler, msg: &str) -> FatalError {
     match llvm::last_error() {
         Some(err) => handler.fatal(&format!("{}: {}", msg, err)),
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index df442609052..f868385ee86 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -21,7 +21,7 @@ use rustc_session::Session;
 use rustc_span::source_map::{Span, DUMMY_SP};
 use rustc_span::symbol::Symbol;
 use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx};
-use rustc_target::spec::{HasTargetSpec, RelocModel, Target};
+use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel};
 
 use std::cell::{Cell, RefCell};
 use std::ffi::CStr;
@@ -87,19 +87,12 @@ pub struct CodegenCx<'ll, 'tcx> {
     local_gen_sym_counter: Cell<usize>,
 }
 
-fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode {
-    let tls_model_arg = match sess.opts.debugging_opts.tls_model {
-        Some(ref s) => &s[..],
-        None => &sess.target.target.options.tls_model[..],
-    };
-
-    match crate::back::write::TLS_MODEL_ARGS.iter().find(|&&arg| arg.0 == tls_model_arg) {
-        Some(x) => x.1,
-        _ => {
-            sess.err(&format!("{:?} is not a valid TLS model", tls_model_arg));
-            sess.abort_if_errors();
-            bug!();
-        }
+fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
+    match tls_model {
+        TlsModel::GeneralDynamic => llvm::ThreadLocalMode::GeneralDynamic,
+        TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic,
+        TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec,
+        TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec,
     }
 }
 
@@ -267,7 +260,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
 
         let check_overflow = tcx.sess.overflow_checks();
 
-        let tls_model = get_tls_model(&tcx.sess);
+        let tls_model = to_llvm_tls_model(tcx.sess.tls_model());
 
         let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod());
 
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index 330d6ea75d2..42302a56b41 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -216,7 +216,7 @@ impl CodegenBackend for LlvmCodegenBackend {
             }
             PrintRequest::TlsModels => {
                 println!("Available TLS models:");
-                for &(name, _) in back::write::TLS_MODEL_ARGS.iter() {
+                for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] {
                     println!("    {}", name);
                 }
                 println!();
diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs
index d342f8b0ad2..654cd4980a4 100644
--- a/src/librustc_hir/hir.rs
+++ b/src/librustc_hir/hir.rs
@@ -1601,7 +1601,7 @@ pub enum ExprKind<'hir> {
 ///
 /// To resolve the path to a `DefId`, call [`qpath_res`].
 ///
-/// [`qpath_res`]: ../ty/struct.TypeckTables.html#method.qpath_res
+/// [`qpath_res`]: ../rustc_middle/ty/struct.TypeckTables.html#method.qpath_res
 #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
 pub enum QPath<'hir> {
     /// Path to a definition, optionally "fully-qualified" with a `Self`
diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs
index f0e7581b760..cee2e5b5bec 100644
--- a/src/librustc_interface/tests.rs
+++ b/src/librustc_interface/tests.rs
@@ -14,7 +14,8 @@ use rustc_session::{build_session, Session};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
 use rustc_span::SourceFileHashAlgorithm;
-use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelocModel, RelroLevel};
+use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy};
+use rustc_target::spec::{RelocModel, RelroLevel, TlsModel};
 use std::collections::{BTreeMap, BTreeSet};
 use std::iter::FromIterator;
 use std::path::PathBuf;
@@ -567,7 +568,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(symbol_mangling_version, SymbolManglingVersion::V0);
     tracked!(teach, true);
     tracked!(thinlto, Some(true));
-    tracked!(tls_model, Some(String::from("tls model")));
+    tracked!(tls_model, Some(TlsModel::GeneralDynamic));
     tracked!(treat_err_as_bug, Some(1));
     tracked!(unleash_the_miri_inside_of_you, true);
     tracked!(verify_llvm_ir, true);
diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs
index 1ab02f84c11..0dfc391d9cd 100644
--- a/src/librustc_session/config.rs
+++ b/src/librustc_session/config.rs
@@ -1315,10 +1315,6 @@ fn collect_print_requests(
         prints.push(PrintRequest::CodeModels);
         cg.code_model = None;
     }
-    if dopts.tls_model.as_ref().map_or(false, |s| s == "help") {
-        prints.push(PrintRequest::TlsModels);
-        dopts.tls_model = None;
-    }
 
     prints.extend(matches.opt_strs("print").into_iter().map(|s| match &*s {
         "crate-name" => PrintRequest::CrateName,
@@ -2001,7 +1997,8 @@ crate mod dep_tracking {
     use crate::utils::NativeLibraryKind;
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
-    use rustc_target::spec::{MergeFunctions, PanicStrategy, RelocModel, RelroLevel, TargetTriple};
+    use rustc_target::spec::{MergeFunctions, PanicStrategy, RelocModel};
+    use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel};
     use std::collections::hash_map::DefaultHasher;
     use std::collections::BTreeMap;
     use std::hash::Hash;
@@ -2050,6 +2047,7 @@ crate mod dep_tracking {
     impl_dep_tracking_hash_via_hash!(Option<Vec<String>>);
     impl_dep_tracking_hash_via_hash!(Option<MergeFunctions>);
     impl_dep_tracking_hash_via_hash!(Option<RelocModel>);
+    impl_dep_tracking_hash_via_hash!(Option<TlsModel>);
     impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
     impl_dep_tracking_hash_via_hash!(Option<RelroLevel>);
     impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs
index 2279c16748c..5b983d1105d 100644
--- a/src/librustc_session/options.rs
+++ b/src/librustc_session/options.rs
@@ -6,7 +6,8 @@ use crate::search_paths::SearchPath;
 use crate::utils::NativeLibraryKind;
 
 use rustc_target::spec::TargetTriple;
-use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelocModel, RelroLevel};
+use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy};
+use rustc_target::spec::{RelocModel, RelroLevel, TlsModel};
 
 use rustc_feature::UnstableFeatures;
 use rustc_span::edition::Edition;
@@ -267,6 +268,8 @@ macro_rules! options {
         pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
         pub const parse_relocation_model: &str =
             "one of supported relocation models (`rustc --print relocation-models`)";
+        pub const parse_tls_model: &str =
+            "one of supported TLS models (`rustc --print tls-models`)";
     }
 
     #[allow(dead_code)]
@@ -606,6 +609,14 @@ macro_rules! options {
             true
         }
 
+        fn parse_tls_model(slot: &mut Option<TlsModel>, v: Option<&str>) -> bool {
+            match v.and_then(|s| TlsModel::from_str(s).ok()) {
+                Some(tls_model) => *slot = Some(tls_model),
+                _ => return false,
+            }
+            true
+        }
+
         fn parse_symbol_mangling_version(
             slot: &mut SymbolManglingVersion,
             v: Option<&str>,
@@ -977,7 +988,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "measure time of each LLVM pass (default: no)"),
     time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass (default: no)"),
-    tls_model: Option<String> = (None, parse_opt_string, [TRACKED],
+    tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
         "choose the TLS model to use (`rustc --print tls-models` for details)"),
     trace_macros: bool = (false, parse_bool, [UNTRACKED],
         "for every macro invocation, print its name and arguments (default: no)"),
diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs
index 226d9392095..42f9a8d6b05 100644
--- a/src/librustc_session/session.rs
+++ b/src/librustc_session/session.rs
@@ -22,7 +22,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{self, FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
 use rustc_span::SourceFileHashAlgorithm;
-use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target, TargetTriple};
+use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target, TargetTriple, TlsModel};
 
 use std::cell::{self, RefCell};
 use std::env;
@@ -588,6 +588,10 @@ impl Session {
         self.opts.cg.relocation_model.unwrap_or(self.target.target.options.relocation_model)
     }
 
+    pub fn tls_model(&self) -> TlsModel {
+        self.opts.debugging_opts.tls_model.unwrap_or(self.target.target.options.tls_model)
+    }
+
     pub fn must_not_eliminate_frame_pointers(&self) -> bool {
         // "mcount" function relies on stack pointer.
         // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
diff --git a/src/librustc_target/spec/cloudabi_base.rs b/src/librustc_target/spec/cloudabi_base.rs
index 53af9dcc186..3659c9ecdfc 100644
--- a/src/librustc_target/spec/cloudabi_base.rs
+++ b/src/librustc_target/spec/cloudabi_base.rs
@@ -1,4 +1,4 @@
-use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions};
+use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions, TlsModel};
 
 pub fn opts() -> TargetOptions {
     let mut args = LinkArgs::new();
@@ -29,7 +29,7 @@ pub fn opts() -> TargetOptions {
         // (Global Offset Table) to obtain the effective address of a
         // thread-local variable. Using a GOT is useful only when doing
         // dynamic linking.
-        tls_model: "local-exec".to_string(),
+        tls_model: TlsModel::LocalExec,
         relro_level: RelroLevel::Full,
         ..Default::default()
     }
diff --git a/src/librustc_target/spec/hermit_base.rs b/src/librustc_target/spec/hermit_base.rs
index cb12055290e..18fb2aa3d56 100644
--- a/src/librustc_target/spec/hermit_base.rs
+++ b/src/librustc_target/spec/hermit_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions};
+use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy};
+use crate::spec::{RelocModel, TargetOptions, TlsModel};
 
 pub fn opts() -> TargetOptions {
     let mut pre_link_args = LinkArgs::new();
@@ -17,7 +18,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         relocation_model: RelocModel::Static,
         target_family: None,
-        tls_model: "initial-exec".to_string(),
+        tls_model: TlsModel::InitialExec,
         ..Default::default()
     }
 }
diff --git a/src/librustc_target/spec/hermit_kernel_base.rs b/src/librustc_target/spec/hermit_kernel_base.rs
index 11599fda409..7f2dada714d 100644
--- a/src/librustc_target/spec/hermit_kernel_base.rs
+++ b/src/librustc_target/spec/hermit_kernel_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions};
+use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy};
+use crate::spec::{RelocModel, TargetOptions, TlsModel};
 
 pub fn opts() -> TargetOptions {
     let mut pre_link_args = LinkArgs::new();
@@ -18,7 +19,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         relocation_model: RelocModel::Static,
         target_family: None,
-        tls_model: "initial-exec".to_string(),
+        tls_model: TlsModel::InitialExec,
         ..Default::default()
     }
 }
diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs
index 77fc78e8148..e853c07632f 100644
--- a/src/librustc_target/spec/mod.rs
+++ b/src/librustc_target/spec/mod.rs
@@ -305,6 +305,42 @@ impl ToJson for RelocModel {
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum TlsModel {
+    GeneralDynamic,
+    LocalDynamic,
+    InitialExec,
+    LocalExec,
+}
+
+impl FromStr for TlsModel {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<TlsModel, ()> {
+        Ok(match s {
+            // Note the difference "general" vs "global" difference. The model name is "general",
+            // but the user-facing option name is "global" for consistency with other compilers.
+            "global-dynamic" => TlsModel::GeneralDynamic,
+            "local-dynamic" => TlsModel::LocalDynamic,
+            "initial-exec" => TlsModel::InitialExec,
+            "local-exec" => TlsModel::LocalExec,
+            _ => return Err(()),
+        })
+    }
+}
+
+impl ToJson for TlsModel {
+    fn to_json(&self) -> Json {
+        match *self {
+            TlsModel::GeneralDynamic => "global-dynamic",
+            TlsModel::LocalDynamic => "local-dynamic",
+            TlsModel::InitialExec => "initial-exec",
+            TlsModel::LocalExec => "local-exec",
+        }
+        .to_json()
+    }
+}
+
 pub enum LoadTargetError {
     BuiltinTargetNotFound(String),
     Other(String),
@@ -660,7 +696,7 @@ pub struct TargetOptions {
     pub code_model: Option<String>,
     /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec"
     /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang.
-    pub tls_model: String,
+    pub tls_model: TlsModel,
     /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
     pub disable_redzone: bool,
     /// Eliminate frame pointers from stack frames if possible. Defaults to true.
@@ -863,7 +899,7 @@ impl Default for TargetOptions {
             executables: false,
             relocation_model: RelocModel::Pic,
             code_model: None,
-            tls_model: "global-dynamic".to_string(),
+            tls_model: TlsModel::GeneralDynamic,
             disable_redzone: false,
             eliminate_frame_pointer: true,
             function_sections: true,
@@ -1060,6 +1096,18 @@ impl Target {
                     Some(Ok(()))
                 })).unwrap_or(Ok(()))
             } );
+            ($key_name:ident, TlsModel) => ( {
+                let name = (stringify!($key_name)).replace("_", "-");
+                obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+                    match s.parse::<TlsModel>() {
+                        Ok(tls_model) => base.options.$key_name = tls_model,
+                        _ => return Some(Err(format!("'{}' is not a valid TLS model. \
+                                                      Run `rustc --print tls-models` to \
+                                                      see the list of supported values.", s))),
+                    }
+                    Some(Ok(()))
+                })).unwrap_or(Ok(()))
+            } );
             ($key_name:ident, PanicStrategy) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
@@ -1200,7 +1248,7 @@ impl Target {
         key!(executables, bool);
         key!(relocation_model, RelocModel)?;
         key!(code_model, optional);
-        key!(tls_model);
+        key!(tls_model, TlsModel)?;
         key!(disable_redzone, bool);
         key!(eliminate_frame_pointer, bool);
         key!(function_sections, bool);
diff --git a/src/librustc_target/spec/wasm32_base.rs b/src/librustc_target/spec/wasm32_base.rs
index 08bade2abf4..bb19b9d00e8 100644
--- a/src/librustc_target/spec/wasm32_base.rs
+++ b/src/librustc_target/spec/wasm32_base.rs
@@ -1,4 +1,4 @@
-use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions};
+use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
 use std::collections::BTreeMap;
 
 pub fn options() -> TargetOptions {
@@ -138,7 +138,7 @@ pub fn options() -> TargetOptions {
         // `has_elf_tls`) and we need to get it to work by specifying
         // `local-exec` as that's all that's implemented in LLVM today for wasm.
         has_elf_tls: true,
-        tls_model: "local-exec".to_string(),
+        tls_model: TlsModel::LocalExec,
 
         // gdb scripts don't work on wasm blobs
         emit_debug_gdb_scripts: false,
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 8070ad17120..3d665123f67 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -211,12 +211,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::RawPtr(mt_b) => {
                 return self.coerce_unsafe_ptr(a, b, mt_b.mutbl);
             }
-
-            ty::Ref(r_b, ty, mutbl) => {
-                let mt_b = ty::TypeAndMut { ty, mutbl };
-                return self.coerce_borrowed_pointer(a, b, r_b, mt_b);
+            ty::Ref(r_b, _, mutbl_b) => {
+                return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
             }
-
             _ => {}
         }
 
@@ -255,7 +252,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         a: Ty<'tcx>,
         b: Ty<'tcx>,
         r_b: ty::Region<'tcx>,
-        mt_b: TypeAndMut<'tcx>,
+        mutbl_b: hir::Mutability,
     ) -> CoerceResult<'tcx> {
         debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b);
 
@@ -268,7 +265,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         let (r_a, mt_a) = match a.kind {
             ty::Ref(r_a, ty, mutbl) => {
                 let mt_a = ty::TypeAndMut { ty, mutbl };
-                coerce_mutbls(mt_a.mutbl, mt_b.mutbl)?;
+                coerce_mutbls(mt_a.mutbl, mutbl_b)?;
                 (r_a, mt_a)
             }
             _ => return self.unify_and(a, b, identity),
@@ -364,7 +361,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 r_a // [3] above
             } else {
                 if r_borrow_var.is_none() {
-                    // create var lazilly, at most once
+                    // create var lazily, at most once
                     let coercion = Coercion(span);
                     let r = self.next_region_var(coercion);
                     r_borrow_var = Some(r); // [4] above
@@ -375,7 +372,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 r,
                 TypeAndMut {
                     ty: referent_ty,
-                    mutbl: mt_b.mutbl, // [1] above
+                    mutbl: mutbl_b, // [1] above
                 },
             );
             match self.unify(derefd_ty_a, b) {
@@ -417,11 +414,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             // `self.x` both have `&mut `type would be a move of
             // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
             // which is a borrow.
-            assert_eq!(mt_b.mutbl, hir::Mutability::Not); // can only coerce &T -> &U
+            assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U
             return success(vec![], ty, obligations);
         }
 
-        let needs = Needs::maybe_mut_place(mt_b.mutbl);
+        let needs = Needs::maybe_mut_place(mutbl_b);
         let InferOk { value: mut adjustments, obligations: o } =
             autoderef.adjust_steps_as_infer_ok(self, needs);
         obligations.extend(o);
@@ -433,7 +430,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::Ref(r_borrow, _, _) => r_borrow,
             _ => span_bug!(span, "expected a ref type, got {:?}", ty),
         };
-        let mutbl = match mt_b.mutbl {
+        let mutbl = match mutbl_b {
             hir::Mutability::Not => AutoBorrowMutability::Not,
             hir::Mutability::Mut => {
                 AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase }
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 119bdfcb0f4..f4c164a324e 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -660,6 +660,11 @@ impl Read for File {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -674,6 +679,11 @@ impl Write for File {
         self.inner.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.inner.flush()
     }
@@ -695,6 +705,11 @@ impl Read for &File {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -709,6 +724,11 @@ impl Write for &File {
         self.inner.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.inner.flush()
     }
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index 16ca539b3c1..046b1a68880 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -292,6 +292,10 @@ impl<R: Read> Read for BufReader<R> {
         Ok(nread)
     }
 
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
     // we can't skip unconditionally because of the large buffer case in read.
     unsafe fn initializer(&self) -> Initializer {
         self.inner.initializer()
@@ -680,6 +684,10 @@ impl<W: Write> Write for BufWriter<W> {
         }
     }
 
+    fn is_write_vectored(&self) -> bool {
+        self.get_ref().is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.flush_buf().and_then(|()| self.get_mut().flush())
     }
diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs
index f36aa1846a1..f3e3fc81a5d 100644
--- a/src/libstd/io/cursor.rs
+++ b/src/libstd/io/cursor.rs
@@ -266,6 +266,10 @@ where
         Ok(nread)
     }
 
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
         let n = buf.len();
         Read::read_exact(&mut self.fill_buf()?, buf)?;
@@ -373,6 +377,11 @@ impl Write for Cursor<&mut [u8]> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -389,6 +398,11 @@ impl Write for Cursor<&mut Vec<u8>> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -405,6 +419,11 @@ impl Write for Cursor<Vec<u8>> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -423,6 +442,11 @@ impl Write for Cursor<Box<[u8]>> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs
index b7f82e65299..01dff0b3eb3 100644
--- a/src/libstd/io/impls.rs
+++ b/src/libstd/io/impls.rs
@@ -21,6 +21,11 @@ impl<R: Read + ?Sized> Read for &mut R {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        (**self).is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -53,6 +58,11 @@ impl<W: Write + ?Sized> Write for &mut W {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        (**self).is_write_vectored()
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         (**self).flush()
     }
@@ -110,6 +120,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        (**self).is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         (**self).initializer()
     }
@@ -142,6 +157,11 @@ impl<W: Write + ?Sized> Write for Box<W> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        (**self).is_write_vectored()
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         (**self).flush()
     }
@@ -241,6 +261,11 @@ impl Read for &[u8] {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -317,6 +342,11 @@ impl Write for &mut [u8] {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
         if self.write(data)? == data.len() {
             Ok(())
@@ -352,6 +382,11 @@ impl Write for Vec<u8> {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
         self.extend_from_slice(buf);
         Ok(())
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 5ab88260d6a..9cfb1728c04 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -256,6 +256,7 @@
 //! [`Read::read`]: trait.Read.html#tymethod.read
 //! [`Result`]: ../result/enum.Result.html
 //! [`.unwrap()`]: ../result/enum.Result.html#method.unwrap
+// ignore-tidy-filelength
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -580,6 +581,19 @@ pub trait Read {
         default_read_vectored(|b| self.read(b), bufs)
     }
 
+    /// Determines if this `Read`er has an efficient `read_vectored`
+    /// implementation.
+    ///
+    /// If a `Read`er does not override the default `read_vectored`
+    /// implementation, code using it may want to avoid the method all together
+    /// and coalesce writes into a single buffer for higher performance.
+    ///
+    /// The default implementation returns `false`.
+    #[unstable(feature = "can_vector", issue = "69941")]
+    fn is_read_vectored(&self) -> bool {
+        false
+    }
+
     /// Determines if this `Read`er can work with buffers of uninitialized
     /// memory.
     ///
@@ -1304,6 +1318,19 @@ pub trait Write {
         default_write_vectored(|b| self.write(b), bufs)
     }
 
+    /// Determines if this `Write`er has an efficient `write_vectored`
+    /// implementation.
+    ///
+    /// If a `Write`er does not override the default `write_vectored`
+    /// implementation, code using it may want to avoid the method all together
+    /// and coalesce writes into a single buffer for higher performance.
+    ///
+    /// The default implementation returns `false`.
+    #[unstable(feature = "can_vector", issue = "69941")]
+    fn is_write_vectored(&self) -> bool {
+        false
+    }
+
     /// Flush this output stream, ensuring that all intermediately buffered
     /// contents reach their destination.
     ///
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index a064c552c84..b65b150d2c3 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -88,6 +88,11 @@ impl Read for StdinRaw {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -101,6 +106,11 @@ impl Write for StdoutRaw {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.0.flush()
     }
@@ -114,6 +124,11 @@ impl Write for StderrRaw {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         self.0.flush()
     }
@@ -140,6 +155,14 @@ impl<W: io::Write> io::Write for Maybe<W> {
         }
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        match self {
+            Maybe::Real(w) => w.is_write_vectored(),
+            Maybe::Fake => true,
+        }
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         match *self {
             Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()),
@@ -162,6 +185,14 @@ impl<R: io::Read> io::Read for Maybe<R> {
             Maybe::Fake => Ok(0),
         }
     }
+
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        match self {
+            Maybe::Real(w) => w.is_read_vectored(),
+            Maybe::Fake => true,
+        }
+    }
 }
 
 fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
@@ -352,6 +383,10 @@ impl Read for Stdin {
         self.lock().read_vectored(bufs)
     }
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.lock().is_read_vectored()
+    }
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -377,6 +412,11 @@ impl Read for StdinLock<'_> {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -543,6 +583,10 @@ impl Write for Stdout {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.lock().write_vectored(bufs)
     }
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.lock().is_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.lock().flush()
     }
@@ -561,6 +605,10 @@ impl Write for StdoutLock<'_> {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.inner.borrow_mut().write_vectored(bufs)
     }
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.inner.borrow_mut().is_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.inner.borrow_mut().flush()
     }
@@ -709,6 +757,10 @@ impl Write for Stderr {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.lock().write_vectored(bufs)
     }
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.lock().is_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.lock().flush()
     }
@@ -727,6 +779,10 @@ impl Write for StderrLock<'_> {
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.inner.borrow_mut().write_vectored(bufs)
     }
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.inner.borrow_mut().is_write_vectored()
+    }
     fn flush(&mut self) -> io::Result<()> {
         self.inner.borrow_mut().flush()
     }
diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs
index b09161b97aa..b9d5dc27db0 100644
--- a/src/libstd/io/util.rs
+++ b/src/libstd/io/util.rs
@@ -180,6 +180,11 @@ impl Read for Repeat {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -236,6 +241,11 @@ impl Write for Sink {
     }
 
     #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 5fd15bb8fe4..ac07af5e278 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -243,6 +243,7 @@
 #![feature(box_syntax)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
+#![feature(can_vector)]
 #![feature(cfg_target_has_atomic)]
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs
index 5023d692408..9ac54dd5f7a 100644
--- a/src/libstd/net/tcp.rs
+++ b/src/libstd/net/tcp.rs
@@ -577,6 +577,11 @@ impl Read for TcpStream {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -591,6 +596,11 @@ impl Write for TcpStream {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -606,6 +616,11 @@ impl Read for &TcpStream {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -620,6 +635,11 @@ impl Write for &TcpStream {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/process.rs b/src/libstd/process.rs
index 3eee45d000c..b457d190b95 100644
--- a/src/libstd/process.rs
+++ b/src/libstd/process.rs
@@ -245,6 +245,10 @@ impl Write for ChildStdin {
         self.inner.write_vectored(bufs)
     }
 
+    fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -301,6 +305,11 @@ impl Read for ChildStdout {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -357,6 +366,11 @@ impl Read for ChildStderr {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
diff --git a/src/libstd/sys/cloudabi/shims/fs.rs b/src/libstd/sys/cloudabi/shims/fs.rs
index e6160d1457d..ecb5b51cccd 100644
--- a/src/libstd/sys/cloudabi/shims/fs.rs
+++ b/src/libstd/sys/cloudabi/shims/fs.rs
@@ -202,6 +202,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -210,6 +214,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs
index 67c436fa795..375aaab405d 100644
--- a/src/libstd/sys/cloudabi/shims/net.rs
+++ b/src/libstd/sys/cloudabi/shims/net.rs
@@ -47,6 +47,10 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -55,6 +59,10 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/cloudabi/shims/pipe.rs b/src/libstd/sys/cloudabi/shims/pipe.rs
index fb14dc59101..10d0925823e 100644
--- a/src/libstd/sys/cloudabi/shims/pipe.rs
+++ b/src/libstd/sys/cloudabi/shims/pipe.rs
@@ -12,6 +12,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -20,6 +24,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn diverge(&self) -> ! {
         match self.0 {}
     }
diff --git a/src/libstd/sys/hermit/fs.rs b/src/libstd/sys/hermit/fs.rs
index 287a0390667..82ccab1462b 100644
--- a/src/libstd/sys/hermit/fs.rs
+++ b/src/libstd/sys/hermit/fs.rs
@@ -301,6 +301,11 @@ impl File {
         crate::io::default_read_vectored(|buf| self.read(buf), bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        false
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
@@ -309,6 +314,11 @@ impl File {
         crate::io::default_write_vectored(|buf| self.write(buf), bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        false
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/hermit/net.rs b/src/libstd/sys/hermit/net.rs
index 377c3132c5a..5b5379c8b05 100644
--- a/src/libstd/sys/hermit/net.rs
+++ b/src/libstd/sys/hermit/net.rs
@@ -99,6 +99,11 @@ impl TcpStream {
         Ok(size)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
         self.write_vectored(&[IoSlice::new(buffer)])
     }
@@ -114,6 +119,11 @@ impl TcpStream {
         Ok(size)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         Err(io::Error::new(ErrorKind::Other, "peer_addr isn't supported"))
     }
diff --git a/src/libstd/sys/hermit/pipe.rs b/src/libstd/sys/hermit/pipe.rs
index fb14dc59101..10d0925823e 100644
--- a/src/libstd/sys/hermit/pipe.rs
+++ b/src/libstd/sys/hermit/pipe.rs
@@ -12,6 +12,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -20,6 +24,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn diverge(&self) -> ! {
         match self.0 {}
     }
diff --git a/src/libstd/sys/hermit/stdio.rs b/src/libstd/sys/hermit/stdio.rs
index 2eb011ccb39..208265de465 100644
--- a/src/libstd/sys/hermit/stdio.rs
+++ b/src/libstd/sys/hermit/stdio.rs
@@ -20,6 +20,11 @@ impl Stdin {
         //    .read(data)
         Ok(0)
     }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
 }
 
 impl Stdout {
@@ -51,6 +56,11 @@ impl Stdout {
         }
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
@@ -85,6 +95,11 @@ impl Stderr {
         }
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs
index 7da2424a642..90158030c7f 100644
--- a/src/libstd/sys/sgx/fd.rs
+++ b/src/libstd/sys/sgx/fd.rs
@@ -34,6 +34,11 @@ impl FileDesc {
         usercalls::read(self.fd, bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         usercalls::write(self.fd, &[IoSlice::new(buf)])
     }
@@ -42,6 +47,11 @@ impl FileDesc {
         usercalls::write(self.fd, bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         usercalls::flush(self.fd)
     }
diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs
index e6160d1457d..ecb5b51cccd 100644
--- a/src/libstd/sys/sgx/fs.rs
+++ b/src/libstd/sys/sgx/fs.rs
@@ -202,6 +202,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -210,6 +214,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs
index bd0652ab464..666a157b09c 100644
--- a/src/libstd/sys/sgx/net.rs
+++ b/src/libstd/sys/sgx/net.rs
@@ -149,6 +149,11 @@ impl TcpStream {
         self.inner.inner.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.inner.inner.is_read_vectored()
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.inner.inner.write(buf)
     }
@@ -157,6 +162,11 @@ impl TcpStream {
         self.inner.inner.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.inner.inner.is_write_vectored()
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         addr_to_sockaddr(&self.peer_addr)
     }
diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs
index fb14dc59101..10d0925823e 100644
--- a/src/libstd/sys/sgx/pipe.rs
+++ b/src/libstd/sys/sgx/pipe.rs
@@ -12,6 +12,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -20,6 +24,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn diverge(&self) -> ! {
         match self.0 {}
     }
diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs
index 4c3cb67c9ee..bfdc39ada75 100644
--- a/src/libstd/sys/unix/ext/net.rs
+++ b/src/libstd/sys/unix/ext/net.rs
@@ -614,6 +614,11 @@ impl io::Read for UnixStream {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        io::Read::is_read_vectored(&&*self)
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -630,6 +635,11 @@ impl<'a> io::Read for &'a UnixStream {
     }
 
     #[inline]
+    fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    #[inline]
     unsafe fn initializer(&self) -> Initializer {
         Initializer::nop()
     }
@@ -645,6 +655,11 @@ impl io::Write for UnixStream {
         io::Write::write_vectored(&mut &*self, bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        io::Write::is_write_vectored(&&*self)
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         io::Write::flush(&mut &*self)
     }
@@ -660,6 +675,11 @@ impl<'a> io::Write for &'a UnixStream {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs
index 1bba56e334a..1ef7ffacfcf 100644
--- a/src/libstd/sys/unix/fd.rs
+++ b/src/libstd/sys/unix/fd.rs
@@ -64,6 +64,11 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
         let mut me = self;
         (&mut me).read_to_end(buf)
@@ -116,6 +121,11 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         #[cfg(target_os = "android")]
         use super::android::cvt_pwrite64;
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index a233aa47dff..2cfc63d9492 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -828,6 +828,11 @@ impl File {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         self.0.read_at(buf, offset)
     }
@@ -840,6 +845,11 @@ impl File {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         self.0.write_at(buf, offset)
     }
diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs
index c6e4f5693ed..a2912387108 100644
--- a/src/libstd/sys/unix/l4re.rs
+++ b/src/libstd/sys/unix/l4re.rs
@@ -55,6 +55,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn is_read_vectored(&self) -> bool {
+            unimpl!();
+        }
+
         pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
             unimpl!();
         }
@@ -75,6 +79,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn is_write_vectored(&self) -> bool {
+            unimpl!();
+        }
+
         pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
             unimpl!();
         }
@@ -171,6 +179,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn is_read_vectored(&self) -> bool {
+            unimpl!();
+        }
+
         pub fn write(&self, _: &[u8]) -> io::Result<usize> {
             unimpl!();
         }
@@ -179,6 +191,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn is_write_vectored(&self) -> bool {
+            unimpl!();
+        }
+
         pub fn peer_addr(&self) -> io::Result<SocketAddr> {
             unimpl!();
         }
diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs
index d18c22b0573..f062bc012f7 100644
--- a/src/libstd/sys/unix/net.rs
+++ b/src/libstd/sys/unix/net.rs
@@ -226,6 +226,11 @@ impl Socket {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     fn recv_from_with_flags(
         &self,
         buf: &mut [u8],
@@ -263,6 +268,11 @@ impl Socket {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
         let timeout = match dur {
             Some(dur) => {
diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs
index 2a861c87801..f2a2eabef91 100644
--- a/src/libstd/sys/unix/pipe.rs
+++ b/src/libstd/sys/unix/pipe.rs
@@ -64,6 +64,11 @@ impl AnonPipe {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
@@ -72,6 +77,11 @@ impl AnonPipe {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn fd(&self) -> &FileDesc {
         &self.0
     }
diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs
index b9c56963885..f8353214cbc 100644
--- a/src/libstd/sys/unix/stdio.rs
+++ b/src/libstd/sys/unix/stdio.rs
@@ -20,6 +20,11 @@ impl io::Read for Stdin {
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs)
     }
+
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
 }
 
 impl Stdout {
@@ -37,6 +42,11 @@ impl io::Write for Stdout {
         ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
@@ -57,6 +67,11 @@ impl io::Write for Stderr {
         ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs)
     }
 
+    #[inline]
+    fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/vxworks/fd.rs b/src/libstd/sys/vxworks/fd.rs
index 65c67dabc1a..23e9dc428ce 100644
--- a/src/libstd/sys/vxworks/fd.rs
+++ b/src/libstd/sys/vxworks/fd.rs
@@ -54,6 +54,11 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
         let mut me = self;
         (&mut me).read_to_end(buf)
@@ -99,6 +104,11 @@ impl FileDesc {
         Ok(ret as usize)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         unsafe fn cvt_pwrite(
             fd: c_int,
diff --git a/src/libstd/sys/vxworks/fs.rs b/src/libstd/sys/vxworks/fs.rs
index 68f2c133170..557e65ca01b 100644
--- a/src/libstd/sys/vxworks/fs.rs
+++ b/src/libstd/sys/vxworks/fs.rs
@@ -351,6 +351,11 @@ impl File {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         self.0.read_at(buf, offset)
     }
@@ -363,6 +368,11 @@ impl File {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         self.0.write_at(buf, offset)
     }
diff --git a/src/libstd/sys/vxworks/net.rs b/src/libstd/sys/vxworks/net.rs
index 7d4e5624f7e..de0b15b43a2 100644
--- a/src/libstd/sys/vxworks/net.rs
+++ b/src/libstd/sys/vxworks/net.rs
@@ -163,6 +163,11 @@ impl Socket {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     fn recv_from_with_flags(
         &self,
         buf: &mut [u8],
@@ -200,6 +205,11 @@ impl Socket {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
         let timeout = match dur {
             Some(dur) => {
diff --git a/src/libstd/sys/vxworks/pipe.rs b/src/libstd/sys/vxworks/pipe.rs
index 0990cb8e83c..a18376212af 100644
--- a/src/libstd/sys/vxworks/pipe.rs
+++ b/src/libstd/sys/vxworks/pipe.rs
@@ -24,10 +24,16 @@ impl AnonPipe {
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
         self.0.read(buf)
     }
+
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         self.0.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
@@ -36,6 +42,11 @@ impl AnonPipe {
         self.0.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
     pub fn fd(&self) -> &FileDesc {
         &self.0
     }
diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs
index a11f61fdd69..793daea43c2 100644
--- a/src/libstd/sys/wasi/fs.rs
+++ b/src/libstd/sys/wasi/fs.rs
@@ -399,6 +399,11 @@ impl File {
         self.fd.read(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.write_vectored(&[IoSlice::new(buf)])
     }
@@ -407,6 +412,11 @@ impl File {
         self.fd.write(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/wasi/net.rs b/src/libstd/sys/wasi/net.rs
index 8a69028ff1d..e186453588d 100644
--- a/src/libstd/sys/wasi/net.rs
+++ b/src/libstd/sys/wasi/net.rs
@@ -48,6 +48,10 @@ impl TcpStream {
         unsupported()
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
         unsupported()
     }
@@ -56,6 +60,10 @@ impl TcpStream {
         unsupported()
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         unsupported()
     }
diff --git a/src/libstd/sys/wasi/pipe.rs b/src/libstd/sys/wasi/pipe.rs
index fb14dc59101..10d0925823e 100644
--- a/src/libstd/sys/wasi/pipe.rs
+++ b/src/libstd/sys/wasi/pipe.rs
@@ -12,6 +12,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -20,6 +24,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn diverge(&self) -> ! {
         match self.0 {}
     }
diff --git a/src/libstd/sys/wasi/stdio.rs b/src/libstd/sys/wasi/stdio.rs
index 1d53884f2d6..9f9e35566ec 100644
--- a/src/libstd/sys/wasi/stdio.rs
+++ b/src/libstd/sys/wasi/stdio.rs
@@ -19,6 +19,11 @@ impl Stdin {
         ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn as_raw_fd(&self) -> u32 {
         0
     }
@@ -37,6 +42,11 @@ impl Stdout {
         ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
@@ -59,6 +69,11 @@ impl Stderr {
         ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/libstd/sys/wasm/fs.rs b/src/libstd/sys/wasm/fs.rs
index e6160d1457d..ecb5b51cccd 100644
--- a/src/libstd/sys/wasm/fs.rs
+++ b/src/libstd/sys/wasm/fs.rs
@@ -202,6 +202,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -210,6 +214,10 @@ impl File {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs
index b7c3108f172..5c9f1098f9b 100644
--- a/src/libstd/sys/wasm/net.rs
+++ b/src/libstd/sys/wasm/net.rs
@@ -44,6 +44,10 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -52,6 +56,10 @@ impl TcpStream {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         match self.0 {}
     }
diff --git a/src/libstd/sys/wasm/pipe.rs b/src/libstd/sys/wasm/pipe.rs
index fb14dc59101..10d0925823e 100644
--- a/src/libstd/sys/wasm/pipe.rs
+++ b/src/libstd/sys/wasm/pipe.rs
@@ -12,6 +12,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_read_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         match self.0 {}
     }
@@ -20,6 +24,10 @@ impl AnonPipe {
         match self.0 {}
     }
 
+    pub fn is_write_vectored(&self) -> bool {
+        match self.0 {}
+    }
+
     pub fn diverge(&self) -> ! {
         match self.0 {}
     }
diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs
index 427f4b684e1..cdbfac267b9 100644
--- a/src/libstd/sys/windows/fs.rs
+++ b/src/libstd/sys/windows/fs.rs
@@ -409,6 +409,11 @@ impl File {
         self.handle.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.handle.is_read_vectored()
+    }
+
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         self.handle.read_at(buf, offset)
     }
@@ -421,6 +426,11 @@ impl File {
         self.handle.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.handle.is_write_vectored()
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         self.handle.write_at(buf, offset)
     }
diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs
index d00381792e3..2131cfc2c94 100644
--- a/src/libstd/sys/windows/handle.rs
+++ b/src/libstd/sys/windows/handle.rs
@@ -92,6 +92,11 @@ impl RawHandle {
         crate::io::default_read_vectored(|buf| self.read(buf), bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        false
+    }
+
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         let mut read = 0;
         let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
@@ -171,6 +176,11 @@ impl RawHandle {
         crate::io::default_write_vectored(|buf| self.write(buf), bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        false
+    }
+
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         let mut written = 0;
         let len = cmp::min(buf.len(), <c::DWORD>::max_value() as usize) as c::DWORD;
diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs
index d8d4fdfce2f..a15ded92f08 100644
--- a/src/libstd/sys/windows/net.rs
+++ b/src/libstd/sys/windows/net.rs
@@ -266,6 +266,11 @@ impl Socket {
         }
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
     pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
         self.recv_with_flags(buf, c::MSG_PEEK)
     }
@@ -324,6 +329,11 @@ impl Socket {
         Ok(nwritten as usize)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
     pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> {
         let timeout = match dur {
             Some(dur) => {
diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs
index 992e634dea5..104a8db4659 100644
--- a/src/libstd/sys/windows/pipe.rs
+++ b/src/libstd/sys/windows/pipe.rs
@@ -182,6 +182,11 @@ impl AnonPipe {
         self.inner.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.inner.write(buf)
     }
@@ -189,6 +194,11 @@ impl AnonPipe {
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         self.inner.write_vectored(bufs)
     }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
 }
 
 pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> {
diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs
index cdd3d2edf1f..a9b6079de75 100644
--- a/src/libstd/sys_common/net.rs
+++ b/src/libstd/sys_common/net.rs
@@ -265,6 +265,11 @@ impl TcpStream {
         self.inner.read_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let len = cmp::min(buf.len(), <wrlen_t>::max_value() as usize) as wrlen_t;
         let ret = cvt(unsafe {
@@ -277,6 +282,11 @@ impl TcpStream {
         self.inner.write_vectored(bufs)
     }
 
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.inner.is_write_vectored()
+    }
+
     pub fn peer_addr(&self) -> io::Result<SocketAddr> {
         sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) })
     }
diff --git a/src/test/ui/consts/recursive-zst-static.stderr b/src/test/ui/consts/recursive-zst-static.default.stderr
index e21dcf691ab..d424b22f000 100644
--- a/src/test/ui/consts/recursive-zst-static.stderr
+++ b/src/test/ui/consts/recursive-zst-static.default.stderr
@@ -1,17 +1,17 @@
 error[E0391]: cycle detected when const-evaluating `FOO`
-  --> $DIR/recursive-zst-static.rs:7:18
+  --> $DIR/recursive-zst-static.rs:10:18
    |
 LL | static FOO: () = FOO;
    |                  ^^^
    |
 note: ...which requires const-evaluating `FOO`...
-  --> $DIR/recursive-zst-static.rs:7:1
+  --> $DIR/recursive-zst-static.rs:10:1
    |
 LL | static FOO: () = FOO;
    | ^^^^^^^^^^^^^^^^^^^^^
    = note: ...which again requires const-evaluating `FOO`, completing the cycle
 note: cycle used when const-evaluating + checking `FOO`
-  --> $DIR/recursive-zst-static.rs:7:1
+  --> $DIR/recursive-zst-static.rs:10:1
    |
 LL | static FOO: () = FOO;
    | ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/consts/recursive-zst-static.rs b/src/test/ui/consts/recursive-zst-static.rs
index 768df58e1e3..29a467c006a 100644
--- a/src/test/ui/consts/recursive-zst-static.rs
+++ b/src/test/ui/consts/recursive-zst-static.rs
@@ -1,3 +1,6 @@
+// revisions: default unleash
+//[unleash]compile-flags: -Zunleash-the-miri-inside-of-you
+
 // This test ensures that we do not allow ZST statics to initialize themselves without ever
 // actually creating a value of that type. This is important, as the ZST may have private fields
 // that users can reasonably expect to only get initialized by their own code. Thus unsafe code
diff --git a/src/test/ui/consts/recursive-zst-static.unleash.stderr b/src/test/ui/consts/recursive-zst-static.unleash.stderr
new file mode 100644
index 00000000000..d424b22f000
--- /dev/null
+++ b/src/test/ui/consts/recursive-zst-static.unleash.stderr
@@ -0,0 +1,21 @@
+error[E0391]: cycle detected when const-evaluating `FOO`
+  --> $DIR/recursive-zst-static.rs:10:18
+   |
+LL | static FOO: () = FOO;
+   |                  ^^^
+   |
+note: ...which requires const-evaluating `FOO`...
+  --> $DIR/recursive-zst-static.rs:10:1
+   |
+LL | static FOO: () = FOO;
+   | ^^^^^^^^^^^^^^^^^^^^^
+   = note: ...which again requires const-evaluating `FOO`, completing the cycle
+note: cycle used when const-evaluating + checking `FOO`
+  --> $DIR/recursive-zst-static.rs:10:1
+   |
+LL | static FOO: () = FOO;
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0391`.
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index fb4611ed1ca..570ffd5d306 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -114,7 +114,7 @@ fn walk(cache: &mut Cache, root: &Path, dir: &Path, errors: &mut bool) {
 }
 
 fn check(cache: &mut Cache, root: &Path, file: &Path, errors: &mut bool) -> Option<PathBuf> {
-    // Ignore none HTML files.
+    // Ignore non-HTML files.
     if file.extension().and_then(|s| s.to_str()) != Some("html") {
         return None;
     }
diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs
index 243d41598f8..f7fd0c670d7 100644
--- a/src/tools/tidy/src/error_codes_check.rs
+++ b/src/tools/tidy/src/error_codes_check.rs
@@ -17,7 +17,7 @@ const WHITELIST: &[&str] = &[
 
 // Some error codes don't have any tests apparently...
 const IGNORE_EXPLANATION_CHECK: &[&str] =
-    &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750", "E0751"];
+    &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750"];
 
 fn check_error_code_explanation(
     f: &str,