diff --git a/.gitignore b/.gitignore index 856ff7dbb0f33cef38a9a680945d1eb27f5cc370..1c50d9b054ddc8b1edd734fe1d404af8da742974 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ __pycache__/ /mingw-build/ # Created by default with `src/ci/docker/run.sh`: /obj/ -/rustllvm/ /unicode-downloads /target # Generated by compiletest for incremental: diff --git a/Cargo.lock b/Cargo.lock index 8deab1deee0d68c9a631657a1cefbaf3ae2f08d7..b687e714d4fa29609ae2d878530c9beb5ae55b80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1259,11 +1259,10 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.8.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" +checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" dependencies = [ - "autocfg", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -1401,9 +1400,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", "hashbrown", diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index fb98f55a2154a5757fcb5b7d885d57a55f4612cf..151acddae840e567c171362311ce6813f6603c5b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -403,8 +403,8 @@ pub fn append(&mut self, new_stream: TokenStream) { self.index = index; } - pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { - self.stream.0[self.index..].get(n).map(|(tree, _)| tree) + pub fn look_ahead(&self, n: usize) -> Option { + self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone()) } } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 38f552558c83949f9f135147e8f4cedb8faa17a8..04792b334d553ff0ab70d7456208bebf8fe7bffb 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -25,7 +25,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_llvm = { path = "../../src/librustc_llvm" } +rustc_llvm = { path = "../rustc_llvm" } rustc_session = { path = "../rustc_session" } rustc_serialize = { path = "../rustc_serialize" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 32822eba930c6022556ec3024e42f777dca36844..4942c997682d80b7fbae0a9e0669e01420726403 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -96,7 +96,7 @@ pub enum DLLStorageClass { DllExport = 2, // Function to be accessible from DLL. } -/// Matches LLVMRustAttribute in rustllvm.h +/// Matches LLVMRustAttribute in LLVMWrapper.h /// Semantically a subset of the C++ enum llvm::Attribute::AttrKind, /// though it is not ABI compatible (since it's a C++ enum) #[repr(C)] @@ -1705,7 +1705,7 @@ pub fn LLVMRustPassManagerBuilderPopulateThinLTOPassManager( PM: &PassManager<'_>, ); - // Stuff that's in rustllvm/ because it's not upstream yet. + // Stuff that's in llvm-wrapper/ because it's not upstream yet. /// Opens an object file. pub fn LLVMCreateObjectFile( diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 765871a6396f353d8aea31812da16059a0ec9ac6..39c82f97e0a39ce3503f084269ba7fba98285941 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -47,26 +47,15 @@ fn to_internal(self) -> token::DelimToken { } } -impl - FromInternal<( - TreeAndJoint, - Option<&'_ tokenstream::TokenTree>, - &'_ ParseSess, - &'_ mut Vec, - )> for TokenTree +impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> + for TokenTree { fn from_internal( - ((tree, is_joint), look_ahead, sess, stack): ( - TreeAndJoint, - Option<&tokenstream::TokenTree>, - &ParseSess, - &mut Vec, - ), + ((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec), ) -> Self { use rustc_ast::token::*; - let joint = is_joint == Joint - && matches!(look_ahead, Some(tokenstream::TokenTree::Token(t)) if t.is_op()); + let joint = is_joint == Joint; let Token { kind, span } = match tree { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = Delimiter::from_internal(delim); @@ -456,8 +445,7 @@ fn next( loop { let tree = iter.stack.pop().or_else(|| { let next = iter.cursor.next_with_joint()?; - let lookahead = iter.cursor.look_ahead(0); - Some(TokenTree::from_internal((next, lookahead, self.sess, &mut iter.stack))) + Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) })?; // A hack used to pass AST fragments to attribute and derive macros // as a single nonterminal token instead of a token stream. diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 4339092b63e85cd28da48f33e3aad538267b723d..29ec3572016d759185989c8c6efb1e9f7d941eb0 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -599,6 +599,7 @@ pub enum RenderOption { NoNodeStyles, Monospace, + DarkTheme, } /// Returns vec holding all the default render options. @@ -630,10 +631,23 @@ pub fn render_opts<'a, N, E, G, W>(g: &'a G, w: &mut W, options: &[RenderOption] writeln!(w, "digraph {} {{", g.graph_id().as_slice())?; // Global graph properties + let mut graph_attrs = Vec::new(); + let mut content_attrs = Vec::new(); if options.contains(&RenderOption::Monospace) { - writeln!(w, r#" graph[fontname="monospace"];"#)?; - writeln!(w, r#" node[fontname="monospace"];"#)?; - writeln!(w, r#" edge[fontname="monospace"];"#)?; + let font = r#"fontname="Courier, monospace""#; + graph_attrs.push(font); + content_attrs.push(font); + }; + if options.contains(&RenderOption::DarkTheme) { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + if !(graph_attrs.is_empty() && content_attrs.is_empty()) { + writeln!(w, r#" graph[{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node[{}];"#, content_attrs_str)?; + writeln!(w, r#" edge[{}];"#, content_attrs_str)?; } for n in g.nodes().iter() { diff --git a/src/librustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml similarity index 77% rename from src/librustc_llvm/Cargo.toml rename to compiler/rustc_llvm/Cargo.toml index 7120f2e991acd37a8396264b50f1bfb9cdbd92ce..ee83689f0a4695d41de257fec03e8d60a66866cc 100644 --- a/src/librustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -4,9 +4,6 @@ name = "rustc_llvm" version = "0.0.0" edition = "2018" -[lib] -path = "lib.rs" - [features] static-libstdcpp = [] emscripten = [] @@ -15,5 +12,5 @@ emscripten = [] libc = "0.2.73" [build-dependencies] -build_helper = { path = "../build_helper" } +build_helper = { path = "../../src/build_helper" } cc = "1.0.58" diff --git a/src/librustc_llvm/build.rs b/compiler/rustc_llvm/build.rs similarity index 96% rename from src/librustc_llvm/build.rs rename to compiler/rustc_llvm/build.rs index 306ffbf5daab42de8079f87fd77dcab35a374632..7f1e5cf336ac414046673967de62c72864876a05 100644 --- a/src/librustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -175,15 +175,15 @@ fn main() { cfg.debug(false); } - build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm")); - cfg.file("../rustllvm/PassWrapper.cpp") - .file("../rustllvm/RustWrapper.cpp") - .file("../rustllvm/ArchiveWrapper.cpp") - .file("../rustllvm/CoverageMappingWrapper.cpp") - .file("../rustllvm/Linker.cpp") + build_helper::rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper")); + cfg.file("llvm-wrapper/PassWrapper.cpp") + .file("llvm-wrapper/RustWrapper.cpp") + .file("llvm-wrapper/ArchiveWrapper.cpp") + .file("llvm-wrapper/CoverageMappingWrapper.cpp") + .file("llvm-wrapper/Linker.cpp") .cpp(true) .cpp_link_stdlib(None) // we handle this below - .compile("rustllvm"); + .compile("llvm-wrapper"); let (llvm_kind, llvm_link_arg) = detect_llvm_link(); @@ -259,7 +259,7 @@ fn main() { } // Some LLVM linker flags (-L and -l) may be needed even when linking - // librustc_llvm, for example when using static libc++, we may need to + // rustc_llvm, for example when using static libc++, we may need to // manually specify the library search path and -ldl -lpthread as link // dependencies. let llvm_linker_flags = tracked_env_var_os("LLVM_LINKER_FLAGS"); diff --git a/src/rustllvm/.editorconfig b/compiler/rustc_llvm/llvm-wrapper/.editorconfig similarity index 100% rename from src/rustllvm/.editorconfig rename to compiler/rustc_llvm/llvm-wrapper/.editorconfig diff --git a/src/rustllvm/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp similarity index 99% rename from src/rustllvm/ArchiveWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index 9ce614fda575248063ae8d636921c102fd067754..2797fe8df4a8e0460f7899aa5ba2c342ed593b64 100644 --- a/src/rustllvm/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -1,4 +1,4 @@ -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" diff --git a/src/rustllvm/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp similarity index 98% rename from src/rustllvm/CoverageMappingWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 81aba0cbf7d42a8a0da5f41f3855f9c42a329522..2b1143a4ecff54ab6b79a737a2ddf54b269b75e2 100644 --- a/src/rustllvm/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -1,4 +1,4 @@ -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProf.h" diff --git a/src/rustllvm/rustllvm.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h similarity index 100% rename from src/rustllvm/rustllvm.h rename to compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h diff --git a/src/rustllvm/Linker.cpp b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp similarity index 97% rename from src/rustllvm/Linker.cpp rename to compiler/rustc_llvm/llvm-wrapper/Linker.cpp index 69176f9cb1f6d528134a2109e5ca4348d1cbea90..8766e96f086d2e845ae8b07a9afdb3bc40570598 100644 --- a/src/rustllvm/Linker.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/Linker.cpp @@ -1,6 +1,6 @@ #include "llvm/Linker/Linker.h" -#include "rustllvm.h" +#include "LLVMWrapper.h" using namespace llvm; diff --git a/src/rustllvm/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp similarity index 99% rename from src/rustllvm/PassWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 76fe5e7f769f77066b59e23e47021b1937623531..7b1c3f9ba2c686c2600d6e8bee08b558280744cd 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -3,7 +3,7 @@ #include #include -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" diff --git a/src/rustllvm/README b/compiler/rustc_llvm/llvm-wrapper/README similarity index 100% rename from src/rustllvm/README rename to compiler/rustc_llvm/llvm-wrapper/README diff --git a/src/rustllvm/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp similarity index 99% rename from src/rustllvm/RustWrapper.cpp rename to compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 9d90b0dfe07024235a511e5fd77b0c47aa78e53e..e85a9b763800465c44da19eac5e1463f8a3d57c8 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1,4 +1,4 @@ -#include "rustllvm.h" +#include "LLVMWrapper.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" diff --git a/src/librustc_llvm/lib.rs b/compiler/rustc_llvm/src/lib.rs similarity index 100% rename from src/librustc_llvm/lib.rs rename to compiler/rustc_llvm/src/lib.rs diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs index d3ad42f6bbcce5b8563205b87e98d9858eee21eb..0b5b437d186aade4297534aaa699ff0327276eee 100644 --- a/compiler/rustc_mir/src/dataflow/framework/engine.rs +++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs @@ -306,7 +306,11 @@ fn write_graphviz_results( let mut buf = Vec::new(); let graphviz = graphviz::Formatter::new(body, def_id, results, style); - dot::render_opts(&graphviz, &mut buf, &[dot::RenderOption::Monospace])?; + let mut render_opts = vec![dot::RenderOption::Monospace]; + if tcx.sess.opts.debugging_opts.graphviz_dark_mode { + render_opts.push(dot::RenderOption::DarkTheme); + } + dot::render_opts(&graphviz, &mut buf, &render_opts)?; if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 50193c4a0db7d5c153b41fdb7a7c16419d6984ea..e89c9437706384e3a8ff65ca067135485f72a9dc 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -55,16 +55,28 @@ pub fn write_mir_fn_graphviz<'tcx, W>( writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?; // Global graph properties - writeln!(w, r#" graph [fontname="monospace"];"#)?; - writeln!(w, r#" node [fontname="monospace"];"#)?; - writeln!(w, r#" edge [fontname="monospace"];"#)?; + let font = r#"fontname="Courier, monospace""#; + let mut graph_attrs = vec![font]; + let mut content_attrs = vec![font]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; // Graph label write_graph_label(tcx, def_id, body, w)?; // Nodes for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(def_id, block, body, w)?; + write_node(def_id, block, body, dark_mode, w)?; } // Edges @@ -84,6 +96,7 @@ pub fn write_mir_fn_graphviz<'tcx, W>( pub fn write_node_label( block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, num_cols: u32, init: INIT, @@ -100,8 +113,9 @@ pub fn write_node_label( // Basic block number at the top. write!( w, - r#"{blk}"#, - attrs = r#"bgcolor="gray" align="center""#, + r#"{blk}"#, + bgcolor = if dark_mode { "dimgray" } else { "gray" }, + attrs = r#"align="center""#, colspan = num_cols, blk = block.index() )?; @@ -134,11 +148,12 @@ fn write_node( def_id: DefId, block: BasicBlock, body: &Body<'_>, + dark_mode: bool, w: &mut W, ) -> io::Result<()> { // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; - write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?; + write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; // Close the node label and the node itself. writeln!(w, ">];") } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index fb27ccfbd9429b5a168064eec2b99f5b5f601ef5..d5977ca3c7d2fb8a735841b50dc13c578af613b9 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -262,7 +262,10 @@ fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> { } _ => { let tt = TokenTree::Token(self.token.take()); - let is_joint = self.bump(); + let mut is_joint = self.bump(); + if !self.token.is_op() { + is_joint = NonJoint; + } Ok((tt, is_joint)) } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 1b2067f8f256b49b182c8d38e9c0e9917113526a..84edfecad192fdbccc83accc02578d01eb155396 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -822,15 +822,15 @@ pub fn look_ahead(&self, dist: usize, looker: impl FnOnce(&Token) -> R) -> R } let frame = &self.token_cursor.frame; - match frame.tree_cursor.look_ahead(dist - 1) { + looker(&match frame.tree_cursor.look_ahead(dist - 1) { Some(tree) => match tree { - TokenTree::Token(token) => looker(token), + TokenTree::Token(token) => token, TokenTree::Delimited(dspan, delim, _) => { - looker(&Token::new(token::OpenDelim(delim.clone()), dspan.open)) + Token::new(token::OpenDelim(delim), dspan.open) } }, - None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), - } + None => Token::new(token::CloseDelim(frame.delim), frame.span.close), + }) } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 848c7cb7d7552604fc1e6bc61bd7ed7499f0a9ba..c5050dbea73394efc094fcede30ee8bc3436adbe 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -909,6 +909,8 @@ fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool { "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], + "use dark-themed colors in graphviz output (default: no)"), hir_stats: bool = (false, parse_bool, [UNTRACKED], "print some statistics about AST and HIR (default: no)"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], diff --git a/config.toml.example b/config.toml.example index be23efae5294b3897332474161e064a5b4ed5234..52531f54c299e5340dcfc5a4eafbb6922f606335 100644 --- a/config.toml.example +++ b/config.toml.example @@ -45,7 +45,7 @@ # this flag will indicate that this version check should not be done. #version-check = true -# Link libstdc++ statically into the librustc_llvm instead of relying on a +# Link libstdc++ statically into the rustc_llvm instead of relying on a # dynamic version to be available. #static-libstdcpp = false diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index 6c8a588eb58f33db5d98c32d2f53f2cb7695d80a..1a836f11499ec8189f4734e75053990cb3bad5ff 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -13,6 +13,9 @@ trait Recover { fn replace(&mut self, key: Self::Key) -> Option; } +/// Same purpose as `Option::unwrap` but doesn't always guarantee a panic +/// if the option contains no value. +/// SAFETY: the caller must ensure that the option contains a value. #[inline(always)] pub unsafe fn unwrap_unchecked(val: Option) -> T { val.unwrap_or_else(|| { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 68b5d1df71cb265c9c1cc18f9cbc912d76d35662..92c4f2ccfe8a00df2876e82f121231b22f19e35b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -54,16 +54,9 @@ //! [aliasing]: ../../nomicon/aliasing.html //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer //! [ub]: ../../reference/behavior-considered-undefined.html -//! [null]: ./fn.null.html //! [zst]: ../../nomicon/exotic-sizes.html#zero-sized-types-zsts -//! [atomic operations]: ../../std/sync/atomic/index.html -//! [`copy`]: ../../std/ptr/fn.copy.html +//! [atomic operations]: crate::sync::atomic //! [`offset`]: ../../std/primitive.pointer.html#method.offset -//! [`read_unaligned`]: ./fn.read_unaligned.html -//! [`write_unaligned`]: ./fn.write_unaligned.html -//! [`read_volatile`]: ./fn.read_volatile.html -//! [`write_volatile`]: ./fn.write_volatile.html -//! [`NonNull::dangling`]: ./struct.NonNull.html#method.dangling #![stable(feature = "rust1", since = "1.0.0")] @@ -118,9 +111,9 @@ /// done automatically by the compiler. This means the fields of packed structs /// are not dropped in-place. /// -/// [`ptr::read`]: ../ptr/fn.read.html -/// [`ptr::read_unaligned`]: ../ptr/fn.read_unaligned.html -/// [pinned]: ../pin/index.html +/// [`ptr::read`]: self::read +/// [`ptr::read_unaligned`]: self::read_unaligned +/// [pinned]: crate::pin /// /// # Safety /// @@ -136,14 +129,12 @@ /// Additionally, if `T` is not [`Copy`], using the pointed-to value after /// calling `drop_in_place` can cause undefined behavior. Note that `*to_drop = /// foo` counts as a use because it will cause the value to be dropped -/// again. [`write`] can be used to overwrite data without causing it to be +/// again. [`write()`] can be used to overwrite data without causing it to be /// dropped. /// /// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`write`]: ../ptr/fn.write.html +/// [valid]: self#safety /// /// # Examples /// @@ -243,9 +234,9 @@ pub(crate) struct FatPtr { /// The `len` argument is the number of **elements**, not the number of bytes. /// /// This function is safe, but actually using the return value is unsafe. -/// See the documentation of [`from_raw_parts`] for slice safety requirements. +/// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. /// -/// [`from_raw_parts`]: ../../std/slice/fn.from_raw_parts.html +/// [`slice::from_raw_parts`]: crate::slice::from_raw_parts /// /// # Examples /// @@ -274,10 +265,9 @@ pub const fn slice_from_raw_parts(data: *const T, len: usize) -> *const [T] { /// See the documentation of [`slice_from_raw_parts`] for more details. /// /// This function is safe, but actually using the return value is unsafe. -/// See the documentation of [`from_raw_parts_mut`] for slice safety requirements. +/// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. /// -/// [`slice_from_raw_parts`]: fn.slice_from_raw_parts.html -/// [`from_raw_parts_mut`]: ../../std/slice/fn.from_raw_parts_mut.html +/// [`slice::from_raw_parts_mut`]: crate::slice::from_raw_parts_mut /// /// # Examples /// @@ -316,8 +306,6 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// overlapping region of memory from `x` will be used. This is demonstrated /// in the second example below. /// -/// [`mem::swap`]: ../mem/fn.swap.html -/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -328,7 +316,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// /// Note that even if `T` has size `0`, the pointers must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety +/// [valid]: self#safety /// /// # Examples /// @@ -406,7 +394,7 @@ pub unsafe fn swap(x: *mut T, y: *mut T) { /// Note that even if the effectively copied size (`count * size_of::()`) is `0`, /// the pointers must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety +/// [valid]: self#safety /// /// # Examples /// @@ -533,8 +521,6 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { /// operates on raw pointers instead of references. When references are /// available, [`mem::replace`] should be preferred. /// -/// [`mem::replace`]: ../mem/fn.replace.html -/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -547,7 +533,7 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety +/// [valid]: self#safety /// /// # Examples /// @@ -653,7 +639,7 @@ pub unsafe fn replace(dst: *mut T, mut src: T) -> T { /// `*src` can violate memory safety. Note that assigning to `*src` counts as a /// use because it will attempt to drop the value at `*src`. /// -/// [`write`] can be used to overwrite data without causing it to be dropped. +/// [`write()`] can be used to overwrite data without causing it to be dropped. /// /// ``` /// use std::ptr; @@ -682,11 +668,7 @@ pub unsafe fn replace(dst: *mut T, mut src: T) -> T { /// assert_eq!(s, "bar"); /// ``` /// -/// [`mem::swap`]: ../mem/fn.swap.html -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// [`write`]: ./fn.write.html +/// [valid]: self#safety #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn read(src: *const T) -> T { @@ -723,11 +705,8 @@ pub unsafe fn read(src: *const T) -> T { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL. /// -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html -/// [`write_unaligned`]: ./fn.write_unaligned.html -/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value -/// [valid]: ../ptr/index.html#safety +/// [read-ownership]: read#ownership-of-the-returned-value +/// [valid]: self#safety /// /// ## On `packed` structs /// @@ -819,8 +798,6 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// This is appropriate for initializing uninitialized memory, or overwriting /// memory that has previously been [`read`] from. /// -/// [`read`]: ./fn.read.html -/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -832,8 +809,7 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety -/// [`write_unaligned`]: ./fn.write_unaligned.html +/// [valid]: self#safety /// /// # Examples /// @@ -888,8 +864,6 @@ pub unsafe fn read_unaligned(src: *const T) -> T { /// assert_eq!(foo, "bar"); /// assert_eq!(bar, "foo"); /// ``` -/// -/// [`mem::swap`]: ../mem/fn.swap.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn write(dst: *mut T, src: T) { @@ -904,7 +878,7 @@ pub unsafe fn write(dst: *mut T, src: T) { /// Overwrites a memory location with the given value without reading or /// dropping the old value. /// -/// Unlike [`write`], the pointer may be unaligned. +/// Unlike [`write()`], the pointer may be unaligned. /// /// `write_unaligned` does not drop the contents of `dst`. This is safe, but it /// could leak allocations or resources, so care should be taken not to overwrite @@ -916,9 +890,6 @@ pub unsafe fn write(dst: *mut T, src: T) { /// This is appropriate for initializing uninitialized memory, or overwriting /// memory that has previously been read with [`read_unaligned`]. /// -/// [`write`]: ./fn.write.html -/// [`read_unaligned`]: ./fn.read_unaligned.html -/// /// # Safety /// /// Behavior is undefined if any of the following conditions are violated: @@ -927,7 +898,7 @@ pub unsafe fn write(dst: *mut T, src: T) { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL. /// -/// [valid]: ../ptr/index.html#safety +/// [valid]: self#safety /// /// ## On `packed` structs /// @@ -1007,8 +978,6 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// to not be elided or reordered by the compiler across other volatile /// operations. /// -/// [`write_volatile`]: ./fn.write_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -1041,10 +1010,8 @@ pub unsafe fn write_unaligned(dst: *mut T, src: T) { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety -/// [`Copy`]: ../marker/trait.Copy.html -/// [`read`]: ./fn.read.html -/// [read-ownership]: ./fn.read.html#ownership-of-the-returned-value +/// [valid]: self#safety +/// [read-ownership]: read#ownership-of-the-returned-value /// /// Just like in C, whether an operation is volatile has no bearing whatsoever /// on questions involving concurrent access from multiple threads. Volatile @@ -1089,8 +1056,6 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// Additionally, it does not drop `src`. Semantically, `src` is moved into the /// location pointed to by `dst`. /// -/// [`read_volatile`]: ./fn.read_volatile.html -/// /// # Notes /// /// Rust does not currently have a rigorously and formally defined memory model, @@ -1115,7 +1080,7 @@ pub unsafe fn read_volatile(src: *const T) -> T { /// /// Note that even if `T` has size `0`, the pointer must be non-NULL and properly aligned. /// -/// [valid]: ../ptr/index.html#safety +/// [valid]: self#safety /// /// Just like in C, whether an operation is volatile has no bearing whatsoever /// on questions involving concurrent access from multiple threads. Volatile diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 9cc69287080083a71a552b9e70570b164225d357..bfd05db6b1bbee628c1554fdd33db18d7296d81d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -20,7 +20,7 @@ libc = { version = "0.2.74", default-features = false, features = ['rustc-dep-of compiler_builtins = { version = "0.1.35" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } -hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] } +hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate addr2line = { version = "0.13.0", optional = true, default-features = false } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 57cb179a4244a4fd6e46d6783f28227d244d8108..1a3a493fbb8f68dcff12a948f828669847bf99fb 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -497,6 +497,50 @@ pub fn drain(&mut self) -> Drain<'_, K, V> { Drain { base: self.base.drain() } } + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, the element is removed from the map and yielded. + /// If the closure returns false, or panics, the element remains in the map and will not be + /// yielded. + /// + /// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// elements will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more elements will be subjected to the closure + /// if a panic occurs in the closure, or a panic occurs while dropping an element, + /// or if the `DrainFilter` value is leaked. + /// + /// # Examples + /// + /// Splitting a map into even and odd keys, reusing the original map: + /// + /// ``` + /// #![feature(hash_drain_filter)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = (0..8).map(|x| (x, x)).collect(); + /// let drained: HashMap = map.drain_filter(|k, _v| k % 2 == 0).collect(); + /// + /// let mut evens = drained.keys().copied().collect::>(); + /// let mut odds = map.keys().copied().collect::>(); + /// evens.sort(); + /// odds.sort(); + /// + /// assert_eq!(evens, vec![0, 2, 4, 6]); + /// assert_eq!(odds, vec![1, 3, 5, 7]); + /// ``` + #[inline] + #[unstable(feature = "hash_drain_filter", issue = "59618")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + DrainFilter { base: self.base.drain_filter(pred) } + } + /// Clears the map, removing all key-value pairs. Keeps the allocated memory /// for reuse. /// @@ -1190,6 +1234,19 @@ pub(super) fn iter(&self) -> Iter<'_, K, V> { } } +/// A draining, filtering iterator over the entries of a `HashMap`. +/// +/// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. +/// +/// [`drain_filter`]: HashMap::drain_filter +#[unstable(feature = "hash_drain_filter", issue = "59618")] +pub struct DrainFilter<'a, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + base: base::DrainFilter<'a, K, V, F>, +} + /// A mutable iterator over the values of a `HashMap`. /// /// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its @@ -1247,7 +1304,7 @@ pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { #[unstable(feature = "hash_raw_entry", issue = "56167")] pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// An occupied entry. - Occupied(RawOccupiedEntryMut<'a, K, V>), + Occupied(RawOccupiedEntryMut<'a, K, V, S>), /// A vacant entry. Vacant(RawVacantEntryMut<'a, K, V, S>), } @@ -1255,8 +1312,8 @@ pub enum RawEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// A view into an occupied entry in a `HashMap`. /// It is part of the [`RawEntryMut`] enum. #[unstable(feature = "hash_raw_entry", issue = "56167")] -pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a> { - base: base::RawOccupiedEntryMut<'a, K, V>, +pub struct RawOccupiedEntryMut<'a, K: 'a, V: 'a, S: 'a> { + base: base::RawOccupiedEntryMut<'a, K, V, S>, } /// A view into a vacant entry in a `HashMap`. @@ -1457,7 +1514,7 @@ pub fn and_modify(self, f: F) -> Self } } -impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> { +impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { /// Gets a reference to the key in the entry. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] @@ -1597,7 +1654,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } #[unstable(feature = "hash_raw_entry", issue = "56167")] -impl Debug for RawOccupiedEntryMut<'_, K, V> { +impl Debug for RawOccupiedEntryMut<'_, K, V, S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RawOccupiedEntryMut") .field("key", self.key()) @@ -1990,6 +2047,36 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl Iterator for DrainFilter<'_, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + type Item = (K, V); + + #[inline] + fn next(&mut self) -> Option<(K, V)> { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl FusedIterator for DrainFilter<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl<'a, K, V, F> fmt::Debug for DrainFilter<'a, K, V, F> +where + F: FnMut(&K, &mut V) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("DrainFilter { .. }") + } +} + impl<'a, K, V> Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the default if empty, and returns @@ -2698,7 +2785,7 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, } #[inline] -fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError { +pub(super) fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError { match err { hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow, hashbrown::TryReserveError::AllocError { layout } => { diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 4283d80b78e6444106ed9173c71d129ef8bd59b9..467968354e25d984d1b47f885da660a1ae238a9d 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -924,3 +924,164 @@ fn test_raw_entry() { } } } + +mod test_drain_filter { + use super::*; + + use crate::panic::{catch_unwind, AssertUnwindSafe}; + use crate::sync::atomic::{AtomicUsize, Ordering}; + + trait EqSorted: Iterator { + fn eq_sorted>(self, other: I) -> bool; + } + + impl EqSorted for T + where + T::Item: Eq + Ord, + { + fn eq_sorted>(self, other: I) -> bool { + let mut v: Vec<_> = self.collect(); + v.sort_unstable(); + v.into_iter().eq(other) + } + } + + #[test] + fn empty() { + let mut map: HashMap = HashMap::new(); + map.drain_filter(|_, _| unreachable!("there's nothing to decide on")); + assert!(map.is_empty()); + } + + #[test] + fn consuming_nothing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!(map.drain_filter(|_, _| false).eq_sorted(crate::iter::empty())); + assert_eq!(map.len(), 3); + } + + #[test] + fn consuming_all() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.clone().collect(); + assert!(map.drain_filter(|_, _| true).eq_sorted(pairs)); + assert!(map.is_empty()); + } + + #[test] + fn mutating_and_keeping() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + false + }) + .eq_sorted(crate::iter::empty()) + ); + assert!(map.keys().copied().eq_sorted(0..3)); + assert!(map.values().copied().eq_sorted(6..9)); + } + + #[test] + fn mutating_and_removing() { + let pairs = (0..3).map(|i| (i, i)); + let mut map: HashMap<_, _> = pairs.collect(); + assert!( + map.drain_filter(|_, v| { + *v += 6; + true + }) + .eq_sorted((0..3).map(|i| (i, i + 6))) + ); + assert!(map.is_empty()); + } + + #[test] + fn drop_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + catch_unwind(move || { + drop(map.drain_filter(|_, _| { + PREDS.fetch_add(1, Ordering::SeqCst); + true + })) + }) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + } + + #[test] + fn pred_panic_leak() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + catch_unwind(AssertUnwindSafe(|| { + drop(map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + })) + })) + .unwrap_err(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 2); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + } + + // Same as above, but attempt to use the iterator again after the panic in the predicate + #[test] + fn pred_panic_reuse() { + static PREDS: AtomicUsize = AtomicUsize::new(0); + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut map = (0..3).map(|i| (i, D)).collect::>(); + + { + let mut it = map.drain_filter(|_, _| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + }); + catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err(); + // Iterator behaviour after a panic is explicitly unspecified, + // so this is just the current implementation: + let result = catch_unwind(AssertUnwindSafe(|| it.next())); + assert!(result.is_err()); + } + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 1); + assert_eq!(map.len(), 2); + } +} diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 8c39725ef3550011917ca3a93c62ee5297246463..72f4798b65d66d665f27c1bb9af67636e09bb6b0 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests; +use hashbrown::hash_set as base; + use crate::borrow::Borrow; use crate::collections::TryReserveError; use crate::fmt; @@ -8,7 +10,7 @@ use crate::iter::{Chain, FromIterator, FusedIterator}; use crate::ops::{BitAnd, BitOr, BitXor, Sub}; -use super::map::{self, HashMap, Keys, RandomState}; +use super::map::{map_try_reserve_error, RandomState}; // Future Optimization (FIXME!) // ============================ @@ -101,13 +103,14 @@ /// // use the values stored in the set /// ``` /// +/// [`HashMap`]: crate::collections::HashMap /// [`RefCell`]: crate::cell::RefCell /// [`Cell`]: crate::cell::Cell #[derive(Clone)] #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct HashSet { - map: HashMap, + base: base::HashSet, } impl HashSet { @@ -125,7 +128,7 @@ impl HashSet { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> HashSet { - HashSet { map: HashMap::new() } + Default::default() } /// Creates an empty `HashSet` with the specified capacity. @@ -143,7 +146,7 @@ pub fn new() -> HashSet { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(capacity: usize) -> HashSet { - HashSet { map: HashMap::with_capacity(capacity) } + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) } } } @@ -160,7 +163,7 @@ impl HashSet { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn capacity(&self) -> usize { - self.map.capacity() + self.base.capacity() } /// An iterator visiting all elements in arbitrary order. @@ -182,7 +185,7 @@ pub fn capacity(&self) -> usize { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<'_, T> { - Iter { iter: self.map.keys() } + Iter { base: self.base.iter() } } /// Returns the number of elements in the set. @@ -200,7 +203,7 @@ pub fn iter(&self) -> Iter<'_, T> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn len(&self) -> usize { - self.map.len() + self.base.len() } /// Returns `true` if the set contains no elements. @@ -218,7 +221,7 @@ pub fn len(&self) -> usize { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { - self.map.is_empty() + self.base.is_empty() } /// Clears the set, returning all elements in an iterator. @@ -241,7 +244,48 @@ pub fn is_empty(&self) -> bool { #[inline] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self) -> Drain<'_, T> { - Drain { iter: self.map.drain() } + Drain { base: self.base.drain() } + } + + /// Creates an iterator which uses a closure to determine if a value should be removed. + /// + /// If the closure returns true, then the value is removed and yielded. + /// If the closure returns false, the value will remain in the list and will not be yielded + /// by the iterator. + /// + /// If the iterator is only partially consumed or not consumed at all, each of the remaining + /// values will still be subjected to the closure and removed and dropped if it returns true. + /// + /// It is unspecified how many more values will be subjected to the closure + /// if a panic occurs in the closure, or if a panic occurs while dropping a value, or if the + /// `DrainFilter` itself is leaked. + /// + /// # Examples + /// + /// Splitting a set into even and odd values, reusing the original set: + /// + /// ``` + /// #![feature(hash_drain_filter)] + /// use std::collections::HashSet; + /// + /// let mut set: HashSet = (0..8).collect(); + /// let drained: HashSet = set.drain_filter(|v| v % 2 == 0).collect(); + /// + /// let mut evens = drained.into_iter().collect::>(); + /// let mut odds = set.into_iter().collect::>(); + /// evens.sort(); + /// odds.sort(); + /// + /// assert_eq!(evens, vec![0, 2, 4, 6]); + /// assert_eq!(odds, vec![1, 3, 5, 7]); + /// ``` + #[inline] + #[unstable(feature = "hash_drain_filter", issue = "59618")] + pub fn drain_filter(&mut self, pred: F) -> DrainFilter<'_, T, F> + where + F: FnMut(&T) -> bool, + { + DrainFilter { base: self.base.drain_filter(pred) } } /// Clears the set, removing all values. @@ -259,7 +303,7 @@ pub fn drain(&mut self) -> Drain<'_, T> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn clear(&mut self) { - self.map.clear() + self.base.clear() } /// Creates a new empty hash set which will use the given hasher to hash @@ -288,7 +332,7 @@ pub fn clear(&mut self) { #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_hasher(hasher: S) -> HashSet { - HashSet { map: HashMap::with_hasher(hasher) } + HashSet { base: base::HashSet::with_hasher(hasher) } } /// Creates an empty `HashSet` with the specified capacity, using @@ -318,7 +362,7 @@ pub fn with_hasher(hasher: S) -> HashSet { #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { - HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) } + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } } /// Returns a reference to the set's [`BuildHasher`]. @@ -336,7 +380,7 @@ pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { #[inline] #[stable(feature = "hashmap_public_hasher", since = "1.9.0")] pub fn hasher(&self) -> &S { - self.map.hasher() + self.base.hasher() } } @@ -364,7 +408,7 @@ impl HashSet #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { - self.map.reserve(additional) + self.base.reserve(additional) } /// Tries to reserve capacity for at least `additional` more elements to be inserted @@ -387,7 +431,7 @@ pub fn reserve(&mut self, additional: usize) { #[inline] #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { - self.map.try_reserve(additional) + self.base.try_reserve(additional).map_err(map_try_reserve_error) } /// Shrinks the capacity of the set as much as possible. It will drop @@ -409,7 +453,7 @@ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { - self.map.shrink_to_fit() + self.base.shrink_to_fit() } /// Shrinks the capacity of the set with a lower limit. It will drop @@ -437,7 +481,7 @@ pub fn shrink_to_fit(&mut self) { #[inline] #[unstable(feature = "shrink_to", reason = "new API", issue = "56431")] pub fn shrink_to(&mut self, min_capacity: usize) { - self.map.shrink_to(min_capacity) + self.base.shrink_to(min_capacity) } /// Visits the values representing the difference, @@ -577,7 +621,7 @@ pub fn contains(&self, value: &Q) -> bool T: Borrow, Q: Hash + Eq, { - self.map.contains_key(value) + self.base.contains(value) } /// Returns a reference to the value in the set, if any, that is equal to the given value. @@ -602,7 +646,7 @@ pub fn get(&self, value: &Q) -> Option<&T> T: Borrow, Q: Hash + Eq, { - self.map.get_key_value(value).map(|(k, _)| k) + self.base.get(value) } /// Inserts the given `value` into the set if it is not present, then @@ -626,7 +670,7 @@ pub fn get(&self, value: &Q) -> Option<&T> pub fn get_or_insert(&mut self, value: T) -> &T { // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(&value).or_insert(value, ()).0 + self.base.get_or_insert(value) } /// Inserts an owned copy of the given `value` into the set if it is not @@ -658,7 +702,7 @@ pub fn get_or_insert_owned(&mut self, value: &Q) -> &T { // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(value).or_insert_with(|| (value.to_owned(), ())).0 + self.base.get_or_insert_owned(value) } /// Inserts a value computed from `f` into the set if the given `value` is @@ -691,7 +735,7 @@ pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T { // Although the raw entry gives us `&mut T`, we only return `&T` to be consistent with // `get`. Key mutation is "raw" because you're not supposed to affect `Eq` or `Hash`. - self.map.raw_entry_mut().from_key(value).or_insert_with(|| (f(value), ())).0 + self.base.get_or_insert_with(value, f) } /// Returns `true` if `self` has no elements in common with `other`. @@ -788,7 +832,7 @@ pub fn is_superset(&self, other: &HashSet) -> bool { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, value: T) -> bool { - self.map.insert(value, ()).is_none() + self.base.insert(value) } /// Adds a value to the set, replacing the existing value, if any, that is equal to the given @@ -809,13 +853,7 @@ pub fn insert(&mut self, value: T) -> bool { #[inline] #[stable(feature = "set_recovery", since = "1.9.0")] pub fn replace(&mut self, value: T) -> Option { - match self.map.entry(value) { - map::Entry::Occupied(occupied) => Some(occupied.replace_key()), - map::Entry::Vacant(vacant) => { - vacant.insert(()); - None - } - } + self.base.replace(value) } /// Removes a value from the set. Returns whether the value was @@ -843,7 +881,7 @@ pub fn remove(&mut self, value: &Q) -> bool T: Borrow, Q: Hash + Eq, { - self.map.remove(value).is_some() + self.base.remove(value) } /// Removes and returns the value in the set, if any, that is equal to the given one. @@ -868,7 +906,7 @@ pub fn take(&mut self, value: &Q) -> Option T: Borrow, Q: Hash + Eq, { - self.map.remove_entry(value).map(|(k, _)| k) + self.base.take(value) } /// Retains only the elements specified by the predicate. @@ -886,11 +924,11 @@ pub fn take(&mut self, value: &Q) -> Option /// assert_eq!(set.len(), 3); /// ``` #[stable(feature = "retain_hash_collection", since = "1.18.0")] - pub fn retain(&mut self, mut f: F) + pub fn retain(&mut self, f: F) where F: FnMut(&T) -> bool, { - self.map.retain(|k, _| f(k)); + self.base.retain(f) } } @@ -949,17 +987,17 @@ impl Extend for HashSet { #[inline] fn extend>(&mut self, iter: I) { - self.map.extend(iter.into_iter().map(|k| (k, ()))); + self.base.extend(iter); } #[inline] fn extend_one(&mut self, item: T) { - self.map.insert(item, ()); + self.base.insert(item); } #[inline] fn extend_reserve(&mut self, additional: usize) { - self.map.extend_reserve(additional); + self.base.extend_reserve(additional); } } @@ -976,7 +1014,7 @@ fn extend>(&mut self, iter: I) { #[inline] fn extend_one(&mut self, &item: &'a T) { - self.map.insert(item, ()); + self.base.insert(item); } #[inline] @@ -993,7 +1031,7 @@ impl Default for HashSet /// Creates an empty `HashSet` with the `Default` value for the hasher. #[inline] fn default() -> HashSet { - HashSet { map: HashMap::default() } + HashSet { base: Default::default() } } } @@ -1137,7 +1175,7 @@ fn sub(self, rhs: &HashSet) -> HashSet { /// [`iter`]: HashSet::iter #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a> { - iter: Keys<'a, K, ()>, + base: base::Iter<'a, K>, } /// An owning iterator over the items of a `HashSet`. @@ -1148,7 +1186,7 @@ pub struct Iter<'a, K: 'a> { /// [`into_iter`]: IntoIterator::into_iter #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { - iter: map::IntoIter, + base: base::IntoIter, } /// A draining iterator over the items of a `HashSet`. @@ -1159,7 +1197,20 @@ pub struct IntoIter { /// [`drain`]: HashSet::drain #[stable(feature = "rust1", since = "1.0.0")] pub struct Drain<'a, K: 'a> { - iter: map::Drain<'a, K, ()>, + base: base::Drain<'a, K>, +} + +/// A draining, filtering iterator over the items of a `HashSet`. +/// +/// This `struct` is created by the [`drain_filter`] method on [`HashSet`]. +/// +/// [`drain_filter`]: HashSet::drain_filter +#[unstable(feature = "hash_drain_filter", issue = "59618")] +pub struct DrainFilter<'a, K, F> +where + F: FnMut(&K) -> bool, +{ + base: base::DrainFilter<'a, K, F>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1250,7 +1301,7 @@ impl IntoIterator for HashSet { /// ``` #[inline] fn into_iter(self) -> IntoIter { - IntoIter { iter: self.map.into_iter() } + IntoIter { base: self.base.into_iter() } } } @@ -1258,7 +1309,7 @@ fn into_iter(self) -> IntoIter { impl Clone for Iter<'_, K> { #[inline] fn clone(&self) -> Self { - Iter { iter: self.iter.clone() } + Iter { base: self.base.clone() } } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1267,18 +1318,18 @@ impl<'a, K> Iterator for Iter<'a, K> { #[inline] fn next(&mut self) -> Option<&'a K> { - self.iter.next() + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() + self.base.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Iter<'_, K> { #[inline] fn len(&self) -> usize { - self.iter.len() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -1297,18 +1348,18 @@ impl Iterator for IntoIter { #[inline] fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() + self.base.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { - self.iter.len() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -1317,8 +1368,7 @@ impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let entries_iter = self.iter.iter().map(|(k, _)| k); - f.debug_list().entries(entries_iter).finish() + fmt::Debug::fmt(&self.base, f) } } @@ -1328,18 +1378,18 @@ impl<'a, K> Iterator for Drain<'a, K> { #[inline] fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) + self.base.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() + self.base.size_hint() } } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Drain<'_, K> { #[inline] fn len(&self) -> usize { - self.iter.len() + self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] @@ -1348,8 +1398,37 @@ impl FusedIterator for Drain<'_, K> {} #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Drain<'_, K> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let entries_iter = self.iter.iter().map(|(k, _)| k); - f.debug_list().entries(entries_iter).finish() + fmt::Debug::fmt(&self.base, f) + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl Iterator for DrainFilter<'_, K, F> +where + F: FnMut(&K) -> bool, +{ + type Item = K; + + #[inline] + fn next(&mut self) -> Option { + self.base.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.base.size_hint() + } +} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl FusedIterator for DrainFilter<'_, K, F> where F: FnMut(&K) -> bool {} + +#[unstable(feature = "hash_drain_filter", issue = "59618")] +impl<'a, K, F> fmt::Debug for DrainFilter<'a, K, F> +where + F: FnMut(&K) -> bool, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("DrainFilter { .. }") } } diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index 3582390cee4bcecc0c53a177fb55493f0ed0ebea..40f8467fd93fddf18907d1a9d55440164a9a522f 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -1,6 +1,9 @@ use super::super::map::RandomState; use super::HashSet; +use crate::panic::{catch_unwind, AssertUnwindSafe}; +use crate::sync::atomic::{AtomicU32, Ordering}; + #[test] fn test_zero_capacities() { type HS = HashSet; @@ -413,3 +416,71 @@ fn test_retain() { assert!(set.contains(&4)); assert!(set.contains(&6)); } + +#[test] +fn test_drain_filter() { + let mut x: HashSet<_> = [1].iter().copied().collect(); + let mut y: HashSet<_> = [1].iter().copied().collect(); + + x.drain_filter(|_| true); + y.drain_filter(|_| false); + assert_eq!(x.len(), 0); + assert_eq!(y.len(), 1); +} + +#[test] +fn test_drain_filter_drop_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Hash)] + struct D(i32); + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 1 { + panic!("panic in `drop`"); + } + } + } + + let mut set = (0..3).map(|i| D(i)).collect::>(); + + catch_unwind(move || { + drop(set.drain_filter(|_| { + PREDS.fetch_add(1, Ordering::SeqCst); + true + })) + }) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 3); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); +} + +#[test] +fn test_drain_filter_pred_panic_leak() { + static PREDS: AtomicU32 = AtomicU32::new(0); + static DROPS: AtomicU32 = AtomicU32::new(0); + + #[derive(PartialEq, Eq, PartialOrd, Hash)] + struct D; + impl Drop for D { + fn drop(&mut self) { + DROPS.fetch_add(1, Ordering::SeqCst); + } + } + + let mut set: HashSet<_> = (0..3).map(|_| D).collect(); + + catch_unwind(AssertUnwindSafe(|| { + drop(set.drain_filter(|_| match PREDS.fetch_add(1, Ordering::SeqCst) { + 0 => true, + _ => panic!(), + })) + })) + .ok(); + + assert_eq!(PREDS.load(Ordering::SeqCst), 1); + assert_eq!(DROPS.load(Ordering::SeqCst), 3); + assert_eq!(set.len(), 0); +} diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 01dbb48354825cc15713855fcb647e95b2dbff3f..d2baf4a1d1e6aeebee68622ef3b732fdfb884418 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -812,7 +812,7 @@ pub fn cargo( format!("CARGO_PROFILE_{}_{}", profile, name) }; - // See comment in librustc_llvm/build.rs for why this is necessary, largely llvm-config + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var()); if let Some(e) = env::var_os(util::dylib_path_var()) { @@ -829,9 +829,9 @@ pub fn cargo( // scripts can do less work (i.e. not building/requiring LLVM). if cmd == "check" || cmd == "clippy" || cmd == "fix" { // If we've not yet built LLVM, or it's stale, then bust - // the librustc_llvm cache. That will always work, even though it + // the rustc_llvm cache. That will always work, even though it // may mean that on the next non-check build we'll need to rebuild - // librustc_llvm. But if LLVM is stale, that'll be a tiny amount + // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparitively, and we'd likely need to rebuild it anyway, // so that's okay. if crate::native::prebuilt_llvm_config(self, target).is_err() { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 7814ca8e5bbcef1545235374dfd9e30ead5f4ef3..9d314e8452b9c58385336f707b7625f83242c2b7 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -560,7 +560,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } // Pass down configuration from the LLVM build into the build of - // librustc_llvm and librustc_codegen_llvm. + // rustc_llvm and rustc_codegen_llvm. // // Note that this is disabled if LLVM itself is disabled or we're in a check // build. If we are in a check build we still go ahead here presuming we've @@ -579,7 +579,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { cargo.env("CFG_LLVM_ROOT", s); } - // Some LLVM linker flags (-L and -l) may be needed to link librustc_llvm. + // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm. if let Some(ref s) = builder.config.llvm_ldflags { cargo.env("LLVM_LINKER_FLAGS", s); } diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index a0c79e38f9d8c135c7997ea8012c72c4f912fd3a..6cd850bc0bfaadfe8cc343f42ca86a8f0b811d2e 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -169,7 +169,6 @@ fn run(self, builder: &Builder<'_>) -> PathBuf { .define("LLVM_INCLUDE_TESTS", "OFF") .define("LLVM_INCLUDE_DOCS", "OFF") .define("LLVM_INCLUDE_BENCHMARKS", "OFF") - .define("WITH_POLLY", "OFF") .define("LLVM_ENABLE_TERMINFO", "OFF") .define("LLVM_ENABLE_LIBEDIT", "OFF") .define("LLVM_ENABLE_BINDINGS", "OFF") @@ -305,10 +304,6 @@ fn run(self, builder: &Builder<'_>) -> PathBuf { cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES"); } - if let Some(ref python) = builder.config.python { - cfg.define("PYTHON_EXECUTABLE", python); - } - configure_cmake(builder, target, &mut cfg, true); // FIXME: we don't actually need to build all LLVM tools and all LLVM diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py index 2a46eaadad6f9a1b121b706e77d45882e4690959..a5a1824c84e78be919f81ea5f4119d70686b7121 100644 --- a/src/etc/gdb_lookup.py +++ b/src/etc/gdb_lookup.py @@ -69,9 +69,9 @@ def lookup(valobj): else: return StdOldHashMapProvider(valobj) if rust_type == RustType.STD_HASH_SET: - hash_map = valobj["map"] + hash_map = valobj[valobj.type.fields()[0]] if is_hashbrown_hashmap(hash_map): - return StdHashMapProvider(hash_map, show_values=False) + return StdHashMapProvider(valobj, show_values=False) else: return StdOldHashMapProvider(hash_map, show_values=False) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index 67f99ec4e40b923ca315e0d630f63ab22edd6ee5..bae51e6f9ee93a394ba51718ebfe5b81a9f1c5a5 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -347,7 +347,7 @@ class StdHashMapProvider: self.valobj = valobj self.show_values = show_values - table = self.valobj["base"]["table"] + table = self.table() capacity = int(table["bucket_mask"]) + 1 ctrl = table["ctrl"]["pointer"] @@ -368,6 +368,18 @@ class StdHashMapProvider: if is_presented: self.valid_indices.append(idx) + def table(self): + if self.show_values: + hashbrown_hashmap = self.valobj["base"] + elif self.valobj.type.fields()[0].name == "map": + # BACKCOMPAT: rust 1.47 + # HashSet wraps std::collections::HashMap, which wraps hashbrown::HashMap + hashbrown_hashmap = self.valobj["map"]["base"] + else: + # HashSet wraps hashbrown::HashSet, which wraps hashbrown::HashMap + hashbrown_hashmap = self.valobj["base"]["map"] + return hashbrown_hashmap["table"] + def to_string(self): if self.show_values: return "HashMap(size={})".format(self.size) diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 13420fbaf0a759fa2aa0973b85d01ccc58fb8de9..3cee51982ba9f921eac8be4e9eeed216df09680b 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -94,7 +94,7 @@ def synthetic_lookup(valobj, dict): if rust_type == RustType.STD_HASH_SET: hash_map = valobj.GetChildAtIndex(0) if is_hashbrown_hashmap(hash_map): - return StdHashMapSyntheticProvider(hash_map, dict, show_values=False) + return StdHashMapSyntheticProvider(valobj, dict, show_values=False) else: return StdOldHashMapSyntheticProvider(hash_map, dict, show_values=False) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 19da75c35b456b4e12efdf826c5d61df852a35c2..64cb9837943b902b0b8fcdf9749859848c25e884 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -526,7 +526,7 @@ class StdHashMapSyntheticProvider: def update(self): # type: () -> None - table = self.valobj.GetChildMemberWithName("base").GetChildMemberWithName("table") + table = self.table() capacity = table.GetChildMemberWithName("bucket_mask").GetValueAsUnsigned() + 1 ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0) @@ -552,6 +552,17 @@ class StdHashMapSyntheticProvider: if is_present: self.valid_indices.append(idx) + def table(self): + # type: () -> SBValue + if self.show_values: + hashbrown_hashmap = self.valobj.GetChildMemberWithName("base") + else: + # BACKCOMPAT: rust 1.47 + # HashSet wraps either std HashMap or hashbrown::HashSet, which both + # wrap hashbrown::HashMap, so either way we "unwrap" twice. + hashbrown_hashmap = self.valobj.GetChildAtIndex(0).GetChildAtIndex(0) + return hashbrown_hashmap.GetChildMemberWithName("table") + def has_children(self): # type: () -> bool return True diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index f791979800f19d28bcc924451fdb6083e3860f57..9550c25f2fcfe5da1b78cc5b2ab743f3f1ab105f 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -5,7 +5,7 @@ Current std impls: std::collections::hash::set::HashSet is implemented in terms of... - std::collections::hash::map::HashMap is implemented in terms of... + hashbrown::set::HashSet is implemented in terms of... hashbrown::map::HashMap is implemented in terms of... hashbrown::raw::RawTable<(K, V)> @@ -50,22 +50,22 @@ - {{ size={map.base.table.items} }} + {{ size={base.map.table.items} }} - map.base.table.items - map.base.table.items + map.base.table.growth_left - map.base.hash_builder + base.map.table.items + base.map.table.items + base.map.table.growth_left + base.map.hash_builder - - map.base.table.items + + base.map.table.items - + n-- - (($T1*)map.base.table.ctrl.pointer)[-(i + 1)] + (($T1*)base.map.table.ctrl.pointer)[-(i + 1)] i++ diff --git a/src/test/mir-opt/graphviz.main.mir_map.0.dot b/src/test/mir-opt/graphviz.main.mir_map.0.dot index f5d8b84812a3e15f53490aac02a5e0971414c72a..df4f11f0f21693ba41f3eea94f223781aa62d80d 100644 --- a/src/test/mir-opt/graphviz.main.mir_map.0.dot +++ b/src/test/mir-opt/graphviz.main.mir_map.0.dot @@ -1,7 +1,7 @@ digraph Mir_0_3 { - graph [fontname="monospace"]; - node [fontname="monospace"]; - edge [fontname="monospace"]; + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; label=>; bb0__0_3 [shape="none", label=<
0
_0 = const ()
goto
>]; bb1__0_3 [shape="none", label=<
1
resume
>];