Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 4 pull requests #98265

Merged
merged 9 commits into from
Jun 20, 2022
1 change: 0 additions & 1 deletion compiler/rustc_error_messages/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#![feature(let_chains)]
#![feature(once_cell)]
#![feature(path_try_exists)]
#![feature(rustc_attrs)]
#![feature(type_alias_impl_trait)]

Expand Down
124 changes: 99 additions & 25 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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")
}
}
Expand Down Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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<H: Hasher>(&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<S: Encoder> Encodable<S> 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<D: Decoder> Decodable<D> 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 {
Expand All @@ -2436,20 +2503,28 @@ impl TargetTriple {
/// Creates a target triple from the passed target path.
pub fn from_path(path: &Path) -> Result<Self, io::Error> {
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.
///
/// 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,
}
}

Expand All @@ -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)
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/future/into_future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
22 changes: 22 additions & 0 deletions library/core/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,28 @@ pub const fn replace<T>(dest: &mut T, src: T) -> T {
#[cfg_attr(not(test), rustc_diagnostic_item = "mem_drop")]
pub fn drop<T>(_x: T) {}

/// Bitwise-copies a value.
///
/// This function is not magic; it is literally defined as
/// ```
/// pub fn copy<T: 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<T: Copy>(x: &T) -> T {
*x
}

/// Interprets `src` as having type `&U`, and then reads `src` without moving
/// the contained value.
///
Expand Down
8 changes: 6 additions & 2 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2317,10 +2317,14 @@ impl AsInnerMut<fs_imp::DirBuilder> 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"));
Expand All @@ -2330,7 +2334,7 @@ impl AsInnerMut<fs_imp::DirBuilder> 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<P: AsRef<Path>>(path: P) -> io::Result<bool> {
fs_imp::try_exists(path.as_ref())
Expand Down
17 changes: 11 additions & 6 deletions library/std/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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]
Expand All @@ -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<bool> {
fs::try_exists(self)
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down