diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index fefcaa898c1..ba7cc4908b8 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -1,6 +1,5 @@ #![feature(let_chains)] #![feature(once_cell)] -#![feature(path_try_exists)] #![feature(rustc_attrs)] #![feature(type_alias_impl_trait)] diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 422af667875..fd0c3f36e72 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -39,11 +39,13 @@ use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{sym, Symbol}; use serde_json::Value; use std::borrow::Cow; use std::collections::BTreeMap; use std::convert::TryFrom; +use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; @@ -2183,7 +2185,7 @@ impl Target { TargetTriple::TargetTriple(ref target_triple) => { load_builtin(target_triple).expect("built-in target") } - TargetTriple::TargetPath(..) => { + TargetTriple::TargetJson { .. } => { panic!("built-in targets doens't support target-paths") } } @@ -2248,11 +2250,9 @@ impl Target { Err(format!("Could not find specification for target {:?}", target_triple)) } - TargetTriple::TargetPath(ref target_path) => { - if target_path.is_file() { - return load_file(&target_path); - } - Err(format!("Target path {:?} is not a valid file", target_path)) + TargetTriple::TargetJson { ref contents, .. } => { + let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?; + Target::from_json(obj) } } } @@ -2421,10 +2421,77 @@ impl ToJson for Target { } /// Either a target triple string or a path to a JSON file. -#[derive(PartialEq, Clone, Debug, Hash, Encodable, Decodable)] +#[derive(Clone, Debug)] pub enum TargetTriple { TargetTriple(String), - TargetPath(PathBuf), + TargetJson { + /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to + /// inconsistencies as it is discarded during serialization. + path_for_rustdoc: PathBuf, + triple: String, + contents: String, + }, +} + +// Use a manual implementation to ignore the path field +impl PartialEq for TargetTriple { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::TargetTriple(l0), Self::TargetTriple(r0)) => l0 == r0, + ( + Self::TargetJson { path_for_rustdoc: _, triple: l_triple, contents: l_contents }, + Self::TargetJson { path_for_rustdoc: _, triple: r_triple, contents: r_contents }, + ) => l_triple == r_triple && l_contents == r_contents, + _ => false, + } + } +} + +// Use a manual implementation to ignore the path field +impl Hash for TargetTriple { + fn hash(&self, state: &mut H) -> () { + match self { + TargetTriple::TargetTriple(triple) => { + 0u8.hash(state); + triple.hash(state) + } + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => { + 1u8.hash(state); + triple.hash(state); + contents.hash(state) + } + } + } +} + +// Use a manual implementation to prevent encoding the target json file path in the crate metadata +impl Encodable for TargetTriple { + fn encode(&self, s: &mut S) { + match self { + TargetTriple::TargetTriple(triple) => s.emit_enum_variant(0, |s| s.emit_str(triple)), + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => s + .emit_enum_variant(1, |s| { + s.emit_str(triple); + s.emit_str(contents) + }), + } + } +} + +impl Decodable for TargetTriple { + fn decode(d: &mut D) -> Self { + match d.read_usize() { + 0 => TargetTriple::TargetTriple(d.read_str().to_owned()), + 1 => TargetTriple::TargetJson { + path_for_rustdoc: PathBuf::new(), + triple: d.read_str().to_owned(), + contents: d.read_str().to_owned(), + }, + _ => { + panic!("invalid enum variant tag while decoding `TargetTriple`, expected 0..2"); + } + } + } } impl TargetTriple { @@ -2436,7 +2503,19 @@ impl TargetTriple { /// Creates a target triple from the passed target path. pub fn from_path(path: &Path) -> Result { let canonicalized_path = path.canonicalize()?; - Ok(TargetTriple::TargetPath(canonicalized_path)) + let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("target path {:?} is not a valid file: {}", canonicalized_path, err), + ) + })?; + let triple = canonicalized_path + .file_stem() + .expect("target path must not be empty") + .to_str() + .expect("target path must be valid unicode") + .to_owned(); + Ok(TargetTriple::TargetJson { path_for_rustdoc: canonicalized_path, triple, contents }) } /// Returns a string triple for this target. @@ -2444,12 +2523,8 @@ impl TargetTriple { /// If this target is a path, the file name (without extension) is returned. pub fn triple(&self) -> &str { match *self { - TargetTriple::TargetTriple(ref triple) => triple, - TargetTriple::TargetPath(ref path) => path - .file_stem() - .expect("target path must not be empty") - .to_str() - .expect("target path must be valid unicode"), + TargetTriple::TargetTriple(ref triple) + | TargetTriple::TargetJson { ref triple, .. } => triple, } } @@ -2459,16 +2534,15 @@ impl TargetTriple { /// by `triple()`. pub fn debug_triple(&self) -> String { use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - let triple = self.triple(); - if let TargetTriple::TargetPath(ref path) = *self { - let mut hasher = DefaultHasher::new(); - path.hash(&mut hasher); - let hash = hasher.finish(); - format!("{}-{}", triple, hash) - } else { - triple.into() + match self { + TargetTriple::TargetTriple(triple) => triple.to_owned(), + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents: content } => { + let mut hasher = DefaultHasher::new(); + content.hash(&mut hasher); + let hash = hasher.finish(); + format!("{}-{}", triple, hash) + } } } } diff --git a/library/core/src/future/into_future.rs b/library/core/src/future/into_future.rs index d22094130ad..ad9e80e117f 100644 --- a/library/core/src/future/into_future.rs +++ b/library/core/src/future/into_future.rs @@ -2,7 +2,7 @@ use crate::future::Future; /// Conversion into a `Future`. /// -/// By implementing `Intofuture` for a type, you define how it will be +/// By implementing `IntoFuture` for a type, you define how it will be /// converted to a future. /// /// # `.await` desugaring @@ -29,7 +29,7 @@ use crate::future::Future; /// When implementing futures manually there will often be a choice between /// implementing `Future` or `IntoFuture` for a type. Implementing `Future` is a /// good choice in most cases. But implementing `IntoFuture` is most useful when -/// implementing "async builder" types, which allows the type to be modified +/// implementing "async builder" types, which allow their values to be modified /// multiple times before being `.await`ed. /// /// ```rust diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 71ea3b4ba85..ecd2b75ae44 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -973,6 +973,28 @@ pub const fn replace(dest: &mut T, src: T) -> T { #[cfg_attr(not(test), rustc_diagnostic_item = "mem_drop")] pub fn drop(_x: T) {} +/// Bitwise-copies a value. +/// +/// This function is not magic; it is literally defined as +/// ``` +/// pub fn copy(x: &T) -> T { *x } +/// ``` +/// +/// It is useful when you want to pass a function pointer to a combinator, rather than defining a new closure. +/// +/// Example: +/// ``` +/// #![feature(mem_copy_fn)] +/// use core::mem::copy; +/// let result_from_ffi_function: Result<(), &i32> = Err(&1); +/// let result_copied: Result<(), i32> = result_from_ffi_function.map_err(copy); +/// ``` +#[inline] +#[unstable(feature = "mem_copy_fn", issue = "98262")] +pub fn copy(x: &T) -> T { + *x +} + /// Interprets `src` as having type `&U`, and then reads `src` without moving /// the contained value. /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 55bd2c59406..f46997b807a 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2317,10 +2317,14 @@ impl AsInnerMut for DirBuilder { /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// +/// Note that while this avoids some pitfalls of the `exists()` method, it still can not +/// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios +/// where those bugs are not an issue. +/// /// # Examples /// /// ```no_run -/// #![feature(path_try_exists)] +/// #![feature(fs_try_exists)] /// use std::fs; /// /// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt")); @@ -2330,7 +2334,7 @@ impl AsInnerMut for DirBuilder { /// [`Path::exists`]: crate::path::Path::exists // FIXME: stabilization should modify documentation of `exists()` to recommend this method // instead. -#[unstable(feature = "path_try_exists", issue = "83186")] +#[unstable(feature = "fs_try_exists", issue = "83186")] #[inline] pub fn try_exists>(path: P) -> io::Result { fs_imp::try_exists(path.as_ref()) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 6bbc8f55ace..5dfeb517a19 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -2705,6 +2705,9 @@ impl Path { /// Returns `true` if the path points at an existing entity. /// + /// Warning: this method may be error-prone, consider using [`try_exists()`] instead! + /// It also has a risk of introducing time-of-check to time-of-use (TOCTOU) bugs. + /// /// This function will traverse symbolic links to query information about the /// destination file. /// @@ -2721,7 +2724,9 @@ impl Path { /// # See Also /// /// This is a convenience function that coerces errors to false. If you want to - /// check errors, call [`fs::metadata`]. + /// check errors, call [`Path::try_exists`]. + /// + /// [`try_exists()`]: Self::try_exists #[stable(feature = "path_ext", since = "1.5.0")] #[must_use] #[inline] @@ -2738,20 +2743,20 @@ impl Path { /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission /// denied on some of the parent directories.) /// + /// Note that while this avoids some pitfalls of the `exists()` method, it still can not + /// prevent time-of-check to time-of-use (TOCTOU) bugs. You should only use it in scenarios + /// where those bugs are not an issue. + /// /// # Examples /// /// ```no_run - /// #![feature(path_try_exists)] - /// /// use std::path::Path; /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt")); /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err()); /// ``` /// /// [`exists()`]: Self::exists - // FIXME: stabilization should modify documentation of `exists()` to recommend this method - // instead. - #[unstable(feature = "path_try_exists", issue = "83186")] + #[stable(feature = "path_try_exists", since = "1.63.0")] #[inline] pub fn try_exists(&self) -> io::Result { fs::try_exists(self) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 86c58cd79dc..ab72f4a3f50 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -365,8 +365,8 @@ fn run_test( } compiler.arg("--target").arg(match target { TargetTriple::TargetTriple(s) => s, - TargetTriple::TargetPath(path) => { - path.to_str().expect("target path must be valid unicode").to_string() + TargetTriple::TargetJson { path_for_rustdoc, .. } => { + path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() } }); if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format {