Auto merge of #76558 - tmandry:rollup-bskim2r, r=tmandry
Rollup of 7 pull requests Successful merges: - #74787 (Move `rustllvm` into `compiler/rustc_llvm`) - #76458 (Add drain_filter method to HashMap and HashSet) - #76472 (rustbuild: don't set PYTHON_EXECUTABLE and WITH_POLLY cmake vars since they are no longer supported by llvm) - #76497 (Use intra-doc links in `core::ptr`) - #76500 (Add -Zgraphviz_dark_mode and monospace font fix) - #76543 (Document btree's unwrap_unchecked) - #76556 (Revert #76285) Failed merges: r? `@ghost`
This commit is contained in:
commit
a1894e4afe
40 changed files with 617 additions and 212 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -403,8 +403,8 @@ impl Cursor {
|
|||
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<TokenTree> {
|
||||
self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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 @@ extern "C" {
|
|||
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(
|
||||
|
|
|
@ -47,26 +47,15 @@ impl ToInternal<token::DelimToken> for Delimiter {
|
|||
}
|
||||
}
|
||||
|
||||
impl
|
||||
FromInternal<(
|
||||
TreeAndJoint,
|
||||
Option<&'_ tokenstream::TokenTree>,
|
||||
&'_ ParseSess,
|
||||
&'_ mut Vec<Self>,
|
||||
)> for TokenTree<Group, Punct, Ident, Literal>
|
||||
impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
|
||||
for TokenTree<Group, Punct, Ident, Literal>
|
||||
{
|
||||
fn from_internal(
|
||||
((tree, is_joint), look_ahead, sess, stack): (
|
||||
TreeAndJoint,
|
||||
Option<&tokenstream::TokenTree>,
|
||||
&ParseSess,
|
||||
&mut Vec<Self>,
|
||||
),
|
||||
((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>),
|
||||
) -> 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 @@ impl server::TokenStreamIter for Rustc<'_> {
|
|||
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.
|
||||
|
|
|
@ -599,6 +599,7 @@ pub enum RenderOption {
|
|||
NoNodeStyles,
|
||||
|
||||
Monospace,
|
||||
DarkTheme,
|
||||
}
|
||||
|
||||
/// Returns vec holding all the default render options.
|
||||
|
@ -630,10 +631,23 @@ where
|
|||
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() {
|
||||
|
|
|
@ -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"
|
|
@ -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");
|
|
@ -1,4 +1,4 @@
|
|||
#include "rustllvm.h"
|
||||
#include "LLVMWrapper.h"
|
||||
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ArchiveWriter.h"
|
|
@ -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"
|
|
@ -1,6 +1,6 @@
|
|||
#include "llvm/Linker/Linker.h"
|
||||
|
||||
#include "rustllvm.h"
|
||||
#include "LLVMWrapper.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "rustllvm.h"
|
||||
#include "LLVMWrapper.h"
|
||||
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
@ -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"
|
|
@ -306,7 +306,11 @@ where
|
|||
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)?;
|
||||
|
|
|
@ -55,16 +55,28 @@ where
|
|||
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 @@ where
|
|||
pub fn write_node_label<W: Write, INIT, FINI>(
|
||||
block: BasicBlock,
|
||||
body: &Body<'_>,
|
||||
dark_mode: bool,
|
||||
w: &mut W,
|
||||
num_cols: u32,
|
||||
init: INIT,
|
||||
|
@ -100,8 +113,9 @@ where
|
|||
// Basic block number at the top.
|
||||
write!(
|
||||
w,
|
||||
r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
|
||||
attrs = r#"bgcolor="gray" align="center""#,
|
||||
r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
|
||||
bgcolor = if dark_mode { "dimgray" } else { "gray" },
|
||||
attrs = r#"align="center""#,
|
||||
colspan = num_cols,
|
||||
blk = block.index()
|
||||
)?;
|
||||
|
@ -134,11 +148,12 @@ fn write_node<W: Write>(
|
|||
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, ">];")
|
||||
}
|
||||
|
|
|
@ -262,7 +262,10 @@ impl<'a> TokenTreesReader<'a> {
|
|||
}
|
||||
_ => {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -822,15 +822,15 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -909,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||
"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],
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ trait Recover<Q: ?Sized> {
|
|||
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
|
||||
}
|
||||
|
||||
/// 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<T>(val: Option<T>) -> T {
|
||||
val.unwrap_or_else(|| {
|
||||
|
|
|
@ -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 @@ mod mut_ptr;
|
|||
/// 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 @@ mod mut_ptr;
|
|||
/// 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<T> {
|
|||
/// 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<T>(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<T>(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<T>(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<T>(x: *mut T, y: *mut T) {
|
|||
/// Note that even if the effectively copied size (`count * size_of::<T>()`) 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<T>(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<T>(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<T>(src: *const T) -> T {
|
||||
|
@ -723,11 +705,8 @@ pub unsafe fn read<T>(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<T>(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<T>(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<T>(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<T>(dst: *mut T, src: T) {
|
||||
|
@ -904,7 +878,7 @@ pub unsafe fn write<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -497,6 +497,50 @@ impl<K, V, S> HashMap<K, V, S> {
|
|||
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<i32, i32> = (0..8).map(|x| (x, x)).collect();
|
||||
/// let drained: HashMap<i32, i32> = map.drain_filter(|k, _v| k % 2 == 0).collect();
|
||||
///
|
||||
/// let mut evens = drained.keys().copied().collect::<Vec<_>>();
|
||||
/// let mut odds = map.keys().copied().collect::<Vec<_>>();
|
||||
/// 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<F>(&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 @@ impl<'a, K, V> Drain<'a, 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 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ impl<K: Debug, V: Debug, S> Debug for RawEntryMut<'_, K, V, S> {
|
|||
}
|
||||
|
||||
#[unstable(feature = "hash_raw_entry", issue = "56167")]
|
||||
impl<K: Debug, V: Debug> Debug for RawOccupiedEntryMut<'_, K, V> {
|
||||
impl<K: Debug, V: Debug, S> 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 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "hash_drain_filter", issue = "59618")]
|
||||
impl<K, V, F> 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<usize>) {
|
||||
self.base.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "hash_drain_filter", issue = "59618")]
|
||||
impl<K, V, F> 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 } => {
|
||||
|
|
|
@ -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<I: IntoIterator<Item = Self::Item>>(self, other: I) -> bool;
|
||||
}
|
||||
|
||||
impl<T: Iterator> EqSorted for T
|
||||
where
|
||||
T::Item: Eq + Ord,
|
||||
{
|
||||
fn eq_sorted<I: IntoIterator<Item = Self::Item>>(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<i32, i32> = 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::<HashMap<_, _>>();
|
||||
|
||||
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::<HashMap<_, _>>();
|
||||
|
||||
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::<HashMap<_, _>>();
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::hash::{BuildHasher, Hash};
|
|||
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 super::map::{self, HashMap, Keys, RandomState};
|
|||
/// // 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<T, S = RandomState> {
|
||||
map: HashMap<T, (), S>,
|
||||
base: base::HashSet<T, S>,
|
||||
}
|
||||
|
||||
impl<T> HashSet<T, RandomState> {
|
||||
|
@ -125,7 +128,7 @@ impl<T> HashSet<T, RandomState> {
|
|||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn new() -> HashSet<T, RandomState> {
|
||||
HashSet { map: HashMap::new() }
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Creates an empty `HashSet` with the specified capacity.
|
||||
|
@ -143,7 +146,7 @@ impl<T> HashSet<T, RandomState> {
|
|||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn with_capacity(capacity: usize) -> HashSet<T, RandomState> {
|
||||
HashSet { map: HashMap::with_capacity(capacity) }
|
||||
HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, Default::default()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +163,7 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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<i32> = (0..8).collect();
|
||||
/// let drained: HashSet<i32> = set.drain_filter(|v| v % 2 == 0).collect();
|
||||
///
|
||||
/// let mut evens = drained.into_iter().collect::<Vec<_>>();
|
||||
/// let mut odds = set.into_iter().collect::<Vec<_>>();
|
||||
/// 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<F>(&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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[inline]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub fn with_hasher(hasher: S) -> HashSet<T, S> {
|
||||
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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[inline]
|
||||
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
|
||||
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
|
||||
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 @@ impl<T, S> HashSet<T, S> {
|
|||
#[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 @@ where
|
|||
#[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 @@ where
|
|||
#[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 @@ where
|
|||
#[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 @@ where
|
|||
#[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 @@ where
|
|||
T: Borrow<Q>,
|
||||
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 @@ where
|
|||
T: Borrow<Q>,
|
||||
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 @@ where
|
|||
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 @@ where
|
|||
{
|
||||
// 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 @@ where
|
|||
{
|
||||
// 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 @@ where
|
|||
#[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 @@ where
|
|||
#[inline]
|
||||
#[stable(feature = "set_recovery", since = "1.9.0")]
|
||||
pub fn replace(&mut self, value: T) -> Option<T> {
|
||||
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 @@ where
|
|||
T: Borrow<Q>,
|
||||
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 @@ where
|
|||
T: Borrow<Q>,
|
||||
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 @@ where
|
|||
/// assert_eq!(set.len(), 3);
|
||||
/// ```
|
||||
#[stable(feature = "retain_hash_collection", since = "1.18.0")]
|
||||
pub fn retain<F>(&mut self, mut f: F)
|
||||
pub fn retain<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnMut(&T) -> bool,
|
||||
{
|
||||
self.map.retain(|k, _| f(k));
|
||||
self.base.retain(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -949,17 +987,17 @@ where
|
|||
{
|
||||
#[inline]
|
||||
fn extend<I: IntoIterator<Item = T>>(&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 @@ where
|
|||
|
||||
#[inline]
|
||||
fn extend_one(&mut self, &item: &'a T) {
|
||||
self.map.insert(item, ());
|
||||
self.base.insert(item);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -993,7 +1031,7 @@ where
|
|||
/// Creates an empty `HashSet<T, S>` with the `Default` value for the hasher.
|
||||
#[inline]
|
||||
fn default() -> HashSet<T, S> {
|
||||
HashSet { map: HashMap::default() }
|
||||
HashSet { base: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1137,7 +1175,7 @@ where
|
|||
/// [`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<K> {
|
||||
iter: map::IntoIter<K, ()>,
|
||||
base: base::IntoIter<K>,
|
||||
}
|
||||
|
||||
/// A draining iterator over the items of a `HashSet`.
|
||||
|
@ -1159,7 +1197,20 @@ pub struct IntoIter<K> {
|
|||
/// [`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<T, S> IntoIterator for HashSet<T, S> {
|
|||
/// ```
|
||||
#[inline]
|
||||
fn into_iter(self) -> IntoIter<T> {
|
||||
IntoIter { iter: self.map.into_iter() }
|
||||
IntoIter { base: self.base.into_iter() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1258,7 +1309,7 @@ impl<T, S> IntoIterator for HashSet<T, S> {
|
|||
impl<K> 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<usize>) {
|
||||
self.iter.size_hint()
|
||||
self.base.size_hint()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<K> 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<K> Iterator for IntoIter<K> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<K> {
|
||||
self.iter.next().map(|(k, _)| k)
|
||||
self.base.next()
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
self.base.size_hint()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<K> ExactSizeIterator for IntoIter<K> {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
self.base.len()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "fused", since = "1.26.0")]
|
||||
|
@ -1317,8 +1368,7 @@ impl<K> FusedIterator for IntoIter<K> {}
|
|||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl<K: fmt::Debug> fmt::Debug for IntoIter<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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1328,18 +1378,18 @@ impl<'a, K> Iterator for Drain<'a, K> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<K> {
|
||||
self.iter.next().map(|(k, _)| k)
|
||||
self.base.next()
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
self.base.size_hint()
|
||||
}
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<K> 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<K> FusedIterator for Drain<'_, K> {}
|
|||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl<K: fmt::Debug> 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<K, F> Iterator for DrainFilter<'_, K, F>
|
||||
where
|
||||
F: FnMut(&K) -> bool,
|
||||
{
|
||||
type Item = K;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<K> {
|
||||
self.base.next()
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.base.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "hash_drain_filter", issue = "59618")]
|
||||
impl<K, F> 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 { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<i32>;
|
||||
|
@ -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::<HashSet<_>>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -812,7 +812,7 @@ impl<'a> Builder<'a> {
|
|||
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 @@ impl<'a> Builder<'a> {
|
|||
// 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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -169,7 +169,6 @@ impl Step for Llvm {
|
|||
.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 @@ impl Step for Llvm {
|
|||
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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
Current std impls:
|
||||
std::collections::hash::set::HashSet<K, S> is implemented in terms of...
|
||||
std::collections::hash::map::HashMap<K, V, S> is implemented in terms of...
|
||||
hashbrown::set::HashSet<K, S> is implemented in terms of...
|
||||
hashbrown::map::HashMap<K, V, S> is implemented in terms of...
|
||||
hashbrown::raw::RawTable<(K, V)>
|
||||
|
||||
|
@ -50,22 +50,22 @@
|
|||
</Type>
|
||||
|
||||
<Type Name="std::collections::hash::set::HashSet<*,*>">
|
||||
<DisplayString>{{ size={map.base.table.items} }}</DisplayString>
|
||||
<DisplayString>{{ size={base.map.table.items} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[size]">map.base.table.items</Item>
|
||||
<Item Name="[capacity]">map.base.table.items + map.base.table.growth_left</Item>
|
||||
<Item Name="[state]">map.base.hash_builder</Item>
|
||||
<Item Name="[size]">base.map.table.items</Item>
|
||||
<Item Name="[capacity]">base.map.table.items + base.map.table.growth_left</Item>
|
||||
<Item Name="[state]">base.map.hash_builder</Item>
|
||||
|
||||
<CustomListItems>
|
||||
<Variable Name="i" InitialValue="0" />
|
||||
<Variable Name="n" InitialValue="map.base.table.items" />
|
||||
<Size>map.base.table.items</Size>
|
||||
<Variable Name="n" InitialValue="base.map.table.items" />
|
||||
<Size>base.map.table.items</Size>
|
||||
<Loop>
|
||||
<Break Condition="n == 0" />
|
||||
<If Condition="(map.base.table.ctrl.pointer[i] & 0x80) == 0">
|
||||
<If Condition="(base.map.table.ctrl.pointer[i] & 0x80) == 0">
|
||||
<!-- Bucket is populated -->
|
||||
<Exec>n--</Exec>
|
||||
<Item>(($T1*)map.base.table.ctrl.pointer)[-(i + 1)]</Item>
|
||||
<Item>(($T1*)base.map.table.ctrl.pointer)[-(i + 1)]</Item>
|
||||
</If>
|
||||
<Exec>i++</Exec>
|
||||
</Loop>
|
||||
|
|
|
@ -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=<fn main() -> ()<br align="left"/>>;
|
||||
bb0__0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">0</td></tr><tr><td align="left" balign="left">_0 = const ()<br/></td></tr><tr><td align="left">goto</td></tr></table>>];
|
||||
bb1__0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">1</td></tr><tr><td align="left">resume</td></tr></table>>];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue