diff options
73 files changed, 1307 insertions, 524 deletions
diff --git a/Cargo.lock b/Cargo.lock index 64a7cec030a..146070cf167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -244,47 +245,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] -name = "askama" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" -dependencies = [ - "askama_derive", - "askama_escape", -] - -[[package]] -name = "askama_derive" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" -dependencies = [ - "askama_parser", - "basic-toml", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "serde", - "syn 2.0.67", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "askama_parser" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" -dependencies = [ - "nom", -] - -[[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2662,6 +2622,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] +name = "once_map" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa7085055bbe9c8edbd982048dbcf8181794d4a81cb04a11931673e63cc18dc6" +dependencies = [ + "ahash", + "hashbrown", + "parking_lot", + "stable_deref_trait", +] + +[[package]] name = "opener" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3314,6 +3286,41 @@ dependencies = [ ] [[package]] +name = "rinja" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d47a46d7729e891c8accf260e9daa02ae6d570aa2a94fb1fb27eb5364a2323" +dependencies = [ + "rinja_derive", +] + +[[package]] +name = "rinja_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dae9afe59d58ed8d988d67d1945f3638125d2fd2104058399382e11bd3ea2a" +dependencies = [ + "basic-toml", + "mime", + "mime_guess", + "once_map", + "proc-macro2", + "quote", + "rinja_parser", + "serde", + "syn 2.0.67", +] + +[[package]] +name = "rinja_parser" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1771c78cd5d3b1646ef8d8f2ed100db936e8b291d3cc06e92a339ff346858c" +dependencies = [ + "nom", +] + +[[package]] name = "rls" version = "2.0.0" dependencies = [ @@ -4799,7 +4806,6 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama", "base64", "expect-test", "indexmap", @@ -4807,6 +4813,7 @@ dependencies = [ "minifier", "pulldown-cmark 0.9.6", "regex", + "rinja", "rustdoc-json-types", "serde", "serde_json", diff --git a/compiler/rustc_error_codes/src/error_codes/E0373.md b/compiler/rustc_error_codes/src/error_codes/E0373.md index effa597aad9..d4d26007aa5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0373.md +++ b/compiler/rustc_error_codes/src/error_codes/E0373.md @@ -70,4 +70,4 @@ fn spawn<F: Future + Send + 'static>(future: F) { Similarly to closures, `async` blocks are not executed immediately and may capture closed-over data by reference. For more information, see -https://rust-lang.github.io/async-book/03_async_await/01_chapter.html. +<https://rust-lang.github.io/async-book/03_async_await/01_chapter.html>. diff --git a/compiler/rustc_error_codes/src/error_codes/E0378.md b/compiler/rustc_error_codes/src/error_codes/E0378.md index c6fe997f3dc..7d939b99b04 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0378.md +++ b/compiler/rustc_error_codes/src/error_codes/E0378.md @@ -20,7 +20,7 @@ where The `DispatchFromDyn` trait currently can only be implemented for builtin pointer types and structs that are newtype wrappers around them -— that is, the struct must have only one field (except for`PhantomData`), +— that is, the struct must have only one field (except for `PhantomData`), and that field must itself implement `DispatchFromDyn`. ``` diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2086d4030f9..2a850d9303c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,6 +15,7 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![feature(extract_if)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index 67e4963fddf..69e7120e714 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -10,15 +10,15 @@ const CBK: &[u8] = b"```"; const CIL: &[u8] = b"`"; const CMT_E: &[u8] = b"-->"; const CMT_S: &[u8] = b"<!--"; -const EMP: &[u8] = b"_"; +const EMP_U: &[u8] = b"_"; +const EMP_A: &[u8] = b"*"; const HDG: &[u8] = b"#"; const LNK_CHARS: &str = "$-_.+!*'()/&?=:%"; const LNK_E: &[u8] = b"]"; const LNK_S: &[u8] = b"["; -const STG: &[u8] = b"**"; +const STG_U: &[u8] = b"__"; +const STG_A: &[u8] = b"**"; const STK: &[u8] = b"~~"; -const UL1: &[u8] = b"* "; -const UL2: &[u8] = b"- "; /// Pattern replacements const REPLACEMENTS: &[(&str, &str)] = &[ @@ -100,22 +100,29 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> { }; let res: ParseResult<'_> = match (top_blk, prev) { - (_, Newline | Whitespace) if loop_buf.starts_with(CMT_S) => { + _ if loop_buf.starts_with(CMT_S) => { parse_simple_pat(loop_buf, CMT_S, CMT_E, Po::TrimNoEsc, MdTree::Comment) } (true, Newline) if loop_buf.starts_with(CBK) => Some(parse_codeblock(loop_buf)), - (_, Newline | Whitespace) if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf), + _ if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf), (true, Newline | Whitespace) if loop_buf.starts_with(HDG) => parse_heading(loop_buf), (true, Newline) if loop_buf.starts_with(BRK) => { Some((MdTree::HorizontalRule, parse_to_newline(loop_buf).1)) } - (_, Newline | Whitespace) if loop_buf.starts_with(EMP) => { - parse_simple_pat(loop_buf, EMP, EMP, Po::None, MdTree::Emphasis) + (_, Newline) if unordered_list_start(loop_buf) => Some(parse_unordered_li(loop_buf)), + (_, Newline | Whitespace) if loop_buf.starts_with(STG_U) => { + parse_simple_pat(loop_buf, STG_U, STG_U, Po::None, MdTree::Strong) } - (_, Newline | Whitespace) if loop_buf.starts_with(STG) => { - parse_simple_pat(loop_buf, STG, STG, Po::None, MdTree::Strong) + _ if loop_buf.starts_with(STG_A) => { + parse_simple_pat(loop_buf, STG_A, STG_A, Po::None, MdTree::Strong) } - (_, Newline | Whitespace) if loop_buf.starts_with(STK) => { + (_, Newline | Whitespace) if loop_buf.starts_with(EMP_U) => { + parse_simple_pat(loop_buf, EMP_U, EMP_U, Po::None, MdTree::Emphasis) + } + _ if loop_buf.starts_with(EMP_A) => { + parse_simple_pat(loop_buf, EMP_A, EMP_A, Po::None, MdTree::Emphasis) + } + _ if loop_buf.starts_with(STK) => { parse_simple_pat(loop_buf, STK, STK, Po::None, MdTree::Strikethrough) } (_, Newline | Whitespace) if loop_buf.starts_with(ANC_S) => { @@ -130,11 +137,8 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> { _ => None, } } - (_, Newline) if (loop_buf.starts_with(UL1) || loop_buf.starts_with(UL2)) => { - Some(parse_unordered_li(loop_buf)) - } (_, Newline) if ord_list_start(loop_buf).is_some() => Some(parse_ordered_li(loop_buf)), - (_, Newline | Whitespace) if loop_buf.starts_with(LNK_S) => { + _ if loop_buf.starts_with(LNK_S) => { parse_any_link(loop_buf, top_blk && prev == Prev::Newline) } (_, Escape | _) => None, @@ -251,7 +255,6 @@ fn parse_heading(buf: &[u8]) -> ParseResult<'_> { /// Bulleted list fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> { - debug_assert!(buf.starts_with(b"* ") || buf.starts_with(b"- ")); let (txt, rest) = get_indented_section(&buf[2..]); let ctx = Context { top_block: false, prev: Prev::Whitespace }; let stream = parse_recursive(trim_ascii_start(txt), ctx); @@ -267,25 +270,28 @@ fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> { (MdTree::OrderedListItem(num, stream), rest) } -/// Find first line that isn't empty or doesn't start with whitespace, that will -/// be our contents fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) { - let mut end = buf.len(); - for (idx, window) in buf.windows(2).enumerate() { - let &[ch, next_ch] = window else { unreachable!("always 2 elements") }; - if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' { - // End of stream - end = buf.len().saturating_sub(1); - break; - } else if ch == b'\n' && (!next_ch.is_ascii_whitespace() || next_ch == b'\n') { - end = idx; - break; + let mut lines = buf.split(|&byte| byte == b'\n'); + let mut end = lines.next().map_or(0, |line| line.len()); + for line in lines { + if let Some(first) = line.first() { + if unordered_list_start(line) || !first.is_ascii_whitespace() { + break; + } } + end += line.len() + 1; } (&buf[..end], &buf[end..]) } +fn unordered_list_start(mut buf: &[u8]) -> bool { + while let [b' ', rest @ ..] = buf { + buf = rest; + } + matches!(buf, [b'*' | b'-', b' ', ..]) +} + /// Verify a valid ordered list start (e.g. `1.`) and parse it. Returns the /// parsed number and offset of character after the dot. fn ord_list_start(buf: &[u8]) -> Option<(u16, usize)> { diff --git a/compiler/rustc_errors/src/markdown/tests/parse.rs b/compiler/rustc_errors/src/markdown/tests/parse.rs index e39e8c89b35..e2e3f354ff6 100644 --- a/compiler/rustc_errors/src/markdown/tests/parse.rs +++ b/compiler/rustc_errors/src/markdown/tests/parse.rs @@ -4,13 +4,13 @@ use ParseOpt as PO; #[test] fn test_parse_simple() { let buf = "**abcd** rest"; - let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap(); + let (t, r) = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong).unwrap(); assert_eq!(t, MdTree::Strong("abcd")); assert_eq!(r, b" rest"); // Escaping should fail let buf = r"**abcd\** rest"; - let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong); + let res = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong); assert!(res.is_none()); } @@ -141,12 +141,12 @@ fn test_indented_section() { assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind"); let (txt, rest) = get_indented_section(IND2.as_bytes()); - assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2"); - assert_eq!(str::from_utf8(rest).unwrap(), "\n"); + assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2\n"); + assert_eq!(str::from_utf8(rest).unwrap(), ""); let (txt, rest) = get_indented_section(IND3.as_bytes()); - assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2"); - assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind"); + assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2\n"); + assert_eq!(str::from_utf8(rest).unwrap(), "\nnot ind"); } const HBT: &str = r"# Heading @@ -310,3 +310,56 @@ fn test_code_at_start() { let res = entrypoint(CODE_STARTLINE); assert_eq!(res, expected); } + +#[test] +fn test_code_in_parens() { + let expected = + vec![MdTree::PlainText("("), MdTree::CodeInline("Foo"), MdTree::PlainText(")")].into(); + let res = entrypoint("(`Foo`)"); + assert_eq!(res, expected); +} + +const LIST_WITH_SPACE: &str = " +para + * l1 + * l2 +"; + +#[test] +fn test_list_with_space() { + let expected = vec![ + MdTree::PlainText("para"), + MdTree::ParagraphBreak, + MdTree::UnorderedListItem(vec![MdTree::PlainText("l1")].into()), + MdTree::LineBreak, + MdTree::UnorderedListItem(vec![MdTree::PlainText("l2")].into()), + ] + .into(); + let res = entrypoint(LIST_WITH_SPACE); + assert_eq!(res, expected); +} + +const SNAKE_CASE: &str = " +foo*bar* +foo**bar** +foo_bar_ +foo__bar__ +"; + +#[test] +fn test_snake_case() { + let expected = vec![ + MdTree::PlainText("foo"), + MdTree::Emphasis("bar"), + MdTree::PlainText(" "), + MdTree::PlainText("foo"), + MdTree::Strong("bar"), + MdTree::PlainText(" "), + MdTree::PlainText("foo_bar_"), + MdTree::PlainText(" "), + MdTree::PlainText("foo__bar__"), + ] + .into(); + let res = entrypoint(SNAKE_CASE); + assert_eq!(res, expected); +} diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 341d533492d..65229722771 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -495,6 +495,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.span_label(self.span, "invalid cast"); } + fcx.suggest_no_capture_closure(&mut err, self.cast_ty, self.expr_ty); self.try_suggest_collection_to_bool(fcx, &mut err); err.emit(); diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index af01db19139..fe1ff241395 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -484,7 +484,7 @@ impl<T: Ord, A: Allocator> BinaryHeap<T, A> { /// heap.push(4); /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "112353")] + #[rustc_const_unstable(feature = "const_binary_heap_new_in", issue = "125961")] #[must_use] pub const fn new_in(alloc: A) -> BinaryHeap<T, A> { BinaryHeap { data: Vec::new_in(alloc) } diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 3bb808a6c73..94053ef83a0 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -269,7 +269,7 @@ impl str { without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String { + pub fn replace<P: Pattern>(&self, from: P, to: &str) -> String { let mut result = String::new(); let mut last_end = 0; for (start, part) in self.match_indices(from) { @@ -309,7 +309,7 @@ impl str { #[must_use = "this returns the replaced string as a new allocation, \ without modifying the original"] #[stable(feature = "str_replacen", since = "1.16.0")] - pub fn replacen<'a, P: Pattern<'a>>(&'a self, pat: P, to: &str, count: usize) -> String { + pub fn replacen<P: Pattern>(&self, pat: P, to: &str, count: usize) -> String { // Hope to reduce the times of re-allocation let mut result = String::with_capacity(32); let mut last_end = 0; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 07ffd3e1519..0ff66167a46 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1497,10 +1497,7 @@ impl String { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")] - pub fn remove_matches<'a, P>(&'a mut self, pat: P) - where - P: for<'x> Pattern<'x>, - { + pub fn remove_matches<P: Pattern>(&mut self, pat: P) { use core::str::pattern::Searcher; let rejections = { @@ -2288,35 +2285,41 @@ impl<'a> Extend<Cow<'a, str>> for String { reason = "API not fully fleshed out and ready to be stabilized", issue = "27721" )] -impl<'a, 'b> Pattern<'a> for &'b String { - type Searcher = <&'b str as Pattern<'a>>::Searcher; +impl<'b> Pattern for &'b String { + type Searcher<'a> = <&'b str as Pattern>::Searcher<'a>; - fn into_searcher(self, haystack: &'a str) -> <&'b str as Pattern<'a>>::Searcher { + fn into_searcher(self, haystack: &str) -> <&'b str as Pattern>::Searcher<'_> { self[..].into_searcher(haystack) } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { self[..].is_contained_in(haystack) } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { self[..].is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { self[..].strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool { + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + where + Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { self[..].is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&str> + where + Self::Searcher<'a>: core::str::pattern::ReverseSearcher<'a>, + { self[..].strip_suffix_of(haystack) } } diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 0078f5eaa3d..de5d3991c91 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1927,12 +1927,10 @@ mod pattern { } } - fn cmp_search_to_vec<'a>( - rev: bool, - pat: impl Pattern<'a, Searcher: ReverseSearcher<'a>>, - haystack: &'a str, - right: Vec<SearchStep>, - ) { + fn cmp_search_to_vec<P>(rev: bool, pat: P, haystack: &str, right: Vec<SearchStep>) + where + P: for<'a> Pattern<Searcher<'a>: ReverseSearcher<'a>>, + { let mut searcher = pat.into_searcher(haystack); let mut v = vec![]; loop { @@ -2191,9 +2189,9 @@ generate_iterator_test! { fn different_str_pattern_forwarding_lifetimes() { use std::str::pattern::Pattern; - fn foo<'a, P>(p: P) + fn foo<P>(p: P) where - for<'b> &'b P: Pattern<'a>, + for<'b> &'b P: Pattern, { for _ in 0..3 { "asdf".find(&p); diff --git a/library/core/src/primitive.rs b/library/core/src/primitive.rs index e20b2c5c938..81a72118614 100644 --- a/library/core/src/primitive.rs +++ b/library/core/src/primitive.rs @@ -12,7 +12,7 @@ //! const SOME_PROPERTY: bool = true; //! } //! -//! # trait QueryId { const SOME_PROPERTY: core::primitive::bool; } +//! # trait QueryId { const SOME_PROPERTY: ::core::primitive::bool; } //! ``` //! //! Note that the `SOME_PROPERTY` associated constant would not compile, as its @@ -25,11 +25,17 @@ //! pub struct bool; //! //! impl QueryId for bool { -//! const SOME_PROPERTY: core::primitive::bool = true; +//! const SOME_PROPERTY: ::core::primitive::bool = true; //! } //! -//! # trait QueryId { const SOME_PROPERTY: core::primitive::bool; } +//! # trait QueryId { const SOME_PROPERTY: ::core::primitive::bool; } //! ``` +//! +//! We also used `::core` instead of `core`, because `core` can be +//! shadowed, too. Paths, starting with `::`, are searched in +//! the [extern prelude] since Edition 2018. +//! +//! [extern prelude]: https://doc.rust-lang.org/nightly/reference/names/preludes.html#extern-prelude #[stable(feature = "core_primitive", since = "1.43.0")] pub use bool; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 68508e85f8e..6d3e625bef4 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -4522,6 +4522,121 @@ impl<T> [T] { // are disjunct and in bounds. unsafe { Ok(self.get_many_unchecked_mut(indices)) } } + + /// Returns the index that an element reference points to. + /// + /// Returns `None` if `element` does not point within the slice or if it points between elements. + /// + /// This method is useful for extending slice iterators like [`slice::split`]. + /// + /// Note that this uses pointer arithmetic and **does not compare elements**. + /// To find the index of an element via comparison, use + /// [`.iter().position()`](crate::iter::Iterator::position) instead. + /// + /// # Panics + /// Panics if `T` is zero-sized. + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(substr_range)] + /// + /// let nums: &[u32] = &[1, 7, 1, 1]; + /// let num = &nums[2]; + /// + /// assert_eq!(num, &1); + /// assert_eq!(nums.elem_offset(num), Some(2)); + /// ``` + /// Returning `None` with an in-between element: + /// ``` + /// #![feature(substr_range)] + /// + /// let arr: &[[u32; 2]] = &[[0, 1], [2, 3]]; + /// let flat_arr: &[u32] = arr.as_flattened(); + /// + /// let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap(); + /// let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap(); + /// + /// assert_eq!(ok_elm, &[0, 1]); + /// assert_eq!(weird_elm, &[1, 2]); + /// + /// assert_eq!(arr.elem_offset(ok_elm), Some(0)); // Points to element 0 + /// assert_eq!(arr.elem_offset(weird_elm), None); // Points between element 0 and 1 + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn elem_offset(&self, element: &T) -> Option<usize> { + if T::IS_ZST { + panic!("elements are zero-sized"); + } + + let self_start = self.as_ptr() as usize; + let elem_start = element as *const T as usize; + + let byte_offset = elem_start.wrapping_sub(self_start); + + if byte_offset % mem::size_of::<T>() != 0 { + return None; + } + + let offset = byte_offset / mem::size_of::<T>(); + + if offset < self.len() { Some(offset) } else { None } + } + + /// Returns the range of indices that a subslice points to. + /// + /// Returns `None` if `subslice` does not point within the slice or if it points between elements. + /// + /// This method **does not compare elements**. Instead, this method finds the location in the slice that + /// `subslice` was obtained from. To find the index of a subslice via comparison, instead use + /// [`.windows()`](slice::windows)[`.position()`](crate::iter::Iterator::position). + /// + /// This method is useful for extending slice iterators like [`slice::split`]. + /// + /// Note that this may return a false positive (either `Some(0..0)` or `Some(self.len()..self.len())`) + /// if `subslice` has a length of zero and points to the beginning or end of another, separate, slice. + /// + /// # Panics + /// Panics if `T` is zero-sized. + /// + /// # Examples + /// Basic usage: + /// ``` + /// #![feature(substr_range)] + /// + /// let nums = &[0, 5, 10, 0, 0, 5]; + /// + /// let mut iter = nums + /// .split(|t| *t == 0) + /// .map(|n| nums.subslice_range(n).unwrap()); + /// + /// assert_eq!(iter.next(), Some(0..0)); + /// assert_eq!(iter.next(), Some(1..3)); + /// assert_eq!(iter.next(), Some(4..4)); + /// assert_eq!(iter.next(), Some(5..6)); + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>> { + if T::IS_ZST { + panic!("elements are zero-sized"); + } + + let self_start = self.as_ptr() as usize; + let subslice_start = subslice.as_ptr() as usize; + + let byte_start = subslice_start.wrapping_sub(self_start); + + if byte_start % core::mem::size_of::<T>() != 0 { + return None; + } + + let start = byte_start / core::mem::size_of::<T>(); + let end = start.wrapping_add(subslice.len()); + + if start <= self.len() && end <= self.len() { Some(start..end) } else { None } + } } impl<T, const N: usize> [[T; N]] { diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 19627f28e64..5845e3b5481 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -411,7 +411,7 @@ macro_rules! derive_pattern_clone { (clone $t:ident with |$s:ident| $e:expr) => { impl<'a, P> Clone for $t<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern<Searcher<'a>: Clone>, { fn clone(&self) -> Self { let $s = self; @@ -424,7 +424,7 @@ macro_rules! derive_pattern_clone { /// This macro generates two public iterator structs /// wrapping a private internal one that makes use of the `Pattern` API. /// -/// For all patterns `P: Pattern<'a>` the following items will be +/// For all patterns `P: Pattern` the following items will be /// generated (generics omitted): /// /// struct $forward_iterator($internal_iterator); @@ -484,12 +484,12 @@ macro_rules! generate_pattern_iterators { } => { $(#[$forward_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $forward_iterator<'a, P: Pattern<'a>>(pub(super) $internal_iterator<'a, P>); + pub struct $forward_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($forward_iterator)) @@ -499,7 +499,7 @@ macro_rules! generate_pattern_iterators { } $(#[$common_stability_attribute])* - impl<'a, P: Pattern<'a>> Iterator for $forward_iterator<'a, P> { + impl<'a, P: Pattern> Iterator for $forward_iterator<'a, P> { type Item = $iterty; #[inline] @@ -511,7 +511,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern<Searcher<'a>: Clone>, { fn clone(&self) -> Self { $forward_iterator(self.0.clone()) @@ -520,12 +520,12 @@ macro_rules! generate_pattern_iterators { $(#[$reverse_iterator_attribute])* $(#[$common_stability_attribute])* - pub struct $reverse_iterator<'a, P: Pattern<'a>>(pub(super) $internal_iterator<'a, P>); + pub struct $reverse_iterator<'a, P: Pattern>(pub(super) $internal_iterator<'a, P>); $(#[$common_stability_attribute])* impl<'a, P> fmt::Debug for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(stringify!($reverse_iterator)) @@ -537,7 +537,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Iterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + P: Pattern<Searcher<'a>: ReverseSearcher<'a>>, { type Item = $iterty; @@ -550,7 +550,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> Clone for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: Clone>, + P: Pattern<Searcher<'a>: Clone>, { fn clone(&self) -> Self { $reverse_iterator(self.0.clone()) @@ -558,12 +558,12 @@ macro_rules! generate_pattern_iterators { } #[stable(feature = "fused", since = "1.26.0")] - impl<'a, P: Pattern<'a>> FusedIterator for $forward_iterator<'a, P> {} + impl<'a, P: Pattern> FusedIterator for $forward_iterator<'a, P> {} #[stable(feature = "fused", since = "1.26.0")] impl<'a, P> FusedIterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + P: Pattern<Searcher<'a>: ReverseSearcher<'a>>, {} generate_pattern_iterators!($($t)* with $(#[$common_stability_attribute])*, @@ -578,7 +578,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $forward_iterator<'a, P> where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + P: Pattern<Searcher<'a>: DoubleEndedSearcher<'a>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -589,7 +589,7 @@ macro_rules! generate_pattern_iterators { $(#[$common_stability_attribute])* impl<'a, P> DoubleEndedIterator for $reverse_iterator<'a, P> where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + P: Pattern<Searcher<'a>: DoubleEndedSearcher<'a>>, { #[inline] fn next_back(&mut self) -> Option<$iterty> { @@ -609,17 +609,17 @@ derive_pattern_clone! { with |s| SplitInternal { matcher: s.matcher.clone(), ..*s } } -pub(super) struct SplitInternal<'a, P: Pattern<'a>> { +pub(super) struct SplitInternal<'a, P: Pattern> { pub(super) start: usize, pub(super) end: usize, - pub(super) matcher: P::Searcher, + pub(super) matcher: P::Searcher<'a>, pub(super) allow_trailing_empty: bool, pub(super) finished: bool, } impl<'a, P> fmt::Debug for SplitInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInternal") @@ -632,7 +632,7 @@ where } } -impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { +impl<'a, P: Pattern> SplitInternal<'a, P> { #[inline] fn get_end(&mut self) -> Option<&'a str> { if !self.finished { @@ -689,7 +689,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { if self.finished { return None; @@ -726,7 +726,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { #[inline] fn next_back_inclusive(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { if self.finished { return None; @@ -796,7 +796,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern<'a>> Split<'a, P> { +impl<'a, P: Pattern> Split<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -819,7 +819,7 @@ impl<'a, P: Pattern<'a>> Split<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplit<'a, P> { +impl<'a, P: Pattern> RSplit<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -860,7 +860,7 @@ generate_pattern_iterators! { delegate double ended; } -impl<'a, P: Pattern<'a>> SplitTerminator<'a, P> { +impl<'a, P: Pattern> SplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -883,7 +883,7 @@ impl<'a, P: Pattern<'a>> SplitTerminator<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplitTerminator<'a, P> { +impl<'a, P: Pattern> RSplitTerminator<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -911,7 +911,7 @@ derive_pattern_clone! { with |s| SplitNInternal { iter: s.iter.clone(), ..*s } } -pub(super) struct SplitNInternal<'a, P: Pattern<'a>> { +pub(super) struct SplitNInternal<'a, P: Pattern> { pub(super) iter: SplitInternal<'a, P>, /// The number of splits remaining pub(super) count: usize, @@ -919,7 +919,7 @@ pub(super) struct SplitNInternal<'a, P: Pattern<'a>> { impl<'a, P> fmt::Debug for SplitNInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitNInternal") @@ -929,7 +929,7 @@ where } } -impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { +impl<'a, P: Pattern> SplitNInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { match self.count { @@ -948,7 +948,7 @@ impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { match self.count { 0 => None, @@ -987,7 +987,7 @@ generate_pattern_iterators! { delegate single ended; } -impl<'a, P: Pattern<'a>> SplitN<'a, P> { +impl<'a, P: Pattern> SplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1010,7 +1010,7 @@ impl<'a, P: Pattern<'a>> SplitN<'a, P> { } } -impl<'a, P: Pattern<'a>> RSplitN<'a, P> { +impl<'a, P: Pattern> RSplitN<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. @@ -1038,18 +1038,18 @@ derive_pattern_clone! { with |s| MatchIndicesInternal(s.0.clone()) } -pub(super) struct MatchIndicesInternal<'a, P: Pattern<'a>>(pub(super) P::Searcher); +pub(super) struct MatchIndicesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchIndicesInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchIndicesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { +impl<'a, P: Pattern> MatchIndicesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<(usize, &'a str)> { self.0 @@ -1061,7 +1061,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<(usize, &'a str)> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { self.0 .next_match_back() @@ -1093,18 +1093,18 @@ derive_pattern_clone! { with |s| MatchesInternal(s.0.clone()) } -pub(super) struct MatchesInternal<'a, P: Pattern<'a>>(pub(super) P::Searcher); +pub(super) struct MatchesInternal<'a, P: Pattern>(pub(super) P::Searcher<'a>); impl<'a, P> fmt::Debug for MatchesInternal<'a, P> where - P: Pattern<'a, Searcher: fmt::Debug>, + P: Pattern<Searcher<'a>: fmt::Debug>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("MatchesInternal").field(&self.0).finish() } } -impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { +impl<'a, P: Pattern> MatchesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<&'a str> { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. @@ -1117,7 +1117,7 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { #[inline] fn next_back(&mut self) -> Option<&'a str> where - P::Searcher: ReverseSearcher<'a>, + P::Searcher<'a>: ReverseSearcher<'a>, { // SAFETY: `Searcher` guarantees that `start` and `end` lie on unicode boundaries. self.0.next_match_back().map(|(a, b)| unsafe { @@ -1288,7 +1288,7 @@ pub struct SplitAsciiWhitespace<'a> { /// /// [`split_inclusive`]: str::split_inclusive #[stable(feature = "split_inclusive", since = "1.51.0")] -pub struct SplitInclusive<'a, P: Pattern<'a>>(pub(super) SplitInternal<'a, P>); +pub struct SplitInclusive<'a, P: Pattern>(pub(super) SplitInternal<'a, P>); #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> Iterator for SplitWhitespace<'a> { @@ -1410,7 +1410,7 @@ impl<'a> SplitAsciiWhitespace<'a> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { +impl<'a, P: Pattern> Iterator for SplitInclusive<'a, P> { type Item = &'a str; #[inline] @@ -1420,7 +1420,7 @@ impl<'a, P: Pattern<'a>> Iterator for SplitInclusive<'a, P> { } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { +impl<'a, P: Pattern<Searcher<'a>: fmt::Debug>> fmt::Debug for SplitInclusive<'a, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SplitInclusive").field("0", &self.0).finish() } @@ -1428,14 +1428,14 @@ impl<'a, P: Pattern<'a, Searcher: fmt::Debug>> fmt::Debug for SplitInclusive<'a, // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: Clone>> Clone for SplitInclusive<'a, P> { +impl<'a, P: Pattern<Searcher<'a>: Clone>> Clone for SplitInclusive<'a, P> { fn clone(&self) -> Self { SplitInclusive(self.0.clone()) } } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>> DoubleEndedIterator +impl<'a, P: Pattern<Searcher<'a>: DoubleEndedSearcher<'a>>> DoubleEndedIterator for SplitInclusive<'a, P> { #[inline] @@ -1445,9 +1445,9 @@ impl<'a, P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>> DoubleEndedIterator } #[stable(feature = "split_inclusive", since = "1.51.0")] -impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} +impl<'a, P: Pattern> FusedIterator for SplitInclusive<'a, P> {} -impl<'a, P: Pattern<'a>> SplitInclusive<'a, P> { +impl<'a, P: Pattern> SplitInclusive<'a, P> { /// Returns remainder of the split string. /// /// If the iterator is empty, returns `None`. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 68310938043..6cd029f7436 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -19,6 +19,7 @@ use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; use crate::ascii; use crate::char::{self, EscapeDebugExtArgs}; use crate::mem; +use crate::ops::Range; use crate::slice::{self, SliceIndex}; pub mod pattern; @@ -1137,7 +1138,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pub fn contains<P: Pattern>(&self, pat: P) -> bool { pat.is_contained_in(self) } @@ -1174,7 +1175,7 @@ impl str { /// assert!(bananas.starts_with(&['a', 'b', 'c', 'd'])); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + pub fn starts_with<P: Pattern>(&self, pat: P) -> bool { pat.is_prefix_of(self) } @@ -1198,9 +1199,9 @@ impl str { /// assert!(!bananas.ends_with("nana")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with<'a, P>(&'a self, pat: P) -> bool + pub fn ends_with<P: Pattern>(&self, pat: P) -> bool where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { pat.is_suffix_of(self) } @@ -1249,7 +1250,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option<usize> { + pub fn find<P: Pattern>(&self, pat: P) -> Option<usize> { pat.into_searcher(self).next_match().map(|(i, _)| i) } @@ -1295,9 +1296,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rfind<'a, P>(&'a self, pat: P) -> Option<usize> + pub fn rfind<P: Pattern>(&self, pat: P) -> Option<usize> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { pat.into_searcher(self).next_match_back().map(|(i, _)| i) } @@ -1417,7 +1418,7 @@ impl str { /// [`split_whitespace`]: str::split_whitespace #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { + pub fn split<P: Pattern>(&self, pat: P) -> Split<'_, P> { Split(SplitInternal { start: 0, end: self.len(), @@ -1457,7 +1458,7 @@ impl str { /// ``` #[stable(feature = "split_inclusive", since = "1.51.0")] #[inline] - pub fn split_inclusive<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitInclusive<'a, P> { + pub fn split_inclusive<P: Pattern>(&self, pat: P) -> SplitInclusive<'_, P> { SplitInclusive(SplitInternal { start: 0, end: self.len(), @@ -1512,9 +1513,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit<'a, P>(&'a self, pat: P) -> RSplit<'a, P> + pub fn rsplit<P: Pattern>(&self, pat: P) -> RSplit<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplit(self.split(pat).0) } @@ -1561,7 +1562,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { + pub fn split_terminator<P: Pattern>(&self, pat: P) -> SplitTerminator<'_, P> { SplitTerminator(SplitInternal { allow_trailing_empty: false, ..self.split(pat).0 }) } @@ -1607,9 +1608,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplit_terminator<'a, P>(&'a self, pat: P) -> RSplitTerminator<'a, P> + pub fn rsplit_terminator<P: Pattern>(&self, pat: P) -> RSplitTerminator<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplitTerminator(self.split_terminator(pat).0) } @@ -1662,7 +1663,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { + pub fn splitn<P: Pattern>(&self, n: usize, pat: P) -> SplitN<'_, P> { SplitN(SplitNInternal { iter: self.split(pat).0, count: n }) } @@ -1711,9 +1712,9 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn rsplitn<'a, P>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> + pub fn rsplitn<P: Pattern>(&self, n: usize, pat: P) -> RSplitN<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RSplitN(self.splitn(n, pat).0) } @@ -1731,7 +1732,7 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn split_once<'a, P: Pattern<'a>>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> { + pub fn split_once<P: Pattern>(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> { let (start, end) = delimiter.into_searcher(self).next_match()?; // SAFETY: `Searcher` is known to return valid indices. unsafe { Some((self.get_unchecked(..start), self.get_unchecked(end..))) } @@ -1749,9 +1750,9 @@ impl str { /// ``` #[stable(feature = "str_split_once", since = "1.52.0")] #[inline] - pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> + pub fn rsplit_once<P: Pattern>(&self, delimiter: P) -> Option<(&'_ str, &'_ str)> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { let (start, end) = delimiter.into_searcher(self).next_match_back()?; // SAFETY: `Searcher` is known to return valid indices. @@ -1789,7 +1790,7 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { + pub fn matches<P: Pattern>(&self, pat: P) -> Matches<'_, P> { Matches(MatchesInternal(pat.into_searcher(self))) } @@ -1823,9 +1824,9 @@ impl str { /// ``` #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - pub fn rmatches<'a, P>(&'a self, pat: P) -> RMatches<'a, P> + pub fn rmatches<P: Pattern>(&self, pat: P) -> RMatches<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RMatches(self.matches(pat).0) } @@ -1867,7 +1868,7 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { + pub fn match_indices<P: Pattern>(&self, pat: P) -> MatchIndices<'_, P> { MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } @@ -1907,9 +1908,9 @@ impl str { /// ``` #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - pub fn rmatch_indices<'a, P>(&'a self, pat: P) -> RMatchIndices<'a, P> + pub fn rmatch_indices<P: Pattern>(&self, pat: P) -> RMatchIndices<'_, P> where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { RMatchIndices(self.match_indices(pat).0) } @@ -2122,9 +2123,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_matches<P: Pattern>(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: DoubleEndedSearcher<'a>>, + for<'a> P::Searcher<'a>: DoubleEndedSearcher<'a>, { let mut i = 0; let mut j = 0; @@ -2169,7 +2170,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_start_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + pub fn trim_start_matches<P: Pattern>(&self, pat: P) -> &str { let mut i = self.len(); let mut matcher = pat.into_searcher(self); if let Some((a, _)) = matcher.next_reject() { @@ -2202,7 +2203,7 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { + pub fn strip_prefix<P: Pattern>(&self, prefix: P) -> Option<&str> { prefix.strip_prefix_of(self) } @@ -2229,10 +2230,9 @@ impl str { #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[stable(feature = "str_strip", since = "1.45.0")] - pub fn strip_suffix<'a, P>(&'a self, suffix: P) -> Option<&'a str> + pub fn strip_suffix<P: Pattern>(&self, suffix: P) -> Option<&str> where - P: Pattern<'a>, - <P as Pattern<'a>>::Searcher: ReverseSearcher<'a>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { suffix.strip_suffix_of(self) } @@ -2273,9 +2273,9 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] - pub fn trim_end_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_end_matches<P: Pattern>(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { let mut j = 0; let mut matcher = pat.into_searcher(self); @@ -2317,7 +2317,7 @@ impl str { note = "superseded by `trim_start_matches`", suggestion = "trim_start_matches" )] - pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + pub fn trim_left_matches<P: Pattern>(&self, pat: P) -> &str { self.trim_start_matches(pat) } @@ -2360,9 +2360,9 @@ impl str { note = "superseded by `trim_end_matches`", suggestion = "trim_end_matches" )] - pub fn trim_right_matches<'a, P>(&'a self, pat: P) -> &'a str + pub fn trim_right_matches<P: Pattern>(&self, pat: P) -> &str where - P: Pattern<'a, Searcher: ReverseSearcher<'a>>, + for<'a> P::Searcher<'a>: ReverseSearcher<'a>, { self.trim_end_matches(pat) } @@ -2721,6 +2721,39 @@ impl str { pub fn escape_unicode(&self) -> EscapeUnicode<'_> { EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) } } + + /// Returns the range that a substring points to. + /// + /// Returns `None` if `substr` does not point within `self`. + /// + /// Unlike [`str::find`], **this does not search through the string**. + /// Instead, it uses pointer arithmetic to find where in the string + /// `substr` is derived from. + /// + /// This is useful for extending [`str::split`] and similar methods. + /// + /// Note that this method may return false positives (typically either + /// `Some(0..0)` or `Some(self.len()..self.len())`) if `substr` is a + /// zero-length `str` that points at the beginning or end of another, + /// independent, `str`. + /// + /// # Examples + /// ``` + /// #![feature(substr_range)] + /// + /// let data = "a, b, b, a"; + /// let mut iter = data.split(", ").map(|s| data.substr_range(s).unwrap()); + /// + /// assert_eq!(iter.next(), Some(0..1)); + /// assert_eq!(iter.next(), Some(3..4)); + /// assert_eq!(iter.next(), Some(6..7)); + /// assert_eq!(iter.next(), Some(9..10)); + /// ``` + #[must_use] + #[unstable(feature = "substr_range", issue = "126769")] + pub fn substr_range(&self, substr: &str) -> Option<Range<usize>> { + self.as_bytes().subslice_range(substr.as_bytes()) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 8988229be2e..33a5d45e445 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -48,8 +48,8 @@ use crate::slice::memchr; /// A string pattern. /// -/// A `Pattern<'a>` expresses that the implementing type -/// can be used as a string pattern for searching in a [`&'a str`][str]. +/// A `Pattern` expresses that the implementing type +/// can be used as a string pattern for searching in a [`&str`][str]. /// /// For example, both `'a'` and `"aa"` are patterns that /// would match at index `1` in the string `"baaaab"`. @@ -97,38 +97,38 @@ use crate::slice::memchr; /// assert_eq!("abcdef_z".find(|ch| ch > 'd' && ch < 'y'), Some(4)); /// assert_eq!("abcddd_z".find(|ch| ch > 'd' && ch < 'y'), None); /// ``` -pub trait Pattern<'a>: Sized { +pub trait Pattern: Sized { /// Associated searcher for this pattern - type Searcher: Searcher<'a>; + type Searcher<'a>: Searcher<'a>; /// Constructs the associated searcher from /// `self` and the `haystack` to search in. - fn into_searcher(self, haystack: &'a str) -> Self::Searcher; + fn into_searcher(self, haystack: &str) -> Self::Searcher<'_>; /// Checks whether the pattern matches anywhere in the haystack #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { self.into_searcher(haystack).next_match().is_some() } /// Checks whether the pattern matches at the front of the haystack #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) } /// Checks whether the pattern matches at the back of the haystack #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) } /// Removes the pattern from the front of haystack, if it matches. #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { if let SearchStep::Match(start, len) = self.into_searcher(haystack).next() { debug_assert_eq!( start, 0, @@ -144,9 +144,9 @@ pub trait Pattern<'a>: Sized { /// Removes the pattern from the back of haystack, if it matches. #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { if let SearchStep::Match(start, end) = self.into_searcher(haystack).next_back() { debug_assert_eq!( @@ -349,7 +349,7 @@ pub trait DoubleEndedSearcher<'a>: ReverseSearcher<'a> {} // Impl for char ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `<char as Pattern<'a>>::Searcher`. +/// Associated type for `<char as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharSearcher<'a> { haystack: &'a str, @@ -543,11 +543,11 @@ impl<'a> DoubleEndedSearcher<'a> for CharSearcher<'a> {} /// ``` /// assert_eq!("Hello world".find('o'), Some(4)); /// ``` -impl<'a> Pattern<'a> for char { - type Searcher = CharSearcher<'a>; +impl Pattern for char { + type Searcher<'a> = CharSearcher<'a>; #[inline] - fn into_searcher(self, haystack: &'a str) -> Self::Searcher { + fn into_searcher(self, haystack: &str) -> Self::Searcher<'_> { let mut utf8_encoded = [0; 4]; let utf8_size = self .encode_utf8(&mut utf8_encoded) @@ -566,7 +566,7 @@ impl<'a> Pattern<'a> for char { } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { if (self as u32) < 128 { haystack.as_bytes().contains(&(self as u8)) } else { @@ -576,27 +576,27 @@ impl<'a> Pattern<'a> for char { } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { self.encode_utf8(&mut [0u8; 4]).is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { self.encode_utf8(&mut [0u8; 4]).strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { self.encode_utf8(&mut [0u8; 4]).is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> where - Self::Searcher: ReverseSearcher<'a>, + Self::Searcher<'a>: ReverseSearcher<'a>, { self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) } @@ -651,11 +651,11 @@ struct MultiCharEqSearcher<'a, C: MultiCharEq> { char_indices: super::CharIndices<'a>, } -impl<'a, C: MultiCharEq> Pattern<'a> for MultiCharEqPattern<C> { - type Searcher = MultiCharEqSearcher<'a, C>; +impl<C: MultiCharEq> Pattern for MultiCharEqPattern<C> { + type Searcher<'a> = MultiCharEqSearcher<'a, C>; #[inline] - fn into_searcher(self, haystack: &'a str) -> MultiCharEqSearcher<'a, C> { + fn into_searcher(self, haystack: &str) -> MultiCharEqSearcher<'_, C> { MultiCharEqSearcher { haystack, char_eq: self.0, char_indices: haystack.char_indices() } } } @@ -710,41 +710,41 @@ impl<'a, C: MultiCharEq> DoubleEndedSearcher<'a> for MultiCharEqSearcher<'a, C> ///////////////////////////////////////////////////////////////////////////// macro_rules! pattern_methods { - ($t:ty, $pmap:expr, $smap:expr) => { - type Searcher = $t; + ($a:lifetime, $t:ty, $pmap:expr, $smap:expr) => { + type Searcher<$a> = $t; #[inline] - fn into_searcher(self, haystack: &'a str) -> $t { + fn into_searcher<$a>(self, haystack: &$a str) -> $t { ($smap)(($pmap)(self).into_searcher(haystack)) } #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in<$a>(self, haystack: &$a str) -> bool { ($pmap)(self).is_contained_in(haystack) } #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of<$a>(self, haystack: &$a str) -> bool { ($pmap)(self).is_prefix_of(haystack) } #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of<$a>(self, haystack: &$a str) -> Option<&$a str> { ($pmap)(self).strip_prefix_of(haystack) } #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool + fn is_suffix_of<$a>(self, haystack: &$a str) -> bool where - $t: ReverseSearcher<'a>, + $t: ReverseSearcher<$a>, { ($pmap)(self).is_suffix_of(haystack) } #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + fn strip_suffix_of<$a>(self, haystack: &$a str) -> Option<&$a str> where - $t: ReverseSearcher<'a>, + $t: ReverseSearcher<$a>, { ($pmap)(self).strip_suffix_of(haystack) } @@ -786,16 +786,16 @@ macro_rules! searcher_methods { }; } -/// Associated type for `<[char; N] as Pattern<'a>>::Searcher`. +/// Associated type for `<[char; N] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharArraySearcher<'a, const N: usize>( - <MultiCharEqPattern<[char; N]> as Pattern<'a>>::Searcher, + <MultiCharEqPattern<[char; N]> as Pattern>::Searcher<'a>, ); -/// Associated type for `<&[char; N] as Pattern<'a>>::Searcher`. +/// Associated type for `<&[char; N] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( - <MultiCharEqPattern<&'b [char; N]> as Pattern<'a>>::Searcher, + <MultiCharEqPattern<&'b [char; N]> as Pattern>::Searcher<'a>, ); /// Searches for chars that are equal to any of the [`char`]s in the array. @@ -806,8 +806,8 @@ pub struct CharArrayRefSearcher<'a, 'b, const N: usize>( /// assert_eq!("Hello world".find(['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(['h', 'w']), Some(6)); /// ``` -impl<'a, const N: usize> Pattern<'a> for [char; N] { - pattern_methods!(CharArraySearcher<'a, N>, MultiCharEqPattern, CharArraySearcher); +impl<const N: usize> Pattern for [char; N] { + pattern_methods!('a, CharArraySearcher<'a, N>, MultiCharEqPattern, CharArraySearcher); } unsafe impl<'a, const N: usize> Searcher<'a> for CharArraySearcher<'a, N> { @@ -828,8 +828,8 @@ impl<'a, const N: usize> DoubleEndedSearcher<'a> for CharArraySearcher<'a, N> {} /// assert_eq!("Hello world".find(&['o', 'l']), Some(2)); /// assert_eq!("Hello world".find(&['h', 'w']), Some(6)); /// ``` -impl<'a, 'b, const N: usize> Pattern<'a> for &'b [char; N] { - pattern_methods!(CharArrayRefSearcher<'a, 'b, N>, MultiCharEqPattern, CharArrayRefSearcher); +impl<'b, const N: usize> Pattern for &'b [char; N] { + pattern_methods!('a, CharArrayRefSearcher<'a, 'b, N>, MultiCharEqPattern, CharArrayRefSearcher); } unsafe impl<'a, 'b, const N: usize> Searcher<'a> for CharArrayRefSearcher<'a, 'b, N> { @@ -848,9 +848,9 @@ impl<'a, 'b, const N: usize> DoubleEndedSearcher<'a> for CharArrayRefSearcher<'a // Todo: Change / Remove due to ambiguity in meaning. -/// Associated type for `<&[char] as Pattern<'a>>::Searcher`. +/// Associated type for `<&[char] as Pattern>::Searcher<'a>`. #[derive(Clone, Debug)] -pub struct CharSliceSearcher<'a, 'b>(<MultiCharEqPattern<&'b [char]> as Pattern<'a>>::Searcher); +pub struct CharSliceSearcher<'a, 'b>(<MultiCharEqPattern<&'b [char]> as Pattern>::Searcher<'a>); unsafe impl<'a, 'b> Searcher<'a> for CharSliceSearcher<'a, 'b> { searcher_methods!(forward); @@ -870,17 +870,17 @@ impl<'a, 'b> DoubleEndedSearcher<'a> for CharSliceSearcher<'a, 'b> {} /// assert_eq!("Hello world".find(&['l', 'l'] as &[_]), Some(2)); /// assert_eq!("Hello world".find(&['l', 'l'][..]), Some(2)); /// ``` -impl<'a, 'b> Pattern<'a> for &'b [char] { - pattern_methods!(CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); +impl<'b> Pattern for &'b [char] { + pattern_methods!('a, CharSliceSearcher<'a, 'b>, MultiCharEqPattern, CharSliceSearcher); } ///////////////////////////////////////////////////////////////////////////// // Impl for F: FnMut(char) -> bool ///////////////////////////////////////////////////////////////////////////// -/// Associated type for `<F as Pattern<'a>>::Searcher`. +/// Associated type for `<F as Pattern>::Searcher<'a>`. #[derive(Clone)] -pub struct CharPredicateSearcher<'a, F>(<MultiCharEqPattern<F> as Pattern<'a>>::Searcher) +pub struct CharPredicateSearcher<'a, F>(<MultiCharEqPattern<F> as Pattern>::Searcher<'a>) where F: FnMut(char) -> bool; @@ -919,11 +919,11 @@ impl<'a, F> DoubleEndedSearcher<'a> for CharPredicateSearcher<'a, F> where F: Fn /// assert_eq!("Hello world".find(char::is_uppercase), Some(0)); /// assert_eq!("Hello world".find(|c| "aeiou".contains(c)), Some(1)); /// ``` -impl<'a, F> Pattern<'a> for F +impl<F> Pattern for F where F: FnMut(char) -> bool, { - pattern_methods!(CharPredicateSearcher<'a, F>, MultiCharEqPattern, CharPredicateSearcher); + pattern_methods!('a, CharPredicateSearcher<'a, F>, MultiCharEqPattern, CharPredicateSearcher); } ///////////////////////////////////////////////////////////////////////////// @@ -931,8 +931,8 @@ where ///////////////////////////////////////////////////////////////////////////// /// Delegates to the `&str` impl. -impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str { - pattern_methods!(StrSearcher<'a, 'b>, |&s| s, |s| s); +impl<'b, 'c> Pattern for &'c &'b str { + pattern_methods!('a, StrSearcher<'a, 'b>, |&s| s, |s| s); } ///////////////////////////////////////////////////////////////////////////// @@ -949,23 +949,23 @@ impl<'a, 'b, 'c> Pattern<'a> for &'c &'b str { /// ``` /// assert_eq!("Hello world".find("world"), Some(6)); /// ``` -impl<'a, 'b> Pattern<'a> for &'b str { - type Searcher = StrSearcher<'a, 'b>; +impl<'b> Pattern for &'b str { + type Searcher<'a> = StrSearcher<'a, 'b>; #[inline] - fn into_searcher(self, haystack: &'a str) -> StrSearcher<'a, 'b> { + fn into_searcher(self, haystack: &str) -> StrSearcher<'_, 'b> { StrSearcher::new(haystack, self) } /// Checks whether the pattern matches at the front of the haystack. #[inline] - fn is_prefix_of(self, haystack: &'a str) -> bool { + fn is_prefix_of(self, haystack: &str) -> bool { haystack.as_bytes().starts_with(self.as_bytes()) } /// Checks whether the pattern matches anywhere in the haystack #[inline] - fn is_contained_in(self, haystack: &'a str) -> bool { + fn is_contained_in(self, haystack: &str) -> bool { if self.len() == 0 { return true; } @@ -991,7 +991,7 @@ impl<'a, 'b> Pattern<'a> for &'b str { /// Removes the pattern from the front of haystack, if it matches. #[inline] - fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_prefix_of(self, haystack: &str) -> Option<&str> { if self.is_prefix_of(haystack) { // SAFETY: prefix was just verified to exist. unsafe { Some(haystack.get_unchecked(self.as_bytes().len()..)) } @@ -1002,13 +1002,19 @@ impl<'a, 'b> Pattern<'a> for &'b str { /// Checks whether the pattern matches at the back of the haystack. #[inline] - fn is_suffix_of(self, haystack: &'a str) -> bool { + fn is_suffix_of<'a>(self, haystack: &'a str) -> bool + where + Self::Searcher<'a>: ReverseSearcher<'a>, + { haystack.as_bytes().ends_with(self.as_bytes()) } /// Removes the pattern from the back of haystack, if it matches. #[inline] - fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + fn strip_suffix_of<'a>(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher<'a>: ReverseSearcher<'a>, + { if self.is_suffix_of(haystack) { let i = haystack.len() - self.as_bytes().len(); // SAFETY: suffix was just verified to exist. @@ -1024,7 +1030,7 @@ impl<'a, 'b> Pattern<'a> for &'b str { ///////////////////////////////////////////////////////////////////////////// #[derive(Clone, Debug)] -/// Associated type for `<&str as Pattern<'a>>::Searcher`. +/// Associated type for `<&str as Pattern>::Searcher<'a>`. pub struct StrSearcher<'a, 'b> { haystack: &'a str, needle: &'b str, diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 3db2f113860..5929c94a864 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -20,8 +20,12 @@ core = { path = "../core", public = true } compiler_builtins = { version = "0.1.105" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] } -std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.14", default-features = false, features = [ + 'rustc-dep-of-std', +] } +std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = [ + 'rustc-dep-of-std', +] } # Dependencies of the `backtrace` crate rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } @@ -31,13 +35,27 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.153", default-features = false, features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'elf', + 'macho', + 'pe', + 'unaligned', + 'archive', +] } [target.'cfg(target_os = "aix")'.dependencies] -object = { version = "0.36.0", default-features = false, optional = true, features = ['read_core', 'xcoff', 'unaligned', 'archive'] } +object = { version = "0.36.0", default-features = false, optional = true, features = [ + 'read_core', + 'xcoff', + 'unaligned', + 'archive', +] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } @@ -47,13 +65,19 @@ rand_xorshift = "0.3.0" dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } [target.x86_64-fortanix-unknown-sgx.dependencies] -fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } +fortanix-sgx-abi = { version = "0.5.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = [ + 'rustc-dep-of-std', +], public = true } [target.'cfg(target_os = "wasi")'.dependencies] -wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } +wasi = { version = "0.11.0", features = [ + 'rustc-dep-of-std', +], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } @@ -61,9 +85,9 @@ r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ - 'addr2line/rustc-dep-of-std', - 'object/rustc-dep-of-std', - 'miniz_oxide/rustc-dep-of-std', + 'addr2line/rustc-dep-of-std', + 'object/rustc-dep-of-std', + 'miniz_oxide/rustc-dep-of-std', ] panic-unwind = ["panic_unwind"] @@ -77,7 +101,10 @@ llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message -panic_immediate_abort = ["core/panic_immediate_abort", "alloc/panic_immediate_abort"] +panic_immediate_abort = [ + "core/panic_immediate_abort", + "alloc/panic_immediate_abort", +] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size", "alloc/optimize_for_size"] @@ -97,6 +124,11 @@ threads = 125 # Maximum heap size heap_size = 0x8000000 +[[test]] +name = "pipe-subprocess" +path = "tests/pipe_subprocess.rs" +harness = false + [[bench]] name = "stdbenches" path = "benches/lib.rs" diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index eb30a3355e5..9fba657d116 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -591,6 +591,8 @@ pub mod panic; #[unstable(feature = "core_pattern_types", issue = "none")] pub mod pat; pub mod path; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod pipe; pub mod process; pub mod sync; pub mod time; diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs new file mode 100644 index 00000000000..f251b57a7cc --- /dev/null +++ b/library/std/src/pipe.rs @@ -0,0 +1,130 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::{ + io, + sys::anonymous_pipe::{pipe as pipe_inner, AnonPipe}, +}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result<Self> { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 00000000000..74875677cf3 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,18 @@ +cfg_if::cfg_if! { + if #[cfg(unix)] { + mod unix; + pub(crate) use unix::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else if #[cfg(windows)] { + mod windows; + pub(crate) use windows::{AnonPipe, pipe}; + + #[cfg(all(test, not(miri)))] + mod tests; + } else { + mod unsupported; + pub(crate) use unsupported::{AnonPipe, pipe}; + } +} diff --git a/library/std/src/sys/anonymous_pipe/tests.rs b/library/std/src/sys/anonymous_pipe/tests.rs new file mode 100644 index 00000000000..f5ea583eefe --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/tests.rs @@ -0,0 +1,20 @@ +use crate::{ + io::{Read, Write}, + pipe::pipe, +}; + +#[test] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 00000000000..ddbf1d7334f --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,103 @@ +use crate::{ + io, + os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{fd::FileDesc, pipe::anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = FileDesc; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FileDesc::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedFd> for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedFd> for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(FileDesc::from_inner(owned_fd)) + } +} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs new file mode 100644 index 00000000000..5962b69203e --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -0,0 +1,26 @@ +use crate::{ + io, + pipe::{PipeReader, PipeWriter}, + process::Stdio, +}; + +pub(crate) use crate::sys::pipe::AnonPipe; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for Stdio { + fn from(pipe: PipeReader) -> Self { + pipe.0.diverge() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for Stdio { + fn from(pipe: PipeWriter) -> Self { + pipe.0.diverge() + } +} diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 00000000000..81f95aa286a --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,109 @@ +use crate::{ + io, + os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, + }, + pipe::{PipeReader, PipeWriter}, + process::Stdio, + sys::{handle::Handle, pipe::unnamed_anon_pipe}, + sys_common::{FromInner, IntoInner}, +}; + +pub(crate) type AnonPipe = Handle; + +#[inline] +pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { + unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeReader> for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(Handle::from_raw_handle(raw_handle)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(pipe.0) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<PipeWriter> for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedHandle> for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From<OwnedHandle> for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(Handle::from_inner(owned_handle)) + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e50758ce00d..202997b7495 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -7,6 +7,8 @@ mod pal; mod personality; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 1701717db59..10ae3c3ab57 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -82,6 +82,11 @@ const fn max_iov() -> usize { } impl FileDesc { + #[inline] + pub fn try_clone(&self) -> io::Result<Self> { + self.duplicate() + } + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::read( diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 8308a48f16a..b323da8d859 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -463,15 +463,15 @@ impl FileAttr { #[cfg(target_os = "aix")] impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)) + SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64) } pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)) + SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64) } pub fn created(&self) -> io::Result<SystemTime> { - Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)) + SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64) } } diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 33db24e77e4..8762af614f1 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,10 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + pub fn try_clone(&self) -> io::Result<Self> { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } @@ -79,6 +84,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner<FileDesc> for AnonPipe { diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae5..781eafe2f1a 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -1,8 +1,21 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::{ + fmt, + io::{self, BorrowedCursor, IoSlice, IoSliceMut}, +}; pub struct AnonPipe(!); +impl fmt::Debug for AnonPipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + impl AnonPipe { + pub fn try_clone(&self) -> io::Result<Self> { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { self.0 } diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 84f3d6a5399..f8d8398b825 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -9,7 +9,6 @@ use crate::ffi::CStr; use crate::mem; use crate::os::raw::{c_uint, c_ulong, c_ushort, c_void}; -use crate::os::windows::io::{AsRawHandle, BorrowedHandle}; use crate::ptr; pub(super) mod windows_targets; @@ -114,89 +113,6 @@ if #[cfg(not(target_vendor = "uwp"))] { } } -pub unsafe extern "system" fn WriteFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToWrite: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::WriteFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::<u8>(), - nNumberOfBytesToWrite, - lpOverlapped, - lpCompletionRoutine, - ) -} - -pub unsafe extern "system" fn ReadFileEx( - hFile: BorrowedHandle<'_>, - lpBuffer: *mut ::core::ffi::c_void, - nNumberOfBytesToRead: u32, - lpOverlapped: *mut OVERLAPPED, - lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, -) -> BOOL { - windows_sys::ReadFileEx( - hFile.as_raw_handle(), - lpBuffer.cast::<u8>(), - nNumberOfBytesToRead, - lpOverlapped, - lpCompletionRoutine, - ) -} - -cfg_if::cfg_if! { -if #[cfg(not(target_vendor = "uwp"))] { -pub unsafe fn NtReadFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit<u8>, - length: u32, - byteoffset: Option<&i64>, - key: Option<&u32>, -) -> NTSTATUS { - windows_sys::NtReadFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::<c_void>(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -pub unsafe fn NtWriteFile( - filehandle: BorrowedHandle<'_>, - event: HANDLE, - apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *const u8, - length: u32, - byteoffset: Option<&i64>, - key: Option<&u32>, -) -> NTSTATUS { - windows_sys::NtWriteFile( - filehandle.as_raw_handle(), - event, - apcroutine, - apccontext, - iostatusblock, - buffer.cast::<c_void>(), - length, - byteoffset.map(|o| o as *const i64).unwrap_or(ptr::null()), - key.map(|k| k as *const u32).unwrap_or(ptr::null()), - ) -} -} -} - // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. cfg_if::cfg_if! { if #[cfg(not(target_vendor = "win7"))] { @@ -331,29 +247,29 @@ compat_fn_with_fallback! { } #[cfg(target_vendor = "uwp")] pub fn NtReadFile( - filehandle: BorrowedHandle<'_>, + filehandle: HANDLE, event: HANDLE, apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *mut crate::mem::MaybeUninit<u8>, + apccontext: *const core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *mut core::ffi::c_void, length: u32, - byteoffset: Option<&i64>, - key: Option<&u32> + byteoffset: *const i64, + key: *const u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } #[cfg(target_vendor = "uwp")] pub fn NtWriteFile( - filehandle: BorrowedHandle<'_>, + filehandle: HANDLE, event: HANDLE, apcroutine: PIO_APC_ROUTINE, - apccontext: *mut c_void, - iostatusblock: &mut IO_STATUS_BLOCK, - buffer: *const u8, + apccontext: *const core::ffi::c_void, + iostatusblock: *mut IO_STATUS_BLOCK, + buffer: *const core::ffi::c_void, length: u32, - byteoffset: Option<&i64>, - key: Option<&u32> + byteoffset: *const i64, + key: *const u32 ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index 794e2c90c52..de4f1050e92 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -2463,6 +2463,7 @@ Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.Performance.QueryPerformanceCounter Windows.Win32.System.Performance.QueryPerformanceFrequency Windows.Win32.System.Pipes.CreateNamedPipeW +Windows.Win32.System.Pipes.CreatePipe Windows.Win32.System.Pipes.NAMED_PIPE_MODE Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS Windows.Win32.System.Pipes.PIPE_CLIENT_END diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index eae0f775860..29110bde3b4 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -14,6 +14,7 @@ windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE); +windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> BOOLEAN); windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE); diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index aaa1831dcc2..94408f69e25 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { @@ -136,6 +137,12 @@ impl Handle { } } + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + + Read::read_to_end(&mut me, buf) + } + pub unsafe fn read_overlapped( &self, buf: &mut [mem::MaybeUninit<u8>], @@ -243,15 +250,15 @@ impl Handle { // the provided `len`. let status = unsafe { c::NtReadFile( - self.as_handle(), + self.as_raw_handle(), ptr::null_mut(), None, ptr::null_mut(), &mut io_status, - buf, + buf.cast::<core::ffi::c_void>(), len, - offset.map(|n| n as _).as_ref(), - None, + offset.as_ref().map(|n| ptr::from_ref(n).cast::<i64>()).unwrap_or(ptr::null()), + ptr::null(), ) }; @@ -293,15 +300,15 @@ impl Handle { let len = cmp::min(buf.len(), u32::MAX as usize) as u32; let status = unsafe { c::NtWriteFile( - self.as_handle(), + self.as_raw_handle(), ptr::null_mut(), None, ptr::null_mut(), &mut io_status, - buf.as_ptr(), + buf.as_ptr().cast::<core::ffi::c_void>(), len, - offset.map(|n| n as _).as_ref(), - None, + offset.as_ref().map(|n| ptr::from_ref(n).cast::<i64>()).unwrap_or(ptr::null()), + ptr::null(), ) }; let status = if status == c::STATUS_PENDING { diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7a309b9638b..01433d68b69 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -1,7 +1,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsStr; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::mem; use crate::path::Path; use crate::ptr; @@ -39,6 +39,23 @@ pub struct Pipes { pub theirs: AnonPipe, } +/// Create true unnamed anonymous pipe. +pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut read_pipe = c::INVALID_HANDLE_VALUE; + let mut write_pipe = c::INVALID_HANDLE_VALUE; + + let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(( + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }), + AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }), + )) + } +} + /// Although this looks similar to `anon_pipe` in the Unix module it's actually /// subtly different. Here we'll return two pipes in the `Pipes` return value, /// but one is intended for "us" where as the other is intended for "someone @@ -181,7 +198,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result<AnonPipe> { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -221,15 +238,6 @@ fn random_number() -> usize { } } -// Abstracts over `ReadFileEx` and `WriteFileEx` -type AlertableIoFn = unsafe extern "system" fn( - BorrowedHandle<'_>, - *mut core::ffi::c_void, - u32, - *mut c::OVERLAPPED, - c::LPOVERLAPPED_COMPLETION_ROUTINE, -) -> c::BOOL; - impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner @@ -237,14 +245,18 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result<Self> { + + pub fn try_clone(&self) -> io::Result<Self> { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { let result = unsafe { let len = crate::cmp::min(buf.len(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len) + let ptr = buf.as_mut_ptr(); + self.alertable_io_internal(|overlapped, callback| { + c::ReadFileEx(self.inner.as_raw_handle(), ptr, len, overlapped, callback) + }) }; match result { @@ -260,7 +272,10 @@ impl AnonPipe { pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { let result = unsafe { let len = crate::cmp::min(buf.capacity(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::ReadFileEx, buf.as_mut().as_mut_ptr() as _, len) + let ptr = buf.as_mut().as_mut_ptr().cast::<u8>(); + self.alertable_io_internal(|overlapped, callback| { + c::ReadFileEx(self.inner.as_raw_handle(), ptr, len, overlapped, callback) + }) }; match result { @@ -295,7 +310,9 @@ impl AnonPipe { pub fn write(&self, buf: &[u8]) -> io::Result<usize> { unsafe { let len = crate::cmp::min(buf.len(), u32::MAX as usize) as u32; - self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len) + self.alertable_io_internal(|overlapped, callback| { + c::WriteFileEx(self.inner.as_raw_handle(), buf.as_ptr(), len, overlapped, callback) + }) } } @@ -323,12 +340,9 @@ impl AnonPipe { /// [`ReadFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfileex /// [`WriteFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex /// [Asynchronous Procedure Call]: https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls - #[allow(unsafe_op_in_unsafe_fn)] unsafe fn alertable_io_internal( &self, - io: AlertableIoFn, - buf: *mut core::ffi::c_void, - len: u32, + io: impl FnOnce(&mut c::OVERLAPPED, c::LPOVERLAPPED_COMPLETION_ROUTINE) -> c::BOOL, ) -> io::Result<usize> { // Use "alertable I/O" to synchronize the pipe I/O. // This has four steps. @@ -366,20 +380,25 @@ impl AnonPipe { lpOverlapped: *mut c::OVERLAPPED, ) { // Set `async_result` using a pointer smuggled through `hEvent`. - let result = - AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; - *(*lpOverlapped).hEvent.cast::<Option<AsyncResult>>() = Some(result); + // SAFETY: + // At this point, the OVERLAPPED struct will have been written to by the OS, + // except for our `hEvent` field which we set to a valid AsyncResult pointer (see below) + unsafe { + let result = + AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; + *(*lpOverlapped).hEvent.cast::<Option<AsyncResult>>() = Some(result); + } } // STEP 1: Start the I/O operation. - let mut overlapped: c::OVERLAPPED = crate::mem::zeroed(); + let mut overlapped: c::OVERLAPPED = unsafe { crate::mem::zeroed() }; // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`. // Therefore the documentation suggests using it to smuggle a pointer to the callback. overlapped.hEvent = core::ptr::addr_of_mut!(async_result) as *mut _; // Asynchronous read of the pipe. // If successful, `callback` will be called once it completes. - let result = io(self.inner.as_handle(), buf, len, &mut overlapped, Some(callback)); + let result = io(&mut overlapped, Some(callback)); if result == c::FALSE { // We can return here because the call failed. // After this we must not return until the I/O completes. @@ -390,7 +409,7 @@ impl AnonPipe { let result = loop { // STEP 2: Enter an alertable state. // The second parameter of `SleepEx` is used to make this sleep alertable. - c::SleepEx(c::INFINITE, c::TRUE); + unsafe { c::SleepEx(c::INFINITE, c::TRUE) }; if let Some(result) = async_result { break result; } diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs new file mode 100644 index 00000000000..c2278098b9b --- /dev/null +++ b/library/std/tests/pipe_subprocess.rs @@ -0,0 +1,39 @@ +#![feature(anonymous_pipe)] + +fn main() { + #[cfg(all(not(miri), any(unix, windows)))] + { + use std::{env, io::Read, pipe::pipe, process}; + + if env::var("I_AM_THE_CHILD").is_ok() { + child(); + } else { + parent(); + } + + fn parent() { + let me = env::current_exe().unwrap(); + + let (rx, tx) = pipe().unwrap(); + assert!( + process::Command::new(me) + .env("I_AM_THE_CHILD", "1") + .stdout(tx) + .status() + .unwrap() + .success() + ); + + let mut s = String::new(); + (&rx).read_to_string(&mut s).unwrap(); + drop(rx); + assert_eq!(s, "Heloo,\n"); + + println!("Test pipe_subprocess.rs success"); + } + + fn child() { + println!("Heloo,"); + } + } +} diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 7d67cc3b36e..649bcdd8733 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -472,6 +472,11 @@ impl Step for Rustc { ); } } + if builder.build_wasm_component_ld() { + let src_dir = builder.sysroot_libdir(compiler, host).parent().unwrap().join("bin"); + let ld = exe("wasm-component-ld", compiler.host); + builder.copy_link(&src_dir.join(&ld), &dst_dir.join(&ld)); + } // Man pages t!(fs::create_dir_all(image.join("share/man/man1"))); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 9d5aa795c6c..e32288e2caf 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1570,11 +1570,22 @@ impl Config { let mut is_user_configured_rust_channel = false; if let Some(rust) = toml.rust { - config.download_rustc_commit = - config.download_ci_rustc_commit(rust.download_rustc.clone()); - - if config.download_rustc_commit.is_some() { - check_incompatible_options_for_ci_rustc(&rust); + if let Some(commit) = config.download_ci_rustc_commit(rust.download_rustc.clone()) { + // Primarily used by CI runners to avoid handling download-rustc incompatible + // options one by one on shell scripts. + let disable_ci_rustc_if_incompatible = + env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE") + .is_some_and(|s| s == "1" || s == "true"); + + if let Err(e) = check_incompatible_options_for_ci_rustc(&rust) { + if disable_ci_rustc_if_incompatible { + config.download_rustc_commit = None; + } else { + panic!("{}", e); + } + } else { + config.download_rustc_commit = Some(commit); + } } let Rust { @@ -2612,14 +2623,15 @@ impl Config { /// Checks the CI rustc incompatible options by destructuring the `Rust` instance /// and makes sure that no rust options from config.toml are missed. -fn check_incompatible_options_for_ci_rustc(rust: &Rust) { +fn check_incompatible_options_for_ci_rustc(rust: &Rust) -> Result<(), String> { macro_rules! err { ($name:expr) => { - assert!( - $name.is_none(), - "ERROR: Setting `rust.{}` is incompatible with `rust.download-rustc`.", - stringify!($name).replace("_", "-") - ); + if $name.is_some() { + return Err(format!( + "ERROR: Setting `rust.{}` is incompatible with `rust.download-rustc`.", + stringify!($name).replace("_", "-") + )); + } }; } @@ -2715,6 +2727,8 @@ fn check_incompatible_options_for_ci_rustc(rust: &Rust) { warn!(channel); warn!(description); warn!(incremental); + + Ok(()) } fn set<T>(field: &mut T, val: Option<T>) { diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 51fb126cb34..fe531f0ff59 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -8,7 +8,7 @@ path = "lib.rs" [dependencies] arrayvec = { version = "0.7", default-features = false } -askama = { version = "0.12", default-features = false, features = ["config"] } +rinja = { version = "0.2", default-features = false, features = ["config"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index d4b4db0f3fd..22576b76e41 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -6,7 +6,7 @@ use crate::externalfiles::ExternalHtml; use crate::html::format::{Buffer, Print}; use crate::html::render::{ensure_trailing_slash, StylePath}; -use askama::Template; +use rinja::Template; use super::static_files::{StaticFiles, STATIC_FILES}; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index db1119eca1d..7718413c956 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -36,7 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; use crate::try_err; -use askama::Template; +use rinja::Template; /// Major driving force in all rustdoc rendering. This contains information /// about where in the tree-like hierarchy rendering is occurring and controls diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 877a00e206d..5b9ef67109c 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -46,7 +46,7 @@ use std::path::PathBuf; use std::rc::Rc; use std::str; -use askama::Template; +use rinja::Template; use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 9256330ac7c..a04313b4b79 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -36,10 +36,10 @@ use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -use askama::Template; use itertools::Itertools; +use rinja::Template; -/// Generates an Askama template struct for rendering items with common methods. +/// Generates a Rinja template struct for rendering items with common methods. /// /// Usage: /// ```ignore (illustrative) @@ -309,7 +309,7 @@ fn toggle_close(mut w: impl fmt::Write) { w.write_str("</details>").unwrap(); } -trait ItemTemplate<'a, 'cx: 'a>: askama::Template + fmt::Display { +trait ItemTemplate<'a, 'cx: 'a>: rinja::Template + fmt::Display { fn item_and_mut_cx(&self) -> (&'a clean::Item, RefMut<'_, &'a mut Context<'cx>>); } diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 3d28937eb99..e5bc2ace203 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, rc::Rc}; -use askama::Template; +use rinja::Template; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{def::CtorKind, def_id::DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; diff --git a/src/librustdoc/html/render/type_layout.rs b/src/librustdoc/html/render/type_layout.rs index a27e327f235..35a38d5375f 100644 --- a/src/librustdoc/html/render/type_layout.rs +++ b/src/librustdoc/html/render/type_layout.rs @@ -1,4 +1,4 @@ -use askama::Template; +use rinja::Template; use rustc_data_structures::captures::Captures; use rustc_hir::def_id::DefId; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index d4b1da71b40..7b4d1fa5305 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -8,7 +8,7 @@ use crate::html::layout; use crate::html::render::Context; use crate::visit::DocVisitor; -use askama::Template; +use rinja::Template; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; diff --git a/src/librustdoc/html/templates/STYLE.md b/src/librustdoc/html/templates/STYLE.md index 38aac2a60e9..32bacb11475 100644 --- a/src/librustdoc/html/templates/STYLE.md +++ b/src/librustdoc/html/templates/STYLE.md @@ -1,15 +1,13 @@ # Style for Templates -This directory has templates in the [Tera templating language][teradoc], which is very -similar to [Jinja2][jinjadoc] and [Django][djangodoc] templates, and also to [Askama][askamadoc]. +This directory has templates in the [Rinja templating language][rinjadoc], which is very +similar to [Jinja2][jinjadoc]. -[teradoc]: https://tera.netlify.app/docs/#templates [jinjadoc]: https://jinja.palletsprojects.com/en/3.1.x/templates/ -[djangodoc]: https://docs.djangoproject.com/en/4.1/topics/templates/ -[askamadoc]: https://docs.rs/askama/latest/askama/ +[rinjadoc]: https://docs.rs/rinja/latest/rinja/ We want our rendered output to have as little unnecessary whitespace as -possible, so that pages load quickly. To achieve that we use Tera's +possible, so that pages load quickly. To achieve that we use Rinja's [whitespace control] features. By default, whitespace characters are removed around jinja tags (`{% %}` for example). At the end of most lines, we put an empty comment tag: `{# #}`. This causes all whitespace between the end of the @@ -20,7 +18,7 @@ remove following whitespace but not preceding. We also use the whitespace control characters in most instances of tags with control flow, for example `{% if foo %}`. -[whitespace control]: https://tera.netlify.app/docs/#whitespace-control +[whitespace control]: https://rinja.readthedocs.io/en/stable/configuration.html#whitespace-control We want our templates to be readable, so we use indentation and newlines liberally. We indent by four spaces after opening an HTML tag _or_ a Jinja @@ -28,11 +26,11 @@ tag. In most cases an HTML tag should be followed by a newline, but if the tag has simple contents and fits with its close tag on a single line, the contents don't necessarily need a new line. -Askama templates support quite sophisticated control flow. To keep our templates +Rinja templates support quite sophisticated control flow. To keep our templates simple and understandable, we use only a subset: `if` and `for`. In particular -we avoid [assignments in the template logic][assignments] and [Askama +we avoid [assignments in the template logic][assignments] and [Rinja macros][macros]. This also may make things easier if we switch to a different Jinja-style template system in the future. -[assignments]: https://djc.github.io/askama/template_syntax.html#assignments -[macros]: https://djc.github.io/askama/template_syntax.html#macros +[assignments]: https://rinja.readthedocs.io/en/stable/template_syntax.html#assignments +[macros]: https://rinja.readthedocs.io/en/stable/template_syntax.html#macros diff --git a/src/librustdoc/askama.toml b/src/librustdoc/rinja.toml index 2732c4bc61e..2732c4bc61e 100644 --- a/src/librustdoc/askama.toml +++ b/src/librustdoc/rinja.toml diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 0cf05b32e96..81a5acb5cf2 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -25,7 +25,7 @@ use build_helper::git::{get_git_modified_files, get_git_untracked_files}; use core::panic; use getopts::Options; use std::collections::HashSet; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fs; use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; @@ -225,6 +225,29 @@ pub fn parse_config(args: Vec<String>) -> Config { // Avoid spawning an external command when we know tidy won't be used. false }; + let filters = if mode == Mode::RunMake { + matches + .free + .iter() + .map(|f| { + let path = Path::new(f); + let mut iter = path.iter().skip(1); + + // We skip the test folder and check if the user passed `rmake.rs` or `Makefile`. + if iter + .next() + .is_some_and(|s| s == OsStr::new("rmake.rs") || s == OsStr::new("Makefile")) + && iter.next().is_none() + { + path.parent().unwrap().to_str().unwrap().to_string() + } else { + f.to_string() + } + }) + .collect::<Vec<_>>() + } else { + matches.free.clone() + }; Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), @@ -249,7 +272,7 @@ pub fn parse_config(args: Vec<String>) -> Config { debugger: None, run_ignored, with_debug_assertions, - filters: matches.free.clone(), + filters, skip: matches.opt_strs("skip"), filter_exact: matches.opt_present("exact"), force_pass_mode: matches.opt_str("pass").map(|mode| { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 16c08f709df..78c06551ff2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -3378,6 +3378,7 @@ impl<'test> TestCx<'test> { cmd.env("IS_MSVC", "1") .env("IS_WINDOWS", "1") .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) + .env("MSVC_LIB_PATH", format!("{}", lib.display())) .env("CC", format!("'{}' {}", self.config.cc, cflags)) .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); } else { @@ -3748,6 +3749,7 @@ impl<'test> TestCx<'test> { cmd.env("IS_MSVC", "1") .env("IS_WINDOWS", "1") .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) + .env("MSVC_LIB_PATH", format!("{}", lib.display())) // Note: we diverge from legacy run_make and don't lump `CC` the compiler and // default flags together. .env("CC_DEFAULT_FLAGS", &cflags) diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 158d5cc8ade..4042bac8dac 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -27,7 +27,6 @@ run-make/foreign-rust-exceptions/Makefile run-make/incr-add-rust-src-component/Makefile run-make/incr-foreign-head-span/Makefile run-make/interdependent-c-libraries/Makefile -run-make/issue-15460/Makefile run-make/issue-35164/Makefile run-make/issue-36710/Makefile run-make/issue-47551/Makefile @@ -40,21 +39,17 @@ run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile run-make/link-cfg/Makefile -run-make/link-framework/Makefile run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile -run-make/lto-linkage-used-attr/Makefile run-make/macos-deployment-target/Makefile run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile run-make/native-link-modifier-whole-archive/Makefile run-make/no-alloc-shim/Makefile run-make/no-builtins-attribute/Makefile -run-make/no-duplicate-libs/Makefile run-make/panic-abort-eh_frame/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile run-make/pgo-gen-lto/Makefile -run-make/pgo-gen-no-imp-symbols/Makefile run-make/pgo-indirect-call-promotion/Makefile run-make/pointer-auth-link-with-c/Makefile run-make/print-calling-conventions/Makefile diff --git a/tests/run-make/issue-15460/Makefile b/tests/run-make/issue-15460/Makefile deleted file mode 100644 index a36a085fa6f..00000000000 --- a/tests/run-make/issue-15460/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: $(call NATIVE_STATICLIB,foo) - $(RUSTC) foo.rs -C extra-filename=-383hf8 -C prefer-dynamic - $(RUSTC) bar.rs - $(call RUN,bar) diff --git a/tests/run-make/link-framework/Makefile b/tests/run-make/link-framework/Makefile deleted file mode 100644 index 96d832ad4a8..00000000000 --- a/tests/run-make/link-framework/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# only-apple -# -# Check that linking to a framework actually makes it to the linker. - -include ../tools.mk - -all: - $(RUSTC) dep-link-framework.rs - $(RUSTC) dep-link-weak-framework.rs - - $(RUSTC) empty.rs - otool -L $(TMPDIR)/no-link | $(CGREP) -v CoreFoundation - - $(RUSTC) link-framework.rs - otool -L $(TMPDIR)/link-framework | $(CGREP) CoreFoundation | $(CGREP) -v weak - - $(RUSTC) link-weak-framework.rs - otool -L $(TMPDIR)/link-weak-framework | $(CGREP) CoreFoundation | $(CGREP) weak - -# When linking the framework both normally, and weakly, the weak linking takes preference - - $(RUSTC) link-both.rs - otool -L $(TMPDIR)/link-both | $(CGREP) CoreFoundation | $(CGREP) weak diff --git a/tests/run-make/link-framework/rmake.rs b/tests/run-make/link-framework/rmake.rs new file mode 100644 index 00000000000..e5df93b181a --- /dev/null +++ b/tests/run-make/link-framework/rmake.rs @@ -0,0 +1,38 @@ +// Check that linking to a framework actually makes it to the linker. + +//@ only-apple + +use run_make_support::{cmd, rustc}; + +fn main() { + rustc().input("dep-link-framework.rs").run(); + rustc().input("dep-link-weak-framework.rs").run(); + + rustc().input("empty.rs").run(); + cmd("otool").arg("-L").arg("no-link").run_fail().assert_stdout_not_contains("CoreFoundation"); + + rustc().input("link-framework.rs").run(); + cmd("otool") + .arg("-L") + .arg("link-framework") + .run() + .assert_stdout_contains("CoreFoundation") + .assert_stdout_not_contains("weak"); + + rustc().input("link-weak-framework.rs").run(); + cmd("otool") + .arg("-L") + .arg("link-weak-framework") + .run() + .assert_stdout_contains("CoreFoundation") + .assert_stdout_contains("weak"); + + // When linking the framework both normally, and weakly, the weak linking takes preference. + rustc().input("link-both.rs").run(); + cmd("otool") + .arg("-L") + .arg("link-both") + .run() + .assert_stdout_contains("CoreFoundation") + .assert_stdout_contains("weak"); +} diff --git a/tests/run-make/issue-15460/bar.rs b/tests/run-make/link-native-static-lib-to-dylib/bar.rs index e66aeb6bd39..e66aeb6bd39 100644 --- a/tests/run-make/issue-15460/bar.rs +++ b/tests/run-make/link-native-static-lib-to-dylib/bar.rs diff --git a/tests/run-make/issue-15460/foo.c b/tests/run-make/link-native-static-lib-to-dylib/foo.c index fdf595b574e..fdf595b574e 100644 --- a/tests/run-make/issue-15460/foo.c +++ b/tests/run-make/link-native-static-lib-to-dylib/foo.c diff --git a/tests/run-make/issue-15460/foo.rs b/tests/run-make/link-native-static-lib-to-dylib/foo.rs index b4eaa0b31c5..b4eaa0b31c5 100644 --- a/tests/run-make/issue-15460/foo.rs +++ b/tests/run-make/link-native-static-lib-to-dylib/foo.rs diff --git a/tests/run-make/link-native-static-lib-to-dylib/rmake.rs b/tests/run-make/link-native-static-lib-to-dylib/rmake.rs new file mode 100644 index 00000000000..0746c396314 --- /dev/null +++ b/tests/run-make/link-native-static-lib-to-dylib/rmake.rs @@ -0,0 +1,14 @@ +// Regression test for <https://github.com/rust-lang/rust/issues/15460>. + +//@ ignore-cross-compile + +use run_make_support::{build_native_static_lib, run, rustc}; + +fn main() { + build_native_static_lib("foo"); + + rustc().input("foo.rs").extra_filename("-383hf8").arg("-Cprefer-dynamic").run(); + rustc().input("bar.rs").run(); + + run("bar"); +} diff --git a/tests/run-make/lto-linkage-used-attr/Makefile b/tests/run-make/lto-linkage-used-attr/Makefile deleted file mode 100644 index fed41a00f84..00000000000 --- a/tests/run-make/lto-linkage-used-attr/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -include ../tools.mk - -# Verify that the impl_* symbols are preserved. #108030 -# only-x86_64-unknown-linux-gnu - -all: - $(RUSTC) -Cdebuginfo=0 -Copt-level=3 lib.rs - $(RUSTC) -Clto=fat -Cdebuginfo=0 -Copt-level=3 main.rs diff --git a/tests/run-make/lto-linkage-used-attr/rmake.rs b/tests/run-make/lto-linkage-used-attr/rmake.rs new file mode 100644 index 00000000000..12463b79a75 --- /dev/null +++ b/tests/run-make/lto-linkage-used-attr/rmake.rs @@ -0,0 +1,15 @@ +// Link time optimizations (LTO) used to snip away some important symbols +// when setting optimization level to 3 or higher. +// This is an LLVM, not a rustc bug, fixed here: https://reviews.llvm.org/D145293 +// This test checks that the impl_* symbols are preserved as they should. +// See https://github.com/rust-lang/rust/issues/108030 + +//@ only-x86_64-unknown-linux-gnu +// Reason: some of the inline assembly directives are architecture-specific. + +use run_make_support::rustc; + +fn main() { + rustc().arg("-Cdebuginfo=0").opt_level("3").input("lib.rs").run(); + rustc().arg("-Clto=fat").arg("-Cdebuginfo=0").opt_level("3").input("main.rs").run(); +} diff --git a/tests/run-make/no-duplicate-libs/Makefile b/tests/run-make/no-duplicate-libs/Makefile deleted file mode 100644 index 4be8c026294..00000000000 --- a/tests/run-make/no-duplicate-libs/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -ifdef IS_MSVC -# FIXME(#27979) -all: -else -all: $(call STATICLIB,foo) $(call STATICLIB,bar) - $(RUSTC) main.rs - $(call RUN,main) -endif diff --git a/tests/run-make/no-duplicate-libs/rmake.rs b/tests/run-make/no-duplicate-libs/rmake.rs new file mode 100644 index 00000000000..469348e266c --- /dev/null +++ b/tests/run-make/no-duplicate-libs/rmake.rs @@ -0,0 +1,22 @@ +// The rust compiler used to try to detect duplicated libraries in +// the linking order and remove the duplicates... but certain edge cases, +// such as the one presented in `foo` and `bar` in this test, demand precise +// control over the link order, including duplicates. As the anti-duplication +// filter was removed, this test should now successfully see main be compiled +// and executed. +// See https://github.com/rust-lang/rust/pull/12688 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +//@ ignore-msvc +// Reason: native compilation results in an unresolved external symbol + +use run_make_support::{build_native_static_lib, run, rustc}; + +fn main() { + build_native_static_lib("foo"); + build_native_static_lib("bar"); + rustc().input("main.rs").run(); + run("main"); +} diff --git a/tests/run-make/pgo-gen-no-imp-symbols/Makefile b/tests/run-make/pgo-gen-no-imp-symbols/Makefile deleted file mode 100644 index d2baa145ba5..00000000000 --- a/tests/run-make/pgo-gen-no-imp-symbols/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -include ../tools.mk - -COMPILE_FLAGS=-O -Ccodegen-units=1 -Cprofile-generate="$(TMPDIR)" -Zno-profiler-runtime - -all: - $(RUSTC) $(COMPILE_FLAGS) --emit=llvm-ir test.rs - # We expect symbols starting with "__llvm_profile_". - $(CGREP) "__llvm_profile_" < $(TMPDIR)/test.ll - # We do NOT expect the "__imp_" version of these symbols. - $(CGREP) -v "__imp___llvm_profile_" < $(TMPDIR)/test.ll # 64 bit - $(CGREP) -v "__imp____llvm_profile_" < $(TMPDIR)/test.ll # 32 bit diff --git a/tests/run-make/pgo-gen-no-imp-symbols/rmake.rs b/tests/run-make/pgo-gen-no-imp-symbols/rmake.rs new file mode 100644 index 00000000000..85ade7885ce --- /dev/null +++ b/tests/run-make/pgo-gen-no-imp-symbols/rmake.rs @@ -0,0 +1,27 @@ +// LLVM's profiling instrumentation adds a few symbols that are used by the profiler runtime. +// Since these show up as globals in the LLVM IR, the compiler generates dllimport-related +// __imp_ stubs for them. This can lead to linker errors because the instrumentation +// symbols have weak linkage or are in a comdat section, but the __imp_ stubs aren't. +// Since profiler-related symbols were excluded from stub-generation in #59812, this has +// been fixed, and this test checks that the llvm profile symbol appear, but without the +// anomalous __imp_ stubs. +// See https://github.com/rust-lang/rust/pull/59812 + +use run_make_support::{cwd, rfs, rustc}; + +fn main() { + rustc() + .input("test.rs") + .emit("llvm-ir") + .opt() + .codegen_units(1) + .profile_generate(cwd()) + .arg("-Zno-profiler-runtime") + .run(); + let out = rfs::read_to_string("test.ll"); + // We expect symbols starting with "__llvm_profile_". + assert!(out.contains("__llvm_profile_")); + // We do NOT expect the "__imp_" version of these symbols. + assert!(!out.contains("__imp___llvm_profile_")); // 64 bit + assert!(!out.contains("__imp____llvm_profile_")); // 32 bit +} diff --git a/tests/ui/closures/closure-no-fn-3.stderr b/tests/ui/closures/closure-no-fn-3.stderr index bbf3677fb72..7711347c568 100644 --- a/tests/ui/closures/closure-no-fn-3.stderr +++ b/tests/ui/closures/closure-no-fn-3.stderr @@ -3,6 +3,12 @@ error[E0605]: non-primitive cast: `{closure@$DIR/closure-no-fn-3.rs:6:28: 6:30}` | LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; | ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + | +note: closures can only be coerced to `fn` types if they do not capture any variables + --> $DIR/closure-no-fn-3.rs:6:33 + | +LL | let baz: fn() -> u8 = (|| { b }) as fn() -> u8; + | ^ `b` captured here error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/issue-104961.fixed b/tests/ui/suggestions/issue-104961.fixed index 5def21b506e..3019242880f 100644 --- a/tests/ui/suggestions/issue-104961.fixed +++ b/tests/ui/suggestions/issue-104961.fixed @@ -2,12 +2,12 @@ fn foo(x: &str) -> bool { x.starts_with(&("hi".to_string() + " you")) - //~^ ERROR the trait bound `String: Pattern<'_>` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn foo2(x: &str) -> bool { x.starts_with(&"hi".to_string()) - //~^ ERROR the trait bound `String: Pattern<'_>` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn main() { diff --git a/tests/ui/suggestions/issue-104961.rs b/tests/ui/suggestions/issue-104961.rs index a09b8a88711..b315e9bab0d 100644 --- a/tests/ui/suggestions/issue-104961.rs +++ b/tests/ui/suggestions/issue-104961.rs @@ -2,12 +2,12 @@ fn foo(x: &str) -> bool { x.starts_with("hi".to_string() + " you") - //~^ ERROR the trait bound `String: Pattern<'_>` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn foo2(x: &str) -> bool { x.starts_with("hi".to_string()) - //~^ ERROR the trait bound `String: Pattern<'_>` is not satisfied [E0277] + //~^ ERROR the trait bound `String: Pattern` is not satisfied [E0277] } fn main() { diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr index 3c5f86817f3..0d229e6dada 100644 --- a/tests/ui/suggestions/issue-104961.stderr +++ b/tests/ui/suggestions/issue-104961.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `String: Pattern<'_>` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-104961.rs:4:19 | LL | x.starts_with("hi".to_string() + " you") - | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern<'_>` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::<impl str>::starts_with` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here @@ -14,15 +14,15 @@ help: consider borrowing here LL | x.starts_with(&("hi".to_string() + " you")) | ++ + -error[E0277]: the trait bound `String: Pattern<'_>` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-104961.rs:9:19 | LL | x.starts_with("hi".to_string()) - | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern<'_>` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::<impl str>::starts_with` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here diff --git a/tests/ui/suggestions/issue-62843.stderr b/tests/ui/suggestions/issue-62843.stderr index 84ab4a0edd3..c3c0360b3a9 100644 --- a/tests/ui/suggestions/issue-62843.stderr +++ b/tests/ui/suggestions/issue-62843.stderr @@ -1,12 +1,12 @@ -error[E0277]: the trait bound `String: Pattern<'_>` is not satisfied +error[E0277]: the trait bound `String: Pattern` is not satisfied --> $DIR/issue-62843.rs:4:32 | LL | println!("{:?}", line.find(pattern)); - | ---- ^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | ---- ^^^^^^^ the trait `Pattern` is not implemented for `String` | | | required by a bound introduced by this call | - = note: required for `String` to implement `Pattern<'_>` + = note: required for `String` to implement `Pattern` note: required by a bound in `core::str::<impl str>::find` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider borrowing here diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs index 98825bd536e..dc2de5bb715 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.rs @@ -1,11 +1,11 @@ fn strip_lf(s: &str) -> &str { s.strip_suffix(b'\n').unwrap_or(s) - //~^ ERROR expected a `FnMut(char)` closure, found `u8` - //~| NOTE expected an `FnMut(char)` closure, found `u8` - //~| HELP the trait `FnMut(char)` is not implemented for `u8` - //~| HELP the following other types implement trait `Pattern<'a>`: - //~| NOTE required for `u8` to implement `Pattern<'_>` - + //~^ ERROR the trait bound `u8: Pattern` is not satisfied + //~| NOTE required by a bound introduced by this call + //~| NOTE the trait `FnMut(char)` is not implemented for `u8`, which is required by `u8: Pattern` + //~| HELP the following other types implement trait `Pattern`: + //~| NOTE required for `u8` to implement `Pattern` + //~| NOTE required by a bound in `core::str::<impl str>::strip_suffix` } fn main() {} diff --git a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr index 49272e7d357..8351d15fdf3 100644 --- a/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr +++ b/tests/ui/traits/bound/assoc-fn-bound-root-obligation.stderr @@ -1,11 +1,12 @@ -error[E0277]: expected a `FnMut(char)` closure, found `u8` - --> $DIR/assoc-fn-bound-root-obligation.rs:2:7 +error[E0277]: the trait bound `u8: Pattern` is not satisfied + --> $DIR/assoc-fn-bound-root-obligation.rs:2:20 | LL | s.strip_suffix(b'\n').unwrap_or(s) - | ^^^^^^^^^^^^ expected an `FnMut(char)` closure, found `u8` + | ------------ ^^^^^ the trait `FnMut(char)` is not implemented for `u8`, which is required by `u8: Pattern` + | | + | required by a bound introduced by this call | - = help: the trait `FnMut(char)` is not implemented for `u8`, which is required by `u8: Pattern<'_>` - = help: the following other types implement trait `Pattern<'a>`: + = help: the following other types implement trait `Pattern`: &'b String &'b [char; N] &'b [char] @@ -13,7 +14,9 @@ LL | s.strip_suffix(b'\n').unwrap_or(s) &'c &'b str [char; N] char - = note: required for `u8` to implement `Pattern<'_>` + = note: required for `u8` to implement `Pattern` +note: required by a bound in `core::str::<impl str>::strip_suffix` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.fixed b/tests/ui/traits/suggest-dereferences/root-obligation.fixed index 072296c6b15..ad0f184dc9a 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.fixed +++ b/tests/ui/traits/suggest-dereferences/root-obligation.fixed @@ -4,7 +4,7 @@ fn get_vowel_count(string: &str) -> usize { string .chars() .filter(|c| "aeiou".contains(*c)) - //~^ ERROR the trait bound `&char: Pattern<'_>` is not satisfied + //~^ ERROR the trait bound `&char: Pattern` is not satisfied .count() } diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.rs b/tests/ui/traits/suggest-dereferences/root-obligation.rs index e7025fe0825..a31a9955d31 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.rs +++ b/tests/ui/traits/suggest-dereferences/root-obligation.rs @@ -4,7 +4,7 @@ fn get_vowel_count(string: &str) -> usize { string .chars() .filter(|c| "aeiou".contains(c)) - //~^ ERROR the trait bound `&char: Pattern<'_>` is not satisfied + //~^ ERROR the trait bound `&char: Pattern` is not satisfied .count() } diff --git a/tests/ui/traits/suggest-dereferences/root-obligation.stderr b/tests/ui/traits/suggest-dereferences/root-obligation.stderr index bbfbb98fba7..2f5e1c5b537 100644 --- a/tests/ui/traits/suggest-dereferences/root-obligation.stderr +++ b/tests/ui/traits/suggest-dereferences/root-obligation.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `&char: Pattern<'_>` is not satisfied +error[E0277]: the trait bound `&char: Pattern` is not satisfied --> $DIR/root-obligation.rs:6:38 | LL | .filter(|c| "aeiou".contains(c)) - | -------- ^ the trait `Fn(char)` is not implemented for `char`, which is required by `&char: Pattern<'_>` + | -------- ^ the trait `Fn(char)` is not implemented for `char`, which is required by `&char: Pattern` | | | required by a bound introduced by this call | = note: required for `&char` to implement `FnOnce(char)` - = note: required for `&char` to implement `Pattern<'_>` + = note: required for `&char` to implement `Pattern` note: required by a bound in `core::str::<impl str>::contains` --> $SRC_DIR/core/src/str/mod.rs:LL:COL help: consider dereferencing here |
