"serde_derive",
"serde_json",
"tempfile",
- "tester",
"winapi 0.3.6",
]
"rustc_data_structures",
"rustc_errors",
"rustc_fs_util",
+ "rustc_index",
"rustc_macros",
"rustc_target",
"scoped-tls",
"rustc_errors",
"rustc_fs_util",
"rustc_incremental",
+ "rustc_index",
"rustc_target",
"serialize",
"syntax",
"rustc-hash",
"rustc-rayon",
"rustc-rayon-core",
+ "rustc_index",
"serialize",
"smallvec",
"stable_deref_trait",
"syntax_pos",
]
+[[package]]
+name = "rustc_index"
+version = "0.0.0"
+dependencies = [
+ "serialize",
+ "smallvec",
+]
+
[[package]]
name = "rustc_interface"
version = "0.0.0"
"log",
"rustc",
"rustc_data_structures",
+ "rustc_index",
"rustc_target",
"syntax",
"syntax_pos",
"rustc",
"rustc_data_structures",
"rustc_errors",
+ "rustc_index",
"rustc_target",
"serialize",
"smallvec",
"rustc_apfloat",
"rustc_data_structures",
"rustc_errors",
+ "rustc_index",
"rustc_lexer",
"rustc_target",
"serialize",
"bitflags",
"log",
"rustc_data_structures",
+ "rustc_index",
"serialize",
"syntax_pos",
]
"rustc",
"rustc_data_structures",
"rustc_errors",
+ "rustc_index",
"rustc_target",
"smallvec",
"syntax",
"log",
"rustc_data_structures",
"rustc_errors",
+ "rustc_index",
"rustc_lexer",
"rustc_target",
"scoped-tls",
"arena",
"cfg-if",
"rustc_data_structures",
+ "rustc_index",
"rustc_macros",
"scoped-tls",
"serialize",
"std",
]
-[[package]]
-name = "term"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
-dependencies = [
- "kernel32-sys",
- "winapi 0.2.8",
-]
-
[[package]]
name = "term"
version = "0.6.0"
"term 0.0.0",
]
-[[package]]
-name = "tester"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e812cb26c597f86a49b26dbb58b878bd2a2b4b93fc069dc39499228fe556ff6"
-dependencies = [
- "getopts",
- "libc",
- "term 0.4.6",
-]
-
[[package]]
name = "textwrap"
version = "0.11.0"
* `curl`
* `git`
* `ssl` which comes in `libssl-dev` or `openssl-devel`
- * `pkg-config` if you are on compiling on Linux and targeting Linux
+ * `pkg-config` if you are compiling on Linux and targeting Linux
2. Clone the [source] with `git`:
set -ex
source shared.sh
-VERSION=7.51.0
+VERSION=7.66.0
-curl http://cool.haxx.se/download/curl-$VERSION.tar.bz2 | tar xjf -
+curl https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/curl-$VERSION.tar.xz \
+ | xz --decompress \
+ | tar xf -
mkdir curl-build
cd curl-build
v.pop().unwrap();
assert_eq!(*v0, 13);
}
+
+// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on:
+//
+// ```
+// unsafe impl<T: ?Sized> IsZero for *mut T {
+// fn is_zero(&self) -> bool {
+// (*self).is_null()
+// }
+// }
+// ```
+//
+// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`,
+// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component.
+// That is, a fat pointer can be “null” without being made entirely of zero bits.
+#[test]
+fn vec_macro_repeating_null_raw_fat_pointer() {
+ let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn();
+ let vtable = dbg!(ptr_metadata(raw_dyn));
+ let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable);
+ assert!(null_raw_dyn.is_null());
+
+ let vec = vec![null_raw_dyn; 1];
+ dbg!(ptr_metadata(vec[0]));
+ assert!(vec[0] == null_raw_dyn);
+
+ // Polyfill for https://github.com/rust-lang/rfcs/pull/2580
+
+ fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () {
+ unsafe {
+ std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable
+ }
+ }
+
+ fn ptr_from_raw_parts(data: *mut (), vtable: *mut()) -> *mut dyn Fn() {
+ unsafe {
+ std::mem::transmute::<DynRepr, *mut dyn Fn()>(DynRepr {
+ data,
+ vtable
+ })
+ }
+ }
+
+ #[repr(C)]
+ struct DynRepr {
+ data: *mut (),
+ vtable: *mut (),
+ }
+}
impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
-unsafe impl<T: ?Sized> IsZero for *const T {
+unsafe impl<T> IsZero for *const T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}
-unsafe impl<T: ?Sized> IsZero for *mut T {
+unsafe impl<T> IsZero for *mut T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}
+// `Option<&T>`, `Option<&mut T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
+// For fat pointers, the bytes that would be the pointer metadata in the `Some` variant
+// are padding in the `None` variant, so ignoring them and zero-initializing instead is ok.
+
+unsafe impl<T: ?Sized> IsZero for Option<&T> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.is_none()
+ }
+}
+
+unsafe impl<T: ?Sized> IsZero for Option<&mut T> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.is_none()
+ }
+}
+
+unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.is_none()
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// Common trait implementations for Vec
/// make a difference in release builds (where a loop that has no side-effects
/// is easily detected and eliminated), but is often a big win for debug builds.
///
-/// Note that `ptr::drop_in_place` already performs this check, so if your workload
-/// can be reduced to some small number of drop_in_place calls, using this is
-/// unnecessary. In particular note that you can drop_in_place a slice, and that
+/// Note that [`drop_in_place`] already performs this check, so if your workload
+/// can be reduced to some small number of [`drop_in_place`] calls, using this is
+/// unnecessary. In particular note that you can [`drop_in_place`] a slice, and that
/// will do a single needs_drop check for all the values.
///
/// Types like Vec therefore just `drop_in_place(&mut self[..])` without using
-/// needs_drop explicitly. Types like `HashMap`, on the other hand, have to drop
+/// `needs_drop` explicitly. Types like [`HashMap`], on the other hand, have to drop
/// values one at a time and should use this API.
///
+/// [`drop_in_place`]: ../ptr/fn.drop_in_place.html
+/// [`HashMap`]: ../../std/collections/struct.HashMap.html
///
/// # Examples
///
//! # Options and pointers ("nullable" pointers)
//!
//! Rust's pointer types must always point to a valid location; there are
-//! no "null" pointers. Instead, Rust has *optional* pointers, like
+//! no "null" references. Instead, Rust has *optional* pointers, like
//! the optional owned box, [`Option`]`<`[`Box<T>`]`>`.
//!
//! The following example uses [`Option`] to create an optional box of
use crate::intrinsics::{assume, exact_div, unchecked_sub, is_aligned_and_not_null};
use crate::isize;
use crate::iter::*;
-use crate::ops::{FnMut, Try, self};
+use crate::ops::{FnMut, self};
use crate::option::Option;
use crate::option::Option::{None, Some};
use crate::result::Result;
self.next_back()
}
- #[inline]
- fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R where
- Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
- {
- // manual unrolling is needed when there are conditional exits from the loop
- let mut accum = init;
- unsafe {
- while len!(self) >= 4 {
- accum = f(accum, next_unchecked!(self))?;
- accum = f(accum, next_unchecked!(self))?;
- accum = f(accum, next_unchecked!(self))?;
- accum = f(accum, next_unchecked!(self))?;
- }
- while !is_empty!(self) {
- accum = f(accum, next_unchecked!(self))?;
- }
- }
- Try::from_ok(accum)
- }
-
- #[inline]
- fn fold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc
- where Fold: FnMut(Acc, Self::Item) -> Acc,
- {
- // Let LLVM unroll this, rather than using the default
- // impl that would force the manual unrolling above
- let mut accum = init;
- while let Some(x) = self.next() {
- accum = f(accum, x);
- }
- accum
- }
-
#[inline]
#[rustc_inherit_overflow_checks]
fn position<P>(&mut self, mut predicate: P) -> Option<usize> where
Some(next_back_unchecked!(self))
}
}
-
- #[inline]
- fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
- Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
- {
- // manual unrolling is needed when there are conditional exits from the loop
- let mut accum = init;
- unsafe {
- while len!(self) >= 4 {
- accum = f(accum, next_back_unchecked!(self))?;
- accum = f(accum, next_back_unchecked!(self))?;
- accum = f(accum, next_back_unchecked!(self))?;
- accum = f(accum, next_back_unchecked!(self))?;
- }
- // inlining is_empty everywhere makes a huge performance difference
- while !is_empty!(self) {
- accum = f(accum, next_back_unchecked!(self))?;
- }
- }
- Try::from_ok(accum)
- }
-
- #[inline]
- fn rfold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc
- where Fold: FnMut(Acc, Self::Item) -> Acc,
- {
- // Let LLVM unroll this, rather than using the default
- // impl that would force the manual unrolling above
- let mut accum = init;
- while let Some(x) = self.next_back() {
- accum = f(accum, x);
- }
- accum
- }
}
#[stable(feature = "fused", since = "1.26.0")]
rustc_target = { path = "../librustc_target" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
syntax = { path = "../libsyntax" }
[] steal_mir: rustc::ty::steal::Steal<rustc::mir::Body<$tcx>>,
[] mir: rustc::mir::Body<$tcx>,
[] steal_promoted: rustc::ty::steal::Steal<
- rustc_data_structures::indexed_vec::IndexVec<
+ rustc_index::vec::IndexVec<
rustc::mir::Promoted,
rustc::mir::Body<$tcx>
>
>,
- [] promoted: rustc_data_structures::indexed_vec::IndexVec<
+ [] promoted: rustc_index::vec::IndexVec<
rustc::mir::Promoted,
rustc::mir::Body<$tcx>
>,
[decode] specialization_graph: rustc::traits::specialization_graph::Graph,
[] region_scope_tree: rustc::middle::region::ScopeTree,
[] item_local_set: rustc::util::nodemap::ItemLocalSet,
- [decode] mir_const_qualif: rustc_data_structures::bit_set::BitSet<rustc::mir::Local>,
+ [decode] mir_const_qualif: rustc_index::bit_set::BitSet<rustc::mir::Local>,
[] trait_impls_of: rustc::ty::trait_def::TraitImpls,
[] dropck_outlives:
rustc::infer::canonical::Canonical<'tcx,
use errors::Diagnostic;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use smallvec::SmallVec;
use rustc_data_structures::sync::{Lrc, Lock, AtomicU32, Ordering};
use std::env;
data: Option<Lrc<DepGraphData>>,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct DepNodeIndex { .. }
}
use crate::dep_graph::DepNode;
use crate::ich::Fingerprint;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
-newtype_index! {
+rustc_index::newtype_index! {
pub struct SerializedDepNodeIndex { .. }
}
use crate::ty::{self, TyCtxt};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::fmt;
use std::u32;
-newtype_index! {
+rustc_index::newtype_index! {
pub struct CrateId {
ENCODABLE = custom
}
impl rustc_serialize::UseSpecializedEncodable for CrateNum {}
impl rustc_serialize::UseSpecializedDecodable for CrateNum {}
-newtype_index! {
+rustc_index::newtype_index! {
/// A DefIndex is an index into the hir-map for a crate, identifying a
/// particular definition. It should really be considered an interned
/// shorthand for a particular DefPath.
use crate::util::nodemap::{DefIdMap, NodeMap};
use errors::Applicability;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_data_structures::sync::Lrc;
// lower attributes (we use the AST version) there is nowhere to keep
// the `HirId`s. We don't actually need HIR version of attributes anyway.
Attribute {
+ item: AttrItem {
+ path: attr.path.clone(),
+ tokens: self.lower_token_stream(attr.tokens.clone()),
+ },
id: attr.id,
style: attr.style,
- path: attr.path.clone(),
- tokens: self.lower_token_stream(attr.tokens.clone()),
is_sugared_doc: attr.is_sugared_doc,
span: attr.span,
}
) -> hir::Expr {
// expand <head>
let mut head = self.lower_expr(head);
- let head_sp = head.span;
let desugared_span = self.mark_span_with_reason(
DesugaringKind::ForLoop,
- head_sp,
+ head.span,
None,
);
head.span = desugared_span;
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
- let iter = P(self.expr_ident(head_sp, iter, iter_pat_nid));
- let ref_mut_iter = self.expr_mut_addr_of(head_sp, iter);
+ let iter = P(self.expr_ident(desugared_span, iter, iter_pat_nid));
+ let ref_mut_iter = self.expr_mut_addr_of(desugared_span, iter);
let next_path = &[sym::iter, sym::Iterator, sym::next];
let next_expr = P(self.expr_call_std_path(
- head_sp,
+ desugared_span,
next_path,
hir_vec![ref_mut_iter],
));
let arms = hir_vec![pat_arm, break_arm];
- self.expr_match(head_sp, next_expr, arms, hir::MatchSource::ForLoopDesugar)
+ self.expr_match(desugared_span, next_expr, arms, hir::MatchSource::ForLoopDesugar)
};
- let match_stmt = self.stmt_expr(head_sp, match_expr);
+ let match_stmt = self.stmt_expr(desugared_span, match_expr);
- let next_expr = P(self.expr_ident(head_sp, next_ident, next_pat_hid));
+ let next_expr = P(self.expr_ident(desugared_span, next_ident, next_pat_hid));
// `let mut __next`
let next_let = self.stmt_let_pat(
let pat = self.lower_pat(pat);
let pat_let = self.stmt_let_pat(
ThinVec::new(),
- head_sp,
+ desugared_span,
Some(next_expr),
pat,
hir::LocalSource::ForLoopDesugar,
let into_iter_path =
&[sym::iter, sym::IntoIterator, sym::into_iter];
P(self.expr_call_std_path(
- head_sp,
+ desugared_span,
into_iter_path,
hir_vec![head],
))
};
let match_expr = P(self.expr_match(
- head_sp,
+ desugared_span,
into_iter_expr,
hir_vec![iter_arm],
hir::MatchSource::ForLoopDesugar,
// surrounding scope of the `match` since the `match` is not a terminating scope.
//
// Also, add the attributes to the outer returned expr node.
- self.expr_drop_temps(head_sp, match_expr, e.attrs.clone())
+ self.expr_drop_temps(desugared_span, match_expr, e.attrs.clone())
}
/// Desugar `ExprKind::Try` from: `<expr>?` into:
use crate::hir::def_id::{LOCAL_CRATE, CrateNum};
use crate::hir::intravisit::{Visitor, NestedVisitorMap};
use rustc_data_structures::svh::Svh;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use crate::ich::Fingerprint;
use crate::middle::cstore::CrateStore;
use crate::session::CrateDisambiguator;
use crate::util::nodemap::NodeMap;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{IndexVec};
+use rustc_index::vec::{IndexVec};
use rustc_data_structures::stable_hasher::StableHasher;
use std::borrow::Borrow;
use std::fmt::Write;
use rustc_target::spec::abi::Abi;
use rustc_data_structures::svh::Svh;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use syntax::ast::{self, Name, NodeId};
use syntax::source_map::Spanned;
use syntax::ext::base::MacroKind;
CRATE_HIR_ID
}
+ /// When on a match arm tail expression or on a match arm, give back the enclosing `match`
+ /// expression.
+ ///
+ /// Used by error reporting when there's a type error in a match arm caused by the `match`
+ /// expression needing to be unit.
+ pub fn get_match_if_cause(&self, hir_id: HirId) -> Option<&Expr> {
+ for (_, node) in ParentHirIterator::new(hir_id, &self) {
+ match node {
+ Node::Item(_) |
+ Node::ForeignItem(_) |
+ Node::TraitItem(_) |
+ Node::ImplItem(_) => break,
+ Node::Expr(expr) => match expr.kind {
+ ExprKind::Match(_, _, _) => return Some(expr),
+ _ => {}
+ },
+ Node::Stmt(stmt) => match stmt.kind {
+ StmtKind::Local(_) => break,
+ _ => {}
+ }
+ _ => {}
+ }
+ }
+ None
+ }
+
/// Returns the nearest enclosing scope. A scope is roughly an item or block.
pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option<HirId> {
for (hir_id, node) in ParentHirIterator::new(hir_id, &self) {
// Hack to ensure that we don't try to access the private parts of `ItemLocalId` in this module.
mod item_local_id_inner {
- use rustc_data_structures::indexed_vec::Idx;
+ use rustc_index::vec::Idx;
use rustc_macros::HashStable;
- newtype_index! {
+ rustc_index::newtype_index! {
/// An `ItemLocalId` uniquely identifies something within a given "item-like";
/// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no
/// guarantee that the numerical value of a given `ItemLocalId` corresponds to
}
}
+impl_stable_hash_for!(struct ::syntax::ast::AttrItem {
+ path,
+ tokens,
+});
+
impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
// Make sure that these have been filtered out.
debug_assert!(!self.is_sugared_doc);
let ast::Attribute {
+ ref item,
id: _,
style,
- ref path,
- ref tokens,
is_sugared_doc: _,
span,
} = *self;
+ item.hash_stable(hcx, hasher);
style.hash_stable(hcx, hasher);
- path.hash_stable(hcx, hasher);
- for tt in tokens.trees() {
- tt.hash_stable(hcx, hasher);
- }
span.hash_stable(hcx, hasher);
}
}
use crate::ty::flags::FlagComputation;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use smallvec::SmallVec;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
use crate::infer::region_constraints::MemberConstraint;
use crate::mir::interpret::ConstValue;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
use rustc_serialize::UseSpecializedDecodable;
use smallvec::SmallVec;
use crate::infer::InferCtxtBuilder;
use crate::infer::{InferCtxt, InferOk, InferResult};
use crate::mir::interpret::ConstValue;
-use rustc_data_structures::indexed_vec::Idx;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::Idx;
+use rustc_index::vec::IndexVec;
use std::fmt::Debug;
use syntax_pos::DUMMY_SP;
use crate::traits::query::{Fallible, NoSolution};
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use smallvec::SmallVec;
use std::fmt;
use syntax_pos::Span;
use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::unify as ut;
use crate::hir::def_id::DefId;
use crate::ty::query::Providers;
use rustc_target::spec::abi::Abi::RustIntrinsic;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use syntax_pos::{Span, sym};
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
use crate::hir;
use crate::ty::{self, DefIdTree, TyCtxt};
use crate::ty::query::Providers;
-use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_index::vec::Idx;
use rustc_macros::HashStable;
use syntax::source_map;
use syntax_pos::{Span, DUMMY_SP};
Remainder(FirstStatementIndex)
}
-newtype_index! {
+rustc_index::newtype_index! {
/// Represents a subscope of `block` for a binding that is introduced
/// by `block.stmts[first_statement_index]`. Such subscopes represent
/// a suffix of the block. Note that each subscope does not include
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::sync::{RwLock, MappedReadGuard, ReadGuard};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
InvalidProgramInfo, ResourceExhaustionInfo, UndefinedBehaviorInfo,
};
-pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue};
+pub use self::value::{Scalar, ScalarMaybeUndef, RawConst, ConstValue, get_slice_bytes};
pub use self::allocation::{Allocation, AllocationExtra, Relocations, UndefMask};
Scalar(v),
Undef
});
+
+/// Gets the bytes of a constant slice value.
+pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
+ if let ConstValue::Slice { data, start, end } = val {
+ let len = end - start;
+ data.get_bytes(
+ cx,
+ // invent a pointer, only the offset is relevant anyway
+ Pointer::new(AllocId(0), Size::from_bytes(start as u64)),
+ Size::from_bytes(len as u64),
+ ).unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
+ } else {
+ bug!("expected const slice, but found another const value");
+ }
+}
};
use polonius_engine::Atom;
-use rustc_data_structures::bit_set::BitMatrix;
+use rustc_index::bit_set::BitMatrix;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::sync::MappedReadGuard;
use rustc_macros::HashStable;
///////////////////////////////////////////////////////////////////////////
// Variables and temps
-newtype_index! {
+rustc_index::newtype_index! {
pub struct Local {
derive [HashStable]
DEBUG_FORMAT = "_{}",
///////////////////////////////////////////////////////////////////////////
// BasicBlock
-newtype_index! {
+rustc_index::newtype_index! {
pub struct BasicBlock {
derive [HashStable]
DEBUG_FORMAT = "bb{}",
/// need neither the `V` parameter for `Index` nor the `T` for `Field`.
pub type ProjectionKind = ProjectionElem<(), ()>;
-newtype_index! {
+rustc_index::newtype_index! {
pub struct Field {
derive [HashStable]
DEBUG_FORMAT = "field[{}]"
///////////////////////////////////////////////////////////////////////////
// Scopes
-newtype_index! {
+rustc_index::newtype_index! {
pub struct SourceScope {
derive [HashStable]
DEBUG_FORMAT = "scope[{}]",
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct Promoted {
derive [HashStable]
DEBUG_FORMAT = "promoted[{}]"
pub unsafe_blocks: Lrc<[(hir::HirId, bool)]>,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct GeneratorSavedLocal {
derive [HashStable]
DEBUG_FORMAT = "_{}",
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use super::*;
cache_on_disk_if { key.is_local() }
load_cached(tcx, id) {
let promoted: Option<
- rustc_data_structures::indexed_vec::IndexVec<
+ rustc_index::vec::IndexVec<
crate::mir::Promoted,
crate::mir::Body<'tcx>
>> = tcx.queries.on_disk_cache.try_load_query_result(tcx, id);
"show extended diagnostic help"),
terminal_width: Option<usize> = (None, parse_opt_uint, [UNTRACKED],
"set the current terminal width"),
+ panic_abort_tests: bool = (false, parse_bool, [TRACKED],
+ "support compiling tests with panic=abort"),
continue_parse_after_error: bool = (false, parse_bool, [TRACKED],
"attempt to recover from parse errors (experimental)"),
dep_tasks: bool = (false, parse_bool, [UNTRACKED],
use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
use crate::hir;
-use rustc_data_structures::bit_set::GrowableBitSet;
+use rustc_index::bit_set::GrowableBitSet;
use rustc_data_structures::sync::Lock;
use rustc_target::spec::abi::Abi;
use syntax::attr;
use rustc_data_structures::stable_hasher::{
HashStable, StableHasher, StableVec, hash_stable_hashmap,
};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::sharded::ShardedHashMap;
use rustc_data_structures::sync::{Lrc, Lock, WorkerLocal};
use std::any::Any;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct UserTypeAnnotationIndex {
derive [HashStable]
DEBUG_FORMAT = "UserType({})",
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
use crate::ty::GeneratorSubsts;
use crate::ty::subst::Subst;
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_index::vec::{IndexVec, Idx};
pub use rustc_target::abi::*;
use rustc_target::spec::{HasTargetSpec, abi::Abi as SpecAbi};
use smallvec;
use rustc_data_structures::fx::FxIndexMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
+use rustc_index::vec::{Idx, IndexVec};
use crate::hir;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
/// "Universes" are used during type- and trait-checking in the
/// presence of `for<..>` binders to control what sets of names are
/// visible. Universes are arranged into a tree: the root universe
use crate::ty::util::NeedsDrop;
use crate::ty::subst::SubstsRef;
use crate::util::nodemap::{DefIdSet, DefIdMap, ItemLocalSet};
-use crate::util::common::{ErrorReported};
+use crate::util::common::ErrorReported;
use crate::util::profiling::ProfileCategory::*;
use rustc_data_structures::svh::Svh;
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::StableVec;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_data_structures::sync::{Lrc, Lock, HashMapExt, Once};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use rustc_serialize::{
Decodable, Decoder, Encodable, Encoder, SpecializedDecoder, SpecializedEncoder,
UseSpecializedDecodable, UseSpecializedEncodable, opaque,
use crate::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
use crate::ty::error::{ExpectedFound, TypeError};
-use crate::mir::interpret::{ConstValue, Scalar};
+use crate::mir::interpret::{ConstValue, get_slice_bytes, Scalar};
use std::rc::Rc;
use std::iter;
use rustc_target::spec::abi;
// FIXME(const_generics): we should either handle `Scalar::Ptr` or add a comment
// saying that we're not handling it intentionally.
- // FIXME(const_generics): handle `ConstValue::ByRef` and `ConstValue::Slice`.
+ (a_val @ ConstValue::Slice { .. }, b_val @ ConstValue::Slice { .. }) => {
+ let a_bytes = get_slice_bytes(&tcx, a_val);
+ let b_bytes = get_slice_bytes(&tcx, b_val);
+ if a_bytes == b_bytes {
+ Ok(tcx.mk_const(ty::Const {
+ val: a_val,
+ ty: a.ty,
+ }))
+ } else {
+ Err(TypeError::ConstMismatch(expected_found(relation, &a, &b)))
+ }
+ }
+
+ // FIXME(const_generics): handle `ConstValue::ByRef`.
// FIXME(const_generics): this is wrong, as it is a projection
(ConstValue::Unevaluated(a_def_id, a_substs),
use crate::ty::{self, Lift, Ty, TyCtxt, InferConst};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::print::{FmtPrinter, Printer};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use smallvec::SmallVec;
use crate::mir::interpret;
use crate::mir::interpret::ConstValue;
use crate::middle::region;
use polonius_engine::Atom;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc_macros::HashStable;
use crate::ty::subst::{InternalSubsts, Subst, SubstsRef, GenericArg, GenericArgKind};
use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFoldable};
use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv};
-use crate::ty::layout::VariantIdx;
+use crate::ty::layout::{Size, Integer, IntegerExt, VariantIdx};
use crate::util::captures::Captures;
use crate::mir::interpret::{Scalar, GlobalId};
use std::ops::Range;
use rustc_target::spec::abi;
use syntax::ast::{self, Ident};
+use syntax::attr::{SignedInt, UnsignedInt};
use syntax::symbol::{kw, InternedString};
use self::InferTy::*;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
/// A [De Bruijn index][dbi] is a standard means of representing
/// regions (and perhaps later types) in a higher-ranked setting. In
/// particular, imagine a type like this:
pub index: u32,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct RegionVid {
DEBUG_FORMAT = custom,
}
FreshFloatTy(u32),
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct BoundVar { .. }
}
ty: Ty<'tcx>,
) -> Option<u128> {
assert_eq!(self.ty, ty);
+ // This is purely an optimization -- layout_of is a pretty expensive operation,
+ // but if we can determine the size without calling it, we don't need all that complexity
+ // (hashing, caching, etc.). As such, try to skip it.
+ let size = match ty.kind {
+ ty::Bool => Size::from_bytes(1),
+ ty::Char => Size::from_bytes(4),
+ ty::Int(ity) => {
+ Integer::from_attr(&tcx, SignedInt(ity)).size()
+ }
+ ty::Uint(uty) => {
+ Integer::from_attr(&tcx, UnsignedInt(uty)).size()
+ }
+ _ => tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size,
+ };
// if `ty` does not depend on generic parameters, use an empty param_env
- let size = tcx.layout_of(param_env.with_reveal_all().and(ty)).ok()?.size;
self.eval(tcx, param_env).val.try_to_bits(size)
}
use syntax_pos::Pos;
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
use syntax_pos::BytePos;
use rustc::session::config::{self, DebugInfo};
use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet};
use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_codegen_ssa::debuginfo::{FunctionDebugContext, MirDebugScope, VariableAccess,
VariableKind, FunctionDebugContextData, type_names};
#[macro_use] extern crate rustc;
extern crate rustc_target;
#[macro_use] extern crate rustc_data_structures;
+extern crate rustc_index;
extern crate rustc_incremental;
extern crate rustc_codegen_utils;
extern crate rustc_codegen_ssa;
rustc_errors = { path = "../librustc_errors" }
rustc_fs_util = { path = "../librustc_fs_util" }
rustc_incremental = { path = "../librustc_incremental" }
+rustc_index = { path = "../librustc_index" }
rustc_target = { path = "../librustc_target" }
use rustc::ty::query::Providers;
use rustc::ty::subst::SubstsRef;
use rustc::util::nodemap::{FxHashMap, DefIdMap};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use syntax::ext::allocator::ALLOCATOR_METHODS;
pub type ExportedSymbols = FxHashMap<
use rustc::session::config::{self, EntryFnType, Lto};
use rustc::session::Session;
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc_codegen_utils::{symbol_names_test, check_for_rustc_errors_attr};
use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
use crate::mir::place::PlaceRef;
//! An analysis to determine which locals require allocas and
//! which do not.
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::traversal;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc::middle::lang_items;
use rustc::ty::{self, Ty, TypeFoldable, Instance};
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, FnTypeExt};
use rustc::mir::interpret::ErrorHandled;
use rustc::mir;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, HasTyCtxt};
use syntax::source_map::Span;
use std::iter;
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use self::analyze::CleanupKind;
use self::place::PlaceRef;
use rustc::hir::def_id::CrateNum;
use rustc::mir;
use rustc::ty::{self, Ty, Instance};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use syntax::ast::Name;
use syntax_pos::{SourceFile, Span};
rayon-core = { version = "0.2.0", package = "rustc-rayon-core" }
rustc-hash = "1.0.1"
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
+rustc_index = { path = "../librustc_index", package = "rustc_index" }
[dependencies.parking_lot]
version = "0.9"
+++ /dev/null
-use crate::indexed_vec::{Idx, IndexVec};
-use smallvec::SmallVec;
-use std::fmt;
-use std::iter;
-use std::marker::PhantomData;
-use std::mem;
-use std::slice;
-
-#[cfg(test)]
-mod tests;
-
-pub type Word = u64;
-pub const WORD_BYTES: usize = mem::size_of::<Word>();
-pub const WORD_BITS: usize = WORD_BYTES * 8;
-
-/// A fixed-size bitset type with a dense representation. It does not support
-/// resizing after creation; use `GrowableBitSet` for that.
-///
-/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
-/// just be `usize`.
-///
-/// All operations that involve an element will panic if the element is equal
-/// to or greater than the domain size. All operations that involve two bitsets
-/// will panic if the bitsets have differing domain sizes.
-#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)]
-pub struct BitSet<T: Idx> {
- domain_size: usize,
- words: Vec<Word>,
- marker: PhantomData<T>,
-}
-
-impl<T: Idx> BitSet<T> {
- /// Creates a new, empty bitset with a given `domain_size`.
- #[inline]
- pub fn new_empty(domain_size: usize) -> BitSet<T> {
- let num_words = num_words(domain_size);
- BitSet {
- domain_size,
- words: vec![0; num_words],
- marker: PhantomData,
- }
- }
-
- /// Creates a new, filled bitset with a given `domain_size`.
- #[inline]
- pub fn new_filled(domain_size: usize) -> BitSet<T> {
- let num_words = num_words(domain_size);
- let mut result = BitSet {
- domain_size,
- words: vec![!0; num_words],
- marker: PhantomData,
- };
- result.clear_excess_bits();
- result
- }
-
- /// Gets the domain size.
- pub fn domain_size(&self) -> usize {
- self.domain_size
- }
-
- /// Clear all elements.
- #[inline]
- pub fn clear(&mut self) {
- for word in &mut self.words {
- *word = 0;
- }
- }
-
- /// Clear excess bits in the final word.
- fn clear_excess_bits(&mut self) {
- let num_bits_in_final_word = self.domain_size % WORD_BITS;
- if num_bits_in_final_word > 0 {
- let mask = (1 << num_bits_in_final_word) - 1;
- let final_word_idx = self.words.len() - 1;
- self.words[final_word_idx] &= mask;
- }
- }
-
- /// Efficiently overwrite `self` with `other`.
- pub fn overwrite(&mut self, other: &BitSet<T>) {
- assert!(self.domain_size == other.domain_size);
- self.words.clone_from_slice(&other.words);
- }
-
- /// Count the number of set bits in the set.
- pub fn count(&self) -> usize {
- self.words.iter().map(|e| e.count_ones() as usize).sum()
- }
-
- /// Returns `true` if `self` contains `elem`.
- #[inline]
- pub fn contains(&self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- let (word_index, mask) = word_index_and_mask(elem);
- (self.words[word_index] & mask) != 0
- }
-
- /// Is `self` is a (non-strict) superset of `other`?
- #[inline]
- pub fn superset(&self, other: &BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b)
- }
-
- /// Is the set empty?
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.words.iter().all(|a| *a == 0)
- }
-
- /// Insert `elem`. Returns whether the set has changed.
- #[inline]
- pub fn insert(&mut self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- let (word_index, mask) = word_index_and_mask(elem);
- let word_ref = &mut self.words[word_index];
- let word = *word_ref;
- let new_word = word | mask;
- *word_ref = new_word;
- new_word != word
- }
-
- /// Sets all bits to true.
- pub fn insert_all(&mut self) {
- for word in &mut self.words {
- *word = !0;
- }
- self.clear_excess_bits();
- }
-
- /// Returns `true` if the set has changed.
- #[inline]
- pub fn remove(&mut self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- let (word_index, mask) = word_index_and_mask(elem);
- let word_ref = &mut self.words[word_index];
- let word = *word_ref;
- let new_word = word & !mask;
- *word_ref = new_word;
- new_word != word
- }
-
- /// Sets `self = self | other` and returns `true` if `self` changed
- /// (i.e., if new bits were added).
- pub fn union(&mut self, other: &impl UnionIntoBitSet<T>) -> bool {
- other.union_into(self)
- }
-
- /// Sets `self = self - other` and returns `true` if `self` changed.
- /// (i.e., if any bits were removed).
- pub fn subtract(&mut self, other: &impl SubtractFromBitSet<T>) -> bool {
- other.subtract_from(self)
- }
-
- /// Sets `self = self & other` and return `true` if `self` changed.
- /// (i.e., if any bits were removed).
- pub fn intersect(&mut self, other: &BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- bitwise(&mut self.words, &other.words, |a, b| { a & b })
- }
-
- /// Gets a slice of the underlying words.
- pub fn words(&self) -> &[Word] {
- &self.words
- }
-
- /// Iterates over the indices of set bits in a sorted order.
- #[inline]
- pub fn iter(&self) -> BitIter<'_, T> {
- BitIter {
- cur: None,
- iter: self.words.iter().enumerate(),
- marker: PhantomData,
- }
- }
-
- /// Duplicates the set as a hybrid set.
- pub fn to_hybrid(&self) -> HybridBitSet<T> {
- // Note: we currently don't bother trying to make a Sparse set.
- HybridBitSet::Dense(self.to_owned())
- }
-
- /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at
- /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`).
- ///
- /// This is an optimization for union of a hybrid bitset.
- fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool {
- assert!(sparse.domain_size == self.domain_size);
- self.clear_excess_bits();
-
- let mut not_already = false;
- // Index of the current word not yet merged.
- let mut current_index = 0;
- // Mask of bits that came from the sparse set in the current word.
- let mut new_bit_mask = 0;
- for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) {
- // Next bit is in a word not inspected yet.
- if word_index > current_index {
- self.words[current_index] |= new_bit_mask;
- // Were there any bits in the old word that did not occur in the sparse set?
- not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
- // Check all words we skipped for any set bit.
- not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0);
- // Update next word.
- current_index = word_index;
- // Reset bit mask, no bits have been merged yet.
- new_bit_mask = 0;
- }
- // Add bit and mark it as coming from the sparse set.
- // self.words[word_index] |= mask;
- new_bit_mask |= mask;
- }
- self.words[current_index] |= new_bit_mask;
- // Any bits in the last inspected word that were not in the sparse set?
- not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
- // Any bits in the tail? Note `clear_excess_bits` before.
- not_already |= self.words[current_index+1..].iter().any(|&x| x != 0);
-
- not_already
- }
-}
-
-/// This is implemented by all the bitsets so that BitSet::union() can be
-/// passed any type of bitset.
-pub trait UnionIntoBitSet<T: Idx> {
- // Performs `other = other | self`.
- fn union_into(&self, other: &mut BitSet<T>) -> bool;
-}
-
-/// This is implemented by all the bitsets so that BitSet::subtract() can be
-/// passed any type of bitset.
-pub trait SubtractFromBitSet<T: Idx> {
- // Performs `other = other - self`.
- fn subtract_from(&self, other: &mut BitSet<T>) -> bool;
-}
-
-impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> {
- fn union_into(&self, other: &mut BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- bitwise(&mut other.words, &self.words, |a, b| { a | b })
- }
-}
-
-impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
- fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- bitwise(&mut other.words, &self.words, |a, b| { a & !b })
- }
-}
-
-impl<T: Idx> fmt::Debug for BitSet<T> {
- fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
- w.debug_list()
- .entries(self.iter())
- .finish()
- }
-}
-
-impl<T: Idx> ToString for BitSet<T> {
- fn to_string(&self) -> String {
- let mut result = String::new();
- let mut sep = '[';
-
- // Note: this is a little endian printout of bytes.
-
- // i tracks how many bits we have printed so far.
- let mut i = 0;
- for word in &self.words {
- let mut word = *word;
- for _ in 0..WORD_BYTES { // for each byte in `word`:
- let remain = self.domain_size - i;
- // If less than a byte remains, then mask just that many bits.
- let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
- assert!(mask <= 0xFF);
- let byte = word & mask;
-
- result.push_str(&format!("{}{:02x}", sep, byte));
-
- if remain <= 8 { break; }
- word >>= 8;
- i += 8;
- sep = '-';
- }
- sep = '|';
- }
- result.push(']');
-
- result
- }
-}
-
-pub struct BitIter<'a, T: Idx> {
- cur: Option<(Word, usize)>,
- iter: iter::Enumerate<slice::Iter<'a, Word>>,
- marker: PhantomData<T>
-}
-
-impl<'a, T: Idx> Iterator for BitIter<'a, T> {
- type Item = T;
- fn next(&mut self) -> Option<T> {
- loop {
- if let Some((ref mut word, offset)) = self.cur {
- let bit_pos = word.trailing_zeros() as usize;
- if bit_pos != WORD_BITS {
- let bit = 1 << bit_pos;
- *word ^= bit;
- return Some(T::new(bit_pos + offset))
- }
- }
-
- let (i, word) = self.iter.next()?;
- self.cur = Some((*word, WORD_BITS * i));
- }
- }
-}
-
-#[inline]
-fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
- where Op: Fn(Word, Word) -> Word
-{
- assert_eq!(out_vec.len(), in_vec.len());
- let mut changed = false;
- for (out_elem, in_elem) in out_vec.iter_mut().zip(in_vec.iter()) {
- let old_val = *out_elem;
- let new_val = op(old_val, *in_elem);
- *out_elem = new_val;
- changed |= old_val != new_val;
- }
- changed
-}
-
-const SPARSE_MAX: usize = 8;
-
-/// A fixed-size bitset type with a sparse representation and a maximum of
-/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with
-/// no duplicates; although `SmallVec` can spill its elements to the heap, that
-/// never happens within this type because of the `SPARSE_MAX` limit.
-///
-/// This type is used by `HybridBitSet`; do not use directly.
-#[derive(Clone, Debug)]
-pub struct SparseBitSet<T: Idx> {
- domain_size: usize,
- elems: SmallVec<[T; SPARSE_MAX]>,
-}
-
-impl<T: Idx> SparseBitSet<T> {
- fn new_empty(domain_size: usize) -> Self {
- SparseBitSet {
- domain_size,
- elems: SmallVec::new()
- }
- }
-
- fn len(&self) -> usize {
- self.elems.len()
- }
-
- fn is_empty(&self) -> bool {
- self.elems.len() == 0
- }
-
- fn contains(&self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- self.elems.contains(&elem)
- }
-
- fn insert(&mut self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) {
- if self.elems[i] == elem {
- // `elem` is already in the set.
- false
- } else {
- // `elem` is smaller than one or more existing elements.
- self.elems.insert(i, elem);
- true
- }
- } else {
- // `elem` is larger than all existing elements.
- self.elems.push(elem);
- true
- };
- assert!(self.len() <= SPARSE_MAX);
- changed
- }
-
- fn remove(&mut self, elem: T) -> bool {
- assert!(elem.index() < self.domain_size);
- if let Some(i) = self.elems.iter().position(|&e| e == elem) {
- self.elems.remove(i);
- true
- } else {
- false
- }
- }
-
- fn to_dense(&self) -> BitSet<T> {
- let mut dense = BitSet::new_empty(self.domain_size);
- for elem in self.elems.iter() {
- dense.insert(*elem);
- }
- dense
- }
-
- fn iter(&self) -> slice::Iter<'_, T> {
- self.elems.iter()
- }
-}
-
-impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> {
- fn union_into(&self, other: &mut BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- let mut changed = false;
- for elem in self.iter() {
- changed |= other.insert(*elem);
- }
- changed
- }
-}
-
-impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
- fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
- assert_eq!(self.domain_size, other.domain_size);
- let mut changed = false;
- for elem in self.iter() {
- changed |= other.remove(*elem);
- }
- changed
- }
-}
-
-/// A fixed-size bitset type with a hybrid representation: sparse when there
-/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more
-/// than `SPARSE_MAX`.
-///
-/// This type is especially efficient for sets that typically have a small
-/// number of elements, but a large `domain_size`, and are cleared frequently.
-///
-/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
-/// just be `usize`.
-///
-/// All operations that involve an element will panic if the element is equal
-/// to or greater than the domain size. All operations that involve two bitsets
-/// will panic if the bitsets have differing domain sizes.
-#[derive(Clone, Debug)]
-pub enum HybridBitSet<T: Idx> {
- Sparse(SparseBitSet<T>),
- Dense(BitSet<T>),
-}
-
-impl<T: Idx> HybridBitSet<T> {
- pub fn new_empty(domain_size: usize) -> Self {
- HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
- }
-
- fn domain_size(&self) -> usize {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.domain_size,
- HybridBitSet::Dense(dense) => dense.domain_size,
- }
- }
-
- pub fn clear(&mut self) {
- let domain_size = self.domain_size();
- *self = HybridBitSet::new_empty(domain_size);
- }
-
- pub fn contains(&self, elem: T) -> bool {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.contains(elem),
- HybridBitSet::Dense(dense) => dense.contains(elem),
- }
- }
-
- pub fn superset(&self, other: &HybridBitSet<T>) -> bool {
- match (self, other) {
- (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => {
- self_dense.superset(other_dense)
- }
- _ => {
- assert!(self.domain_size() == other.domain_size());
- other.iter().all(|elem| self.contains(elem))
- }
- }
- }
-
- pub fn is_empty(&self) -> bool {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.is_empty(),
- HybridBitSet::Dense(dense) => dense.is_empty(),
- }
- }
-
- pub fn insert(&mut self, elem: T) -> bool {
- // No need to check `elem` against `self.domain_size` here because all
- // the match cases check it, one way or another.
- match self {
- HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => {
- // The set is sparse and has space for `elem`.
- sparse.insert(elem)
- }
- HybridBitSet::Sparse(sparse) if sparse.contains(elem) => {
- // The set is sparse and does not have space for `elem`, but
- // that doesn't matter because `elem` is already present.
- false
- }
- HybridBitSet::Sparse(sparse) => {
- // The set is sparse and full. Convert to a dense set.
- let mut dense = sparse.to_dense();
- let changed = dense.insert(elem);
- assert!(changed);
- *self = HybridBitSet::Dense(dense);
- changed
- }
- HybridBitSet::Dense(dense) => dense.insert(elem),
- }
- }
-
- pub fn insert_all(&mut self) {
- let domain_size = self.domain_size();
- match self {
- HybridBitSet::Sparse(_) => {
- *self = HybridBitSet::Dense(BitSet::new_filled(domain_size));
- }
- HybridBitSet::Dense(dense) => dense.insert_all(),
- }
- }
-
- pub fn remove(&mut self, elem: T) -> bool {
- // Note: we currently don't bother going from Dense back to Sparse.
- match self {
- HybridBitSet::Sparse(sparse) => sparse.remove(elem),
- HybridBitSet::Dense(dense) => dense.remove(elem),
- }
- }
-
- pub fn union(&mut self, other: &HybridBitSet<T>) -> bool {
- match self {
- HybridBitSet::Sparse(self_sparse) => {
- match other {
- HybridBitSet::Sparse(other_sparse) => {
- // Both sets are sparse. Add the elements in
- // `other_sparse` to `self` one at a time. This
- // may or may not cause `self` to be densified.
- assert_eq!(self.domain_size(), other.domain_size());
- let mut changed = false;
- for elem in other_sparse.iter() {
- changed |= self.insert(*elem);
- }
- changed
- }
- HybridBitSet::Dense(other_dense) => {
- // `self` is sparse and `other` is dense. To
- // merge them, we have two available strategies:
- // * Densify `self` then merge other
- // * Clone other then integrate bits from `self`
- // The second strategy requires dedicated method
- // since the usual `union` returns the wrong
- // result. In the dedicated case the computation
- // is slightly faster if the bits of the sparse
- // bitset map to only few words of the dense
- // representation, i.e. indices are near each
- // other.
- //
- // Benchmarking seems to suggest that the second
- // option is worth it.
- let mut new_dense = other_dense.clone();
- let changed = new_dense.reverse_union_sparse(self_sparse);
- *self = HybridBitSet::Dense(new_dense);
- changed
- }
- }
- }
-
- HybridBitSet::Dense(self_dense) => self_dense.union(other),
- }
- }
-
- /// Converts to a dense set, consuming itself in the process.
- pub fn to_dense(self) -> BitSet<T> {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.to_dense(),
- HybridBitSet::Dense(dense) => dense,
- }
- }
-
- pub fn iter(&self) -> HybridIter<'_, T> {
- match self {
- HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()),
- HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()),
- }
- }
-}
-
-impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> {
- fn union_into(&self, other: &mut BitSet<T>) -> bool {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.union_into(other),
- HybridBitSet::Dense(dense) => dense.union_into(other),
- }
- }
-}
-
-impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> {
- fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
- match self {
- HybridBitSet::Sparse(sparse) => sparse.subtract_from(other),
- HybridBitSet::Dense(dense) => dense.subtract_from(other),
- }
- }
-}
-
-pub enum HybridIter<'a, T: Idx> {
- Sparse(slice::Iter<'a, T>),
- Dense(BitIter<'a, T>),
-}
-
-impl<'a, T: Idx> Iterator for HybridIter<'a, T> {
- type Item = T;
-
- fn next(&mut self) -> Option<T> {
- match self {
- HybridIter::Sparse(sparse) => sparse.next().map(|e| *e),
- HybridIter::Dense(dense) => dense.next(),
- }
- }
-}
-
-/// A resizable bitset type with a dense representation.
-///
-/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
-/// just be `usize`.
-///
-/// All operations that involve an element will panic if the element is equal
-/// to or greater than the domain size.
-#[derive(Clone, Debug, PartialEq)]
-pub struct GrowableBitSet<T: Idx> {
- bit_set: BitSet<T>,
-}
-
-impl<T: Idx> GrowableBitSet<T> {
- /// Ensure that the set can hold at least `min_domain_size` elements.
- pub fn ensure(&mut self, min_domain_size: usize) {
- if self.bit_set.domain_size < min_domain_size {
- self.bit_set.domain_size = min_domain_size;
- }
-
- let min_num_words = num_words(min_domain_size);
- if self.bit_set.words.len() < min_num_words {
- self.bit_set.words.resize(min_num_words, 0)
- }
- }
-
- pub fn new_empty() -> GrowableBitSet<T> {
- GrowableBitSet { bit_set: BitSet::new_empty(0) }
- }
-
- pub fn with_capacity(capacity: usize) -> GrowableBitSet<T> {
- GrowableBitSet { bit_set: BitSet::new_empty(capacity) }
- }
-
- /// Returns `true` if the set has changed.
- #[inline]
- pub fn insert(&mut self, elem: T) -> bool {
- self.ensure(elem.index() + 1);
- self.bit_set.insert(elem)
- }
-
- #[inline]
- pub fn contains(&self, elem: T) -> bool {
- let (word_index, mask) = word_index_and_mask(elem);
- if let Some(word) = self.bit_set.words.get(word_index) {
- (word & mask) != 0
- } else {
- false
- }
- }
-}
-
-/// A fixed-size 2D bit matrix type with a dense representation.
-///
-/// `R` and `C` are index types used to identify rows and columns respectively;
-/// typically newtyped `usize` wrappers, but they can also just be `usize`.
-///
-/// All operations that involve a row and/or column index will panic if the
-/// index exceeds the relevant bound.
-#[derive(Clone, Debug, Eq, PartialEq, RustcDecodable, RustcEncodable)]
-pub struct BitMatrix<R: Idx, C: Idx> {
- num_rows: usize,
- num_columns: usize,
- words: Vec<Word>,
- marker: PhantomData<(R, C)>,
-}
-
-impl<R: Idx, C: Idx> BitMatrix<R, C> {
- /// Creates a new `rows x columns` matrix, initially empty.
- pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix<R, C> {
- // For every element, we need one bit for every other
- // element. Round up to an even number of words.
- let words_per_row = num_words(num_columns);
- BitMatrix {
- num_rows,
- num_columns,
- words: vec![0; num_rows * words_per_row],
- marker: PhantomData,
- }
- }
-
- /// Creates a new matrix, with `row` used as the value for every row.
- pub fn from_row_n(row: &BitSet<C>, num_rows: usize) -> BitMatrix<R, C> {
- let num_columns = row.domain_size();
- let words_per_row = num_words(num_columns);
- assert_eq!(words_per_row, row.words().len());
- BitMatrix {
- num_rows,
- num_columns,
- words: iter::repeat(row.words()).take(num_rows).flatten().cloned().collect(),
- marker: PhantomData,
- }
- }
-
- pub fn rows(&self) -> impl Iterator<Item = R> {
- (0..self.num_rows).map(R::new)
- }
-
- /// The range of bits for a given row.
- fn range(&self, row: R) -> (usize, usize) {
- let words_per_row = num_words(self.num_columns);
- let start = row.index() * words_per_row;
- (start, start + words_per_row)
- }
-
- /// Sets the cell at `(row, column)` to true. Put another way, insert
- /// `column` to the bitset for `row`.
- ///
- /// Returns `true` if this changed the matrix.
- pub fn insert(&mut self, row: R, column: C) -> bool {
- assert!(row.index() < self.num_rows && column.index() < self.num_columns);
- let (start, _) = self.range(row);
- let (word_index, mask) = word_index_and_mask(column);
- let words = &mut self.words[..];
- let word = words[start + word_index];
- let new_word = word | mask;
- words[start + word_index] = new_word;
- word != new_word
- }
-
- /// Do the bits from `row` contain `column`? Put another way, is
- /// the matrix cell at `(row, column)` true? Put yet another way,
- /// if the matrix represents (transitive) reachability, can
- /// `row` reach `column`?
- pub fn contains(&self, row: R, column: C) -> bool {
- assert!(row.index() < self.num_rows && column.index() < self.num_columns);
- let (start, _) = self.range(row);
- let (word_index, mask) = word_index_and_mask(column);
- (self.words[start + word_index] & mask) != 0
- }
-
- /// Returns those indices that are true in rows `a` and `b`. This
- /// is an O(n) operation where `n` is the number of elements
- /// (somewhat independent from the actual size of the
- /// intersection, in particular).
- pub fn intersect_rows(&self, row1: R, row2: R) -> Vec<C> {
- assert!(row1.index() < self.num_rows && row2.index() < self.num_rows);
- let (row1_start, row1_end) = self.range(row1);
- let (row2_start, row2_end) = self.range(row2);
- let mut result = Vec::with_capacity(self.num_columns);
- for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() {
- let mut v = self.words[i] & self.words[j];
- for bit in 0..WORD_BITS {
- if v == 0 {
- break;
- }
- if v & 0x1 != 0 {
- result.push(C::new(base * WORD_BITS + bit));
- }
- v >>= 1;
- }
- }
- result
- }
-
- /// Adds the bits from row `read` to the bits from row `write`, and
- /// returns `true` if anything changed.
- ///
- /// This is used when computing transitive reachability because if
- /// you have an edge `write -> read`, because in that case
- /// `write` can reach everything that `read` can (and
- /// potentially more).
- pub fn union_rows(&mut self, read: R, write: R) -> bool {
- assert!(read.index() < self.num_rows && write.index() < self.num_rows);
- let (read_start, read_end) = self.range(read);
- let (write_start, write_end) = self.range(write);
- let words = &mut self.words[..];
- let mut changed = false;
- for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) {
- let word = words[write_index];
- let new_word = word | words[read_index];
- words[write_index] = new_word;
- changed |= word != new_word;
- }
- changed
- }
-
- /// Adds the bits from `with` to the bits from row `write`, and
- /// returns `true` if anything changed.
- pub fn union_row_with(&mut self, with: &BitSet<C>, write: R) -> bool {
- assert!(write.index() < self.num_rows);
- assert_eq!(with.domain_size(), self.num_columns);
- let (write_start, write_end) = self.range(write);
- let mut changed = false;
- for (read_index, write_index) in (0..with.words().len()).zip(write_start..write_end) {
- let word = self.words[write_index];
- let new_word = word | with.words()[read_index];
- self.words[write_index] = new_word;
- changed |= word != new_word;
- }
- changed
- }
-
- /// Sets every cell in `row` to true.
- pub fn insert_all_into_row(&mut self, row: R) {
- assert!(row.index() < self.num_rows);
- let (start, end) = self.range(row);
- let words = &mut self.words[..];
- for index in start..end {
- words[index] = !0;
- }
- self.clear_excess_bits(row);
- }
-
- /// Clear excess bits in the final word of the row.
- fn clear_excess_bits(&mut self, row: R) {
- let num_bits_in_final_word = self.num_columns % WORD_BITS;
- if num_bits_in_final_word > 0 {
- let mask = (1 << num_bits_in_final_word) - 1;
- let (_, end) = self.range(row);
- let final_word_idx = end - 1;
- self.words[final_word_idx] &= mask;
- }
- }
-
- /// Gets a slice of the underlying words.
- pub fn words(&self) -> &[Word] {
- &self.words
- }
-
- /// Iterates through all the columns set to true in a given row of
- /// the matrix.
- pub fn iter(&self, row: R) -> BitIter<'_, C> {
- assert!(row.index() < self.num_rows);
- let (start, end) = self.range(row);
- BitIter {
- cur: None,
- iter: self.words[start..end].iter().enumerate(),
- marker: PhantomData,
- }
- }
-
- /// Returns the number of elements in `row`.
- pub fn count(&self, row: R) -> usize {
- let (start, end) = self.range(row);
- self.words[start..end].iter().map(|e| e.count_ones() as usize).sum()
- }
-}
-
-/// A fixed-column-size, variable-row-size 2D bit matrix with a moderately
-/// sparse representation.
-///
-/// Initially, every row has no explicit representation. If any bit within a
-/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`.
-/// Furthermore, any previously uninstantiated rows prior to it will be
-/// instantiated as `None`. Those prior rows may themselves become fully
-/// instantiated later on if any of their bits are set.
-///
-/// `R` and `C` are index types used to identify rows and columns respectively;
-/// typically newtyped `usize` wrappers, but they can also just be `usize`.
-#[derive(Clone, Debug)]
-pub struct SparseBitMatrix<R, C>
-where
- R: Idx,
- C: Idx,
-{
- num_columns: usize,
- rows: IndexVec<R, Option<HybridBitSet<C>>>,
-}
-
-impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
- /// Creates a new empty sparse bit matrix with no rows or columns.
- pub fn new(num_columns: usize) -> Self {
- Self {
- num_columns,
- rows: IndexVec::new(),
- }
- }
-
- fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> {
- // Instantiate any missing rows up to and including row `row` with an
- // empty HybridBitSet.
- self.rows.ensure_contains_elem(row, || None);
-
- // Then replace row `row` with a full HybridBitSet if necessary.
- let num_columns = self.num_columns;
- self.rows[row].get_or_insert_with(|| HybridBitSet::new_empty(num_columns))
- }
-
- /// Sets the cell at `(row, column)` to true. Put another way, insert
- /// `column` to the bitset for `row`.
- ///
- /// Returns `true` if this changed the matrix.
- pub fn insert(&mut self, row: R, column: C) -> bool {
- self.ensure_row(row).insert(column)
- }
-
- /// Do the bits from `row` contain `column`? Put another way, is
- /// the matrix cell at `(row, column)` true? Put yet another way,
- /// if the matrix represents (transitive) reachability, can
- /// `row` reach `column`?
- pub fn contains(&self, row: R, column: C) -> bool {
- self.row(row).map_or(false, |r| r.contains(column))
- }
-
- /// Adds the bits from row `read` to the bits from row `write`, and
- /// returns `true` if anything changed.
- ///
- /// This is used when computing transitive reachability because if
- /// you have an edge `write -> read`, because in that case
- /// `write` can reach everything that `read` can (and
- /// potentially more).
- pub fn union_rows(&mut self, read: R, write: R) -> bool {
- if read == write || self.row(read).is_none() {
- return false;
- }
-
- self.ensure_row(write);
- if let (Some(read_row), Some(write_row)) = self.rows.pick2_mut(read, write) {
- write_row.union(read_row)
- } else {
- unreachable!()
- }
- }
-
- /// Union a row, `from`, into the `into` row.
- pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool {
- self.ensure_row(into).union(from)
- }
-
- /// Insert all bits in the given row.
- pub fn insert_all_into_row(&mut self, row: R) {
- self.ensure_row(row).insert_all();
- }
-
- pub fn rows(&self) -> impl Iterator<Item = R> {
- self.rows.indices()
- }
-
- /// Iterates through all the columns set to true in a given row of
- /// the matrix.
- pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
- self.row(row).into_iter().flat_map(|r| r.iter())
- }
-
- pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
- if let Some(Some(row)) = self.rows.get(row) {
- Some(row)
- } else {
- None
- }
- }
-}
-
-#[inline]
-fn num_words<T: Idx>(domain_size: T) -> usize {
- (domain_size.index() + WORD_BITS - 1) / WORD_BITS
-}
-
-#[inline]
-fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) {
- let elem = elem.index();
- let word_index = elem / WORD_BITS;
- let mask = 1 << (elem % WORD_BITS);
- (word_index, mask)
-}
+++ /dev/null
-use super::*;
-
-extern crate test;
-use test::Bencher;
-
-#[test]
-fn test_new_filled() {
- for i in 0..128 {
- let idx_buf = BitSet::new_filled(i);
- let elems: Vec<usize> = idx_buf.iter().collect();
- let expected: Vec<usize> = (0..i).collect();
- assert_eq!(elems, expected);
- }
-}
-
-#[test]
-fn bitset_iter_works() {
- let mut bitset: BitSet<usize> = BitSet::new_empty(100);
- bitset.insert(1);
- bitset.insert(10);
- bitset.insert(19);
- bitset.insert(62);
- bitset.insert(63);
- bitset.insert(64);
- bitset.insert(65);
- bitset.insert(66);
- bitset.insert(99);
- assert_eq!(
- bitset.iter().collect::<Vec<_>>(),
- [1, 10, 19, 62, 63, 64, 65, 66, 99]
- );
-}
-
-#[test]
-fn bitset_iter_works_2() {
- let mut bitset: BitSet<usize> = BitSet::new_empty(320);
- bitset.insert(0);
- bitset.insert(127);
- bitset.insert(191);
- bitset.insert(255);
- bitset.insert(319);
- assert_eq!(bitset.iter().collect::<Vec<_>>(), [0, 127, 191, 255, 319]);
-}
-
-#[test]
-fn union_two_sets() {
- let mut set1: BitSet<usize> = BitSet::new_empty(65);
- let mut set2: BitSet<usize> = BitSet::new_empty(65);
- assert!(set1.insert(3));
- assert!(!set1.insert(3));
- assert!(set2.insert(5));
- assert!(set2.insert(64));
- assert!(set1.union(&set2));
- assert!(!set1.union(&set2));
- assert!(set1.contains(3));
- assert!(!set1.contains(4));
- assert!(set1.contains(5));
- assert!(!set1.contains(63));
- assert!(set1.contains(64));
-}
-
-#[test]
-fn hybrid_bitset() {
- let mut sparse038: HybridBitSet<usize> = HybridBitSet::new_empty(256);
- assert!(sparse038.is_empty());
- assert!(sparse038.insert(0));
- assert!(sparse038.insert(1));
- assert!(sparse038.insert(8));
- assert!(sparse038.insert(3));
- assert!(!sparse038.insert(3));
- assert!(sparse038.remove(1));
- assert!(!sparse038.is_empty());
- assert_eq!(sparse038.iter().collect::<Vec<_>>(), [0, 3, 8]);
-
- for i in 0..256 {
- if i == 0 || i == 3 || i == 8 {
- assert!(sparse038.contains(i));
- } else {
- assert!(!sparse038.contains(i));
- }
- }
-
- let mut sparse01358 = sparse038.clone();
- assert!(sparse01358.insert(1));
- assert!(sparse01358.insert(5));
- assert_eq!(sparse01358.iter().collect::<Vec<_>>(), [0, 1, 3, 5, 8]);
-
- let mut dense10 = HybridBitSet::new_empty(256);
- for i in 0..10 {
- assert!(dense10.insert(i));
- }
- assert!(!dense10.is_empty());
- assert_eq!(dense10.iter().collect::<Vec<_>>(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
-
- let mut dense256 = HybridBitSet::new_empty(256);
- assert!(dense256.is_empty());
- dense256.insert_all();
- assert!(!dense256.is_empty());
- for i in 0..256 {
- assert!(dense256.contains(i));
- }
-
- assert!(sparse038.superset(&sparse038)); // sparse + sparse (self)
- assert!(sparse01358.superset(&sparse038)); // sparse + sparse
- assert!(dense10.superset(&sparse038)); // dense + sparse
- assert!(dense10.superset(&dense10)); // dense + dense (self)
- assert!(dense256.superset(&dense10)); // dense + dense
-
- let mut hybrid = sparse038;
- assert!(!sparse01358.union(&hybrid)); // no change
- assert!(hybrid.union(&sparse01358));
- assert!(hybrid.superset(&sparse01358) && sparse01358.superset(&hybrid));
- assert!(!dense10.union(&sparse01358));
- assert!(!dense256.union(&dense10));
- let mut dense = dense10;
- assert!(dense.union(&dense256));
- assert!(dense.superset(&dense256) && dense256.superset(&dense));
- assert!(hybrid.union(&dense256));
- assert!(hybrid.superset(&dense256) && dense256.superset(&hybrid));
-
- assert_eq!(dense256.iter().count(), 256);
- let mut dense0 = dense256;
- for i in 0..256 {
- assert!(dense0.remove(i));
- }
- assert!(!dense0.remove(0));
- assert!(dense0.is_empty());
-}
-
-#[test]
-fn grow() {
- let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
- for index in 0..65 {
- assert!(set.insert(index));
- assert!(!set.insert(index));
- }
- set.ensure(128);
-
- // Check if the bits set before growing are still set
- for index in 0..65 {
- assert!(set.contains(index));
- }
-
- // Check if the new bits are all un-set
- for index in 65..128 {
- assert!(!set.contains(index));
- }
-
- // Check that we can set all new bits without running out of bounds
- for index in 65..128 {
- assert!(set.insert(index));
- assert!(!set.insert(index));
- }
-}
-
-#[test]
-fn matrix_intersection() {
- let mut matrix: BitMatrix<usize, usize> = BitMatrix::new(200, 200);
-
- // (*) Elements reachable from both 2 and 65.
-
- matrix.insert(2, 3);
- matrix.insert(2, 6);
- matrix.insert(2, 10); // (*)
- matrix.insert(2, 64); // (*)
- matrix.insert(2, 65);
- matrix.insert(2, 130);
- matrix.insert(2, 160); // (*)
-
- matrix.insert(64, 133);
-
- matrix.insert(65, 2);
- matrix.insert(65, 8);
- matrix.insert(65, 10); // (*)
- matrix.insert(65, 64); // (*)
- matrix.insert(65, 68);
- matrix.insert(65, 133);
- matrix.insert(65, 160); // (*)
-
- let intersection = matrix.intersect_rows(2, 64);
- assert!(intersection.is_empty());
-
- let intersection = matrix.intersect_rows(2, 65);
- assert_eq!(intersection, &[10, 64, 160]);
-}
-
-#[test]
-fn matrix_iter() {
- let mut matrix: BitMatrix<usize, usize> = BitMatrix::new(64, 100);
- matrix.insert(3, 22);
- matrix.insert(3, 75);
- matrix.insert(2, 99);
- matrix.insert(4, 0);
- matrix.union_rows(3, 5);
- matrix.insert_all_into_row(6);
-
- let expected = [99];
- let mut iter = expected.iter();
- for i in matrix.iter(2) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [22, 75];
- let mut iter = expected.iter();
- assert_eq!(matrix.count(3), expected.len());
- for i in matrix.iter(3) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [0];
- let mut iter = expected.iter();
- assert_eq!(matrix.count(4), expected.len());
- for i in matrix.iter(4) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [22, 75];
- let mut iter = expected.iter();
- assert_eq!(matrix.count(5), expected.len());
- for i in matrix.iter(5) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- assert_eq!(matrix.count(6), 100);
- let mut count = 0;
- for (idx, i) in matrix.iter(6).enumerate() {
- assert_eq!(idx, i);
- count += 1;
- }
- assert_eq!(count, 100);
-
- if let Some(i) = matrix.iter(7).next() {
- panic!("expected no elements in row, but contains element {:?}", i);
- }
-}
-
-#[test]
-fn sparse_matrix_iter() {
- let mut matrix: SparseBitMatrix<usize, usize> = SparseBitMatrix::new(100);
- matrix.insert(3, 22);
- matrix.insert(3, 75);
- matrix.insert(2, 99);
- matrix.insert(4, 0);
- matrix.union_rows(3, 5);
-
- let expected = [99];
- let mut iter = expected.iter();
- for i in matrix.iter(2) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [22, 75];
- let mut iter = expected.iter();
- for i in matrix.iter(3) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [0];
- let mut iter = expected.iter();
- for i in matrix.iter(4) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-
- let expected = [22, 75];
- let mut iter = expected.iter();
- for i in matrix.iter(5) {
- let j = *iter.next().unwrap();
- assert_eq!(i, j);
- }
- assert!(iter.next().is_none());
-}
-
-/// Merge dense hybrid set into empty sparse hybrid set.
-#[bench]
-fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) {
- let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
- for i in 0..10 {
- assert!(pre_dense.insert(i));
- }
- let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
- b.iter(|| {
- let dense = pre_dense.clone();
- let mut sparse = pre_sparse.clone();
- sparse.union(&dense);
- })
-}
-
-/// Merge dense hybrid set into full hybrid set with same indices.
-#[bench]
-fn union_hybrid_sparse_full_to_dense(b: &mut Bencher) {
- let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
- for i in 0..10 {
- assert!(pre_dense.insert(i));
- }
- let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
- for i in 0..SPARSE_MAX {
- assert!(pre_sparse.insert(i));
- }
- b.iter(|| {
- let dense = pre_dense.clone();
- let mut sparse = pre_sparse.clone();
- sparse.union(&dense);
- })
-}
-
-/// Merge dense hybrid set into full hybrid set with indices over the whole domain.
-#[bench]
-fn union_hybrid_sparse_domain_to_dense(b: &mut Bencher) {
- let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
- for i in 0..10 {
- assert!(pre_dense.insert(i));
- }
- let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
- for i in 0..SPARSE_MAX {
- assert!(pre_sparse.insert(i*64));
- }
- b.iter(|| {
- let dense = pre_dense.clone();
- let mut sparse = pre_sparse.clone();
- sparse.union(&dense);
- })
-}
-
-/// Merge dense hybrid set into empty hybrid set where the domain is very small.
-#[bench]
-fn union_hybrid_sparse_empty_small_domain(b: &mut Bencher) {
- let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
- for i in 0..SPARSE_MAX {
- assert!(pre_dense.insert(i));
- }
- let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
- b.iter(|| {
- let dense = pre_dense.clone();
- let mut sparse = pre_sparse.clone();
- sparse.union(&dense);
- })
-}
-
-/// Merge dense hybrid set into full hybrid set where the domain is very small.
-#[bench]
-fn union_hybrid_sparse_full_small_domain(b: &mut Bencher) {
- let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
- for i in 0..SPARSE_MAX {
- assert!(pre_dense.insert(i));
- }
- let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
- for i in 0..SPARSE_MAX {
- assert!(pre_sparse.insert(i));
- }
- b.iter(|| {
- let dense = pre_dense.clone();
- let mut sparse = pre_sparse.clone();
- sparse.union(&dense);
- })
-}
//! Rice Computer Science TS-06-33870
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>
-use super::super::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;
//! the field `next_edge`). Each of those fields is an array that should
//! be indexed by the direction (see the type `Direction`).
-use crate::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use crate::snapshot_vec::{SnapshotVec, SnapshotVecDelegate};
use std::fmt::Debug;
use std::usize;
-use super::super::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use super::{DirectedGraph, WithNumNodes, WithSuccessors, WithStartNode};
-use crate::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
#[cfg(test)]
mod tests;
-use super::indexed_vec::Idx;
+use rustc_index::vec::Idx;
pub mod dominators;
pub mod implementation;
use crate::fx::FxHashSet;
use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors};
use crate::graph::vec_graph::VecGraph;
-use crate::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use std::ops::Range;
#[cfg(test)]
-use crate::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors};
#[cfg(test)]
+++ /dev/null
-use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
-
-use std::fmt::Debug;
-use std::iter::{self, FromIterator};
-use std::slice;
-use std::marker::PhantomData;
-use std::ops::{Index, IndexMut, Range, RangeBounds};
-use std::fmt;
-use std::hash::Hash;
-use std::vec;
-use std::u32;
-
-/// Represents some newtyped `usize` wrapper.
-///
-/// Purpose: avoid mixing indexes for different bitvector domains.
-pub trait Idx: Copy + 'static + Ord + Debug + Hash {
- fn new(idx: usize) -> Self;
-
- fn index(self) -> usize;
-
- fn increment_by(&mut self, amount: usize) {
- *self = self.plus(amount);
- }
-
- fn plus(self, amount: usize) -> Self {
- Self::new(self.index() + amount)
- }
-}
-
-impl Idx for usize {
- #[inline]
- fn new(idx: usize) -> Self { idx }
- #[inline]
- fn index(self) -> usize { self }
-}
-
-impl Idx for u32 {
- #[inline]
- fn new(idx: usize) -> Self { assert!(idx <= u32::MAX as usize); idx as u32 }
- #[inline]
- fn index(self) -> usize { self as usize }
-}
-
-/// Creates a struct type `S` that can be used as an index with
-/// `IndexVec` and so on.
-///
-/// There are two ways of interacting with these indices:
-///
-/// - The `From` impls are the preferred way. So you can do
-/// `S::from(v)` with a `usize` or `u32`. And you can convert back
-/// to an integer with `u32::from(s)`.
-///
-/// - Alternatively, you can use the methods `S::new(v)` and `s.index()`
-/// to create/return a value.
-///
-/// Internally, the index uses a u32, so the index must not exceed
-/// `u32::MAX`. You can also customize things like the `Debug` impl,
-/// what traits are derived, and so forth via the macro.
-#[macro_export]
-#[allow_internal_unstable(step_trait, rustc_attrs)]
-macro_rules! newtype_index {
- // ---- public rules ----
-
- // Use default constants
- ($(#[$attrs:meta])* $v:vis struct $name:ident { .. }) => (
- $crate::newtype_index!(
- // Leave out derives marker so we can use its absence to ensure it comes first
- @attrs [$(#[$attrs])*]
- @type [$name]
- // shave off 256 indices at the end to allow space for packing these indices into enums
- @max [0xFFFF_FF00]
- @vis [$v]
- @debug_format ["{}"]);
- );
-
- // Define any constants
- ($(#[$attrs:meta])* $v:vis struct $name:ident { $($tokens:tt)+ }) => (
- $crate::newtype_index!(
- // Leave out derives marker so we can use its absence to ensure it comes first
- @attrs [$(#[$attrs])*]
- @type [$name]
- // shave off 256 indices at the end to allow space for packing these indices into enums
- @max [0xFFFF_FF00]
- @vis [$v]
- @debug_format ["{}"]
- $($tokens)+);
- );
-
- // ---- private rules ----
-
- // Base case, user-defined constants (if any) have already been defined
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]) => (
- $(#[$attrs])*
- #[derive(Copy, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)]
- #[rustc_layout_scalar_valid_range_end($max)]
- $v struct $type {
- private: u32
- }
-
- impl Clone for $type {
- fn clone(&self) -> Self {
- *self
- }
- }
-
- impl $type {
- $v const MAX_AS_U32: u32 = $max;
-
- $v const MAX: $type = $type::from_u32_const($max);
-
- #[inline]
- $v fn from_usize(value: usize) -> Self {
- assert!(value <= ($max as usize));
- unsafe {
- $type::from_u32_unchecked(value as u32)
- }
- }
-
- #[inline]
- $v fn from_u32(value: u32) -> Self {
- assert!(value <= $max);
- unsafe {
- $type::from_u32_unchecked(value)
- }
- }
-
- /// Hacky variant of `from_u32` for use in constants.
- /// This version checks the "max" constraint by using an
- /// invalid array dereference.
- #[inline]
- $v const fn from_u32_const(value: u32) -> Self {
- // This will fail at const eval time unless `value <=
- // max` is true (in which case we get the index 0).
- // It will also fail at runtime, of course, but in a
- // kind of wacky way.
- let _ = ["out of range value used"][
- !(value <= $max) as usize
- ];
-
- unsafe {
- $type { private: value }
- }
- }
-
- #[inline]
- $v const unsafe fn from_u32_unchecked(value: u32) -> Self {
- $type { private: value }
- }
-
- /// Extracts the value of this index as an integer.
- #[inline]
- $v fn index(self) -> usize {
- self.as_usize()
- }
-
- /// Extracts the value of this index as a `u32`.
- #[inline]
- $v fn as_u32(self) -> u32 {
- self.private
- }
-
- /// Extracts the value of this index as a `usize`.
- #[inline]
- $v fn as_usize(self) -> usize {
- self.as_u32() as usize
- }
- }
-
- impl std::ops::Add<usize> for $type {
- type Output = Self;
-
- fn add(self, other: usize) -> Self {
- Self::new(self.index() + other)
- }
- }
-
- impl Idx for $type {
- #[inline]
- fn new(value: usize) -> Self {
- Self::from(value)
- }
-
- #[inline]
- fn index(self) -> usize {
- usize::from(self)
- }
- }
-
- impl ::std::iter::Step for $type {
- #[inline]
- fn steps_between(start: &Self, end: &Self) -> Option<usize> {
- <usize as ::std::iter::Step>::steps_between(
- &Idx::index(*start),
- &Idx::index(*end),
- )
- }
-
- #[inline]
- fn replace_one(&mut self) -> Self {
- ::std::mem::replace(self, Self::new(1))
- }
-
- #[inline]
- fn replace_zero(&mut self) -> Self {
- ::std::mem::replace(self, Self::new(0))
- }
-
- #[inline]
- fn add_one(&self) -> Self {
- Self::new(Idx::index(*self) + 1)
- }
-
- #[inline]
- fn sub_one(&self) -> Self {
- Self::new(Idx::index(*self) - 1)
- }
-
- #[inline]
- fn add_usize(&self, u: usize) -> Option<Self> {
- Idx::index(*self).checked_add(u).map(Self::new)
- }
-
- #[inline]
- fn sub_usize(&self, u: usize) -> Option<Self> {
- Idx::index(*self).checked_sub(u).map(Self::new)
- }
- }
-
- impl From<$type> for u32 {
- #[inline]
- fn from(v: $type) -> u32 {
- v.as_u32()
- }
- }
-
- impl From<$type> for usize {
- #[inline]
- fn from(v: $type) -> usize {
- v.as_usize()
- }
- }
-
- impl From<usize> for $type {
- #[inline]
- fn from(value: usize) -> Self {
- $type::from_usize(value)
- }
- }
-
- impl From<u32> for $type {
- #[inline]
- fn from(value: u32) -> Self {
- $type::from_u32(value)
- }
- }
-
- $crate::newtype_index!(
- @handle_debug
- @derives [$($derives,)*]
- @type [$type]
- @debug_format [$debug_format]);
- );
-
- // base case for handle_debug where format is custom. No Debug implementation is emitted.
- (@handle_debug
- @derives [$($_derives:ident,)*]
- @type [$type:ident]
- @debug_format [custom]) => ();
-
- // base case for handle_debug, no debug overrides found, so use default
- (@handle_debug
- @derives []
- @type [$type:ident]
- @debug_format [$debug_format:tt]) => (
- impl ::std::fmt::Debug for $type {
- fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- write!(fmt, $debug_format, self.as_u32())
- }
- }
- );
-
- // Debug is requested for derive, don't generate any Debug implementation.
- (@handle_debug
- @derives [Debug, $($derives:ident,)*]
- @type [$type:ident]
- @debug_format [$debug_format:tt]) => ();
-
- // It's not Debug, so just pop it off the front of the derives stack and check the rest.
- (@handle_debug
- @derives [$_derive:ident, $($derives:ident,)*]
- @type [$type:ident]
- @debug_format [$debug_format:tt]) => (
- $crate::newtype_index!(
- @handle_debug
- @derives [$($derives,)*]
- @type [$type]
- @debug_format [$debug_format]);
- );
-
- // Append comma to end of derives list if it's missing
- (@attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- derive [$($derives:ident),*]
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- derive [$($derives,)*]
- $($tokens)*);
- );
-
- // By not including the @derives marker in this list nor in the default args, we can force it
- // to come first if it exists. When encodable is custom, just use the derives list as-is.
- (@attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- derive [$($derives:ident,)+]
- ENCODABLE = custom
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @attrs [$(#[$attrs])*]
- @derives [$($derives,)+]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- );
-
- // By not including the @derives marker in this list nor in the default args, we can force it
- // to come first if it exists. When encodable isn't custom, add serialization traits by default.
- (@attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- derive [$($derives:ident,)+]
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @derives [$($derives,)+ RustcEncodable,]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- $crate::newtype_index!(@decodable $type);
- );
-
- // The case where no derives are added, but encodable is overridden. Don't
- // derive serialization traits
- (@attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- ENCODABLE = custom
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @derives []
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- );
-
- // The case where no derives are added, add serialization derives by default
- (@attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @derives [RustcEncodable,]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- $crate::newtype_index!(@decodable $type);
- );
-
- (@decodable $type:ident) => (
- impl ::rustc_serialize::Decodable for $type {
- fn decode<D: ::rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
- d.read_u32().map(Self::from)
- }
- }
- );
-
- // Rewrite final without comma to one that includes comma
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- $name:ident = $constant:expr) => (
- $crate::newtype_index!(
- @derives [$($derives,)*]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $name = $constant,);
- );
-
- // Rewrite final const without comma to one that includes comma
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- $(#[doc = $doc:expr])*
- const $name:ident = $constant:expr) => (
- $crate::newtype_index!(
- @derives [$($derives,)*]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $(#[doc = $doc])* const $name = $constant,);
- );
-
- // Replace existing default for max
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$_max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- MAX = $max:expr,
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @derives [$($derives,)*]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- );
-
- // Replace existing default for debug_format
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$_debug_format:tt]
- DEBUG_FORMAT = $debug_format:tt,
- $($tokens:tt)*) => (
- $crate::newtype_index!(
- @derives [$($derives,)*]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- );
-
- // Assign a user-defined constant
- (@derives [$($derives:ident,)*]
- @attrs [$(#[$attrs:meta])*]
- @type [$type:ident]
- @max [$max:expr]
- @vis [$v:vis]
- @debug_format [$debug_format:tt]
- $(#[doc = $doc:expr])*
- const $name:ident = $constant:expr,
- $($tokens:tt)*) => (
- $(#[doc = $doc])*
- pub const $name: $type = $type::from_u32_const($constant);
- $crate::newtype_index!(
- @derives [$($derives,)*]
- @attrs [$(#[$attrs])*]
- @type [$type]
- @max [$max]
- @vis [$v]
- @debug_format [$debug_format]
- $($tokens)*);
- );
-}
-
-#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct IndexVec<I: Idx, T> {
- pub raw: Vec<T>,
- _marker: PhantomData<fn(&I)>
-}
-
-// Whether `IndexVec` is `Send` depends only on the data,
-// not the phantom data.
-unsafe impl<I: Idx, T> Send for IndexVec<I, T> where T: Send {}
-
-impl<I: Idx, T: Encodable> Encodable for IndexVec<I, T> {
- fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
- Encodable::encode(&self.raw, s)
- }
-}
-
-impl<I: Idx, T: Decodable> Decodable for IndexVec<I, T> {
- fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
- Decodable::decode(d).map(|v| {
- IndexVec { raw: v, _marker: PhantomData }
- })
- }
-}
-
-impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Debug::fmt(&self.raw, fmt)
- }
-}
-
-pub type Enumerated<I, J> = iter::Map<iter::Enumerate<J>, IntoIdx<I>>;
-
-impl<I: Idx, T> IndexVec<I, T> {
- #[inline]
- pub fn new() -> Self {
- IndexVec { raw: Vec::new(), _marker: PhantomData }
- }
-
- #[inline]
- pub fn from_raw(raw: Vec<T>) -> Self {
- IndexVec { raw, _marker: PhantomData }
- }
-
- #[inline]
- pub fn with_capacity(capacity: usize) -> Self {
- IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }
- }
-
- #[inline]
- pub fn from_elem<S>(elem: T, universe: &IndexVec<I, S>) -> Self
- where T: Clone
- {
- IndexVec { raw: vec![elem; universe.len()], _marker: PhantomData }
- }
-
- #[inline]
- pub fn from_elem_n(elem: T, n: usize) -> Self
- where T: Clone
- {
- IndexVec { raw: vec![elem; n], _marker: PhantomData }
- }
-
- #[inline]
- pub fn push(&mut self, d: T) -> I {
- let idx = I::new(self.len());
- self.raw.push(d);
- idx
- }
-
- #[inline]
- pub fn pop(&mut self) -> Option<T> {
- self.raw.pop()
- }
-
- #[inline]
- pub fn len(&self) -> usize {
- self.raw.len()
- }
-
- /// Gives the next index that will be assigned when `push` is
- /// called.
- #[inline]
- pub fn next_index(&self) -> I {
- I::new(self.len())
- }
-
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.raw.is_empty()
- }
-
- #[inline]
- pub fn into_iter(self) -> vec::IntoIter<T> {
- self.raw.into_iter()
- }
-
- #[inline]
- pub fn into_iter_enumerated(self) -> Enumerated<I, vec::IntoIter<T>>
- {
- self.raw.into_iter().enumerate().map(IntoIdx { _marker: PhantomData })
- }
-
- #[inline]
- pub fn iter(&self) -> slice::Iter<'_, T> {
- self.raw.iter()
- }
-
- #[inline]
- pub fn iter_enumerated(&self) -> Enumerated<I, slice::Iter<'_, T>>
- {
- self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData })
- }
-
- #[inline]
- pub fn indices(&self) -> iter::Map<Range<usize>, IntoIdx<I>> {
- (0..self.len()).map(IntoIdx { _marker: PhantomData })
- }
-
- #[inline]
- pub fn iter_mut(&mut self) -> slice::IterMut<'_, T> {
- self.raw.iter_mut()
- }
-
- #[inline]
- pub fn iter_enumerated_mut(&mut self) -> Enumerated<I, slice::IterMut<'_, T>>
- {
- self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData })
- }
-
- #[inline]
- pub fn drain<'a, R: RangeBounds<usize>>(
- &'a mut self, range: R) -> impl Iterator<Item=T> + 'a {
- self.raw.drain(range)
- }
-
- #[inline]
- pub fn drain_enumerated<'a, R: RangeBounds<usize>>(
- &'a mut self, range: R) -> impl Iterator<Item=(I, T)> + 'a {
- self.raw.drain(range).enumerate().map(IntoIdx { _marker: PhantomData })
- }
-
- #[inline]
- pub fn last(&self) -> Option<I> {
- self.len().checked_sub(1).map(I::new)
- }
-
- #[inline]
- pub fn shrink_to_fit(&mut self) {
- self.raw.shrink_to_fit()
- }
-
- #[inline]
- pub fn swap(&mut self, a: I, b: I) {
- self.raw.swap(a.index(), b.index())
- }
-
- #[inline]
- pub fn truncate(&mut self, a: usize) {
- self.raw.truncate(a)
- }
-
- #[inline]
- pub fn get(&self, index: I) -> Option<&T> {
- self.raw.get(index.index())
- }
-
- #[inline]
- pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
- self.raw.get_mut(index.index())
- }
-
- /// Returns mutable references to two distinct elements, a and b. Panics if a == b.
- #[inline]
- pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) {
- let (ai, bi) = (a.index(), b.index());
- assert!(ai != bi);
-
- if ai < bi {
- let (c1, c2) = self.raw.split_at_mut(bi);
- (&mut c1[ai], &mut c2[0])
- } else {
- let (c2, c1) = self.pick2_mut(b, a);
- (c1, c2)
- }
- }
-
- pub fn convert_index_type<Ix: Idx>(self) -> IndexVec<Ix, T> {
- IndexVec {
- raw: self.raw,
- _marker: PhantomData,
- }
- }
-}
-
-impl<I: Idx, T: Clone> IndexVec<I, T> {
- /// Grows the index vector so that it contains an entry for
- /// `elem`; if that is already true, then has no
- /// effect. Otherwise, inserts new values as needed by invoking
- /// `fill_value`.
- #[inline]
- pub fn ensure_contains_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
- let min_new_len = elem.index() + 1;
- if self.len() < min_new_len {
- self.raw.resize_with(min_new_len, fill_value);
- }
- }
-
- #[inline]
- pub fn resize(&mut self, new_len: usize, value: T) {
- self.raw.resize(new_len, value)
- }
-
- #[inline]
- pub fn resize_to_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
- let min_new_len = elem.index() + 1;
- self.raw.resize_with(min_new_len, fill_value);
- }
-}
-
-impl<I: Idx, T: Ord> IndexVec<I, T> {
- #[inline]
- pub fn binary_search(&self, value: &T) -> Result<I, I> {
- match self.raw.binary_search(value) {
- Ok(i) => Ok(Idx::new(i)),
- Err(i) => Err(Idx::new(i)),
- }
- }
-}
-
-impl<I: Idx, T> Index<I> for IndexVec<I, T> {
- type Output = T;
-
- #[inline]
- fn index(&self, index: I) -> &T {
- &self.raw[index.index()]
- }
-}
-
-impl<I: Idx, T> IndexMut<I> for IndexVec<I, T> {
- #[inline]
- fn index_mut(&mut self, index: I) -> &mut T {
- &mut self.raw[index.index()]
- }
-}
-
-impl<I: Idx, T> Default for IndexVec<I, T> {
- #[inline]
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
- #[inline]
- fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) {
- self.raw.extend(iter);
- }
-}
-
-impl<I: Idx, T> FromIterator<T> for IndexVec<I, T> {
- #[inline]
- fn from_iter<J>(iter: J) -> Self where J: IntoIterator<Item=T> {
- IndexVec { raw: FromIterator::from_iter(iter), _marker: PhantomData }
- }
-}
-
-impl<I: Idx, T> IntoIterator for IndexVec<I, T> {
- type Item = T;
- type IntoIter = vec::IntoIter<T>;
-
- #[inline]
- fn into_iter(self) -> vec::IntoIter<T> {
- self.raw.into_iter()
- }
-
-}
-
-impl<'a, I: Idx, T> IntoIterator for &'a IndexVec<I, T> {
- type Item = &'a T;
- type IntoIter = slice::Iter<'a, T>;
-
- #[inline]
- fn into_iter(self) -> slice::Iter<'a, T> {
- self.raw.iter()
- }
-}
-
-impl<'a, I: Idx, T> IntoIterator for &'a mut IndexVec<I, T> {
- type Item = &'a mut T;
- type IntoIter = slice::IterMut<'a, T>;
-
- #[inline]
- fn into_iter(self) -> slice::IterMut<'a, T> {
- self.raw.iter_mut()
- }
-}
-
-pub struct IntoIdx<I: Idx> { _marker: PhantomData<fn(&I)> }
-impl<I: Idx, T> FnOnce<((usize, T),)> for IntoIdx<I> {
- type Output = (I, T);
-
- extern "rust-call" fn call_once(self, ((n, t),): ((usize, T),)) -> Self::Output {
- (I::new(n), t)
- }
-}
-
-impl<I: Idx, T> FnMut<((usize, T),)> for IntoIdx<I> {
- extern "rust-call" fn call_mut(&mut self, ((n, t),): ((usize, T),)) -> Self::Output {
- (I::new(n), t)
- }
-}
-
-impl<I: Idx> FnOnce<(usize,)> for IntoIdx<I> {
- type Output = I;
-
- extern "rust-call" fn call_once(self, (n,): (usize,)) -> Self::Output {
- I::new(n)
- }
-}
-
-impl<I: Idx> FnMut<(usize,)> for IntoIdx<I> {
- extern "rust-call" fn call_mut(&mut self, (n,): (usize,)) -> Self::Output {
- I::new(n)
- }
-}
pub mod svh;
pub mod base_n;
pub mod binary_search_util;
-pub mod bit_set;
pub mod box_region;
pub mod const_cstr;
pub mod flock;
pub mod fx;
pub mod stable_map;
pub mod graph;
-pub mod indexed_vec;
pub mod jobserver;
pub mod obligation_forest;
pub mod owning_ref;
/// call to `compress`.
///
/// `usize` indices are used here and throughout this module, rather than
- /// `newtype_index!` indices, because this code is hot enough that the
+ /// `rustc_index::newtype_index!` indices, because this code is hot enough that the
/// `u32`-to-`usize` conversions that would be required are significant,
/// and space considerations are not important.
nodes: Vec<Node<O>>,
use std::mem;
use smallvec::SmallVec;
use crate::sip128::SipHasher128;
-use crate::indexed_vec;
-use crate::bit_set;
+use rustc_index::vec;
+use rustc_index::bit_set;
/// When hashing something that ends up affecting properties like symbol names,
/// we want these symbol names to be calculated independently of other factors
}
}
-impl<I: indexed_vec::Idx, T, CTX> HashStable<CTX> for indexed_vec::IndexVec<I, T>
+impl<I: vec::Idx, T, CTX> HashStable<CTX> for vec::IndexVec<I, T>
where T: HashStable<CTX>,
{
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
}
-impl<I: indexed_vec::Idx, CTX> HashStable<CTX> for bit_set::BitSet<I>
+impl<I: vec::Idx, CTX> HashStable<CTX> for bit_set::BitSet<I>
{
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.words().hash_stable(ctx, hasher);
}
}
-impl<R: indexed_vec::Idx, C: indexed_vec::Idx, CTX> HashStable<CTX>
+impl<R: vec::Idx, C: vec::Idx, CTX> HashStable<CTX>
for bit_set::BitMatrix<R, C>
{
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
-use crate::bit_set::BitMatrix;
+use rustc_index::bit_set::BitMatrix;
use crate::fx::FxHashMap;
use crate::stable_hasher::{HashStable, StableHasher};
use crate::sync::Lock;
-use crate::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
pub fn iter<Ls>(
first: Option<Ls::LinkIndex>,
-use crate::bit_set::BitSet;
-use crate::indexed_vec::Idx;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::Idx;
use std::collections::VecDeque;
/// A work queue is a handy data structure for tracking work left to
--- /dev/null
+[package]
+authors = ["The Rust Project Developers"]
+name = "rustc_index"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+name = "rustc_index"
+path = "lib.rs"
+doctest = false
+
+[dependencies]
+rustc_serialize = { path = "../libserialize", package = "serialize" }
+smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
--- /dev/null
+use crate::vec::{Idx, IndexVec};
+use smallvec::SmallVec;
+use std::fmt;
+use std::iter;
+use std::marker::PhantomData;
+use std::mem;
+use std::slice;
+
+#[cfg(test)]
+mod tests;
+
+pub type Word = u64;
+pub const WORD_BYTES: usize = mem::size_of::<Word>();
+pub const WORD_BITS: usize = WORD_BYTES * 8;
+
+/// A fixed-size bitset type with a dense representation. It does not support
+/// resizing after creation; use `GrowableBitSet` for that.
+///
+/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
+/// just be `usize`.
+///
+/// All operations that involve an element will panic if the element is equal
+/// to or greater than the domain size. All operations that involve two bitsets
+/// will panic if the bitsets have differing domain sizes.
+#[derive(Clone, Eq, PartialEq, RustcDecodable, RustcEncodable)]
+pub struct BitSet<T: Idx> {
+ domain_size: usize,
+ words: Vec<Word>,
+ marker: PhantomData<T>,
+}
+
+impl<T: Idx> BitSet<T> {
+ /// Creates a new, empty bitset with a given `domain_size`.
+ #[inline]
+ pub fn new_empty(domain_size: usize) -> BitSet<T> {
+ let num_words = num_words(domain_size);
+ BitSet {
+ domain_size,
+ words: vec![0; num_words],
+ marker: PhantomData,
+ }
+ }
+
+ /// Creates a new, filled bitset with a given `domain_size`.
+ #[inline]
+ pub fn new_filled(domain_size: usize) -> BitSet<T> {
+ let num_words = num_words(domain_size);
+ let mut result = BitSet {
+ domain_size,
+ words: vec![!0; num_words],
+ marker: PhantomData,
+ };
+ result.clear_excess_bits();
+ result
+ }
+
+ /// Gets the domain size.
+ pub fn domain_size(&self) -> usize {
+ self.domain_size
+ }
+
+ /// Clear all elements.
+ #[inline]
+ pub fn clear(&mut self) {
+ for word in &mut self.words {
+ *word = 0;
+ }
+ }
+
+ /// Clear excess bits in the final word.
+ fn clear_excess_bits(&mut self) {
+ let num_bits_in_final_word = self.domain_size % WORD_BITS;
+ if num_bits_in_final_word > 0 {
+ let mask = (1 << num_bits_in_final_word) - 1;
+ let final_word_idx = self.words.len() - 1;
+ self.words[final_word_idx] &= mask;
+ }
+ }
+
+ /// Efficiently overwrite `self` with `other`.
+ pub fn overwrite(&mut self, other: &BitSet<T>) {
+ assert!(self.domain_size == other.domain_size);
+ self.words.clone_from_slice(&other.words);
+ }
+
+ /// Count the number of set bits in the set.
+ pub fn count(&self) -> usize {
+ self.words.iter().map(|e| e.count_ones() as usize).sum()
+ }
+
+ /// Returns `true` if `self` contains `elem`.
+ #[inline]
+ pub fn contains(&self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ let (word_index, mask) = word_index_and_mask(elem);
+ (self.words[word_index] & mask) != 0
+ }
+
+ /// Is `self` is a (non-strict) superset of `other`?
+ #[inline]
+ pub fn superset(&self, other: &BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b)
+ }
+
+ /// Is the set empty?
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.words.iter().all(|a| *a == 0)
+ }
+
+ /// Insert `elem`. Returns whether the set has changed.
+ #[inline]
+ pub fn insert(&mut self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ let (word_index, mask) = word_index_and_mask(elem);
+ let word_ref = &mut self.words[word_index];
+ let word = *word_ref;
+ let new_word = word | mask;
+ *word_ref = new_word;
+ new_word != word
+ }
+
+ /// Sets all bits to true.
+ pub fn insert_all(&mut self) {
+ for word in &mut self.words {
+ *word = !0;
+ }
+ self.clear_excess_bits();
+ }
+
+ /// Returns `true` if the set has changed.
+ #[inline]
+ pub fn remove(&mut self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ let (word_index, mask) = word_index_and_mask(elem);
+ let word_ref = &mut self.words[word_index];
+ let word = *word_ref;
+ let new_word = word & !mask;
+ *word_ref = new_word;
+ new_word != word
+ }
+
+ /// Sets `self = self | other` and returns `true` if `self` changed
+ /// (i.e., if new bits were added).
+ pub fn union(&mut self, other: &impl UnionIntoBitSet<T>) -> bool {
+ other.union_into(self)
+ }
+
+ /// Sets `self = self - other` and returns `true` if `self` changed.
+ /// (i.e., if any bits were removed).
+ pub fn subtract(&mut self, other: &impl SubtractFromBitSet<T>) -> bool {
+ other.subtract_from(self)
+ }
+
+ /// Sets `self = self & other` and return `true` if `self` changed.
+ /// (i.e., if any bits were removed).
+ pub fn intersect(&mut self, other: &BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ bitwise(&mut self.words, &other.words, |a, b| { a & b })
+ }
+
+ /// Gets a slice of the underlying words.
+ pub fn words(&self) -> &[Word] {
+ &self.words
+ }
+
+ /// Iterates over the indices of set bits in a sorted order.
+ #[inline]
+ pub fn iter(&self) -> BitIter<'_, T> {
+ BitIter {
+ cur: None,
+ iter: self.words.iter().enumerate(),
+ marker: PhantomData,
+ }
+ }
+
+ /// Duplicates the set as a hybrid set.
+ pub fn to_hybrid(&self) -> HybridBitSet<T> {
+ // Note: we currently don't bother trying to make a Sparse set.
+ HybridBitSet::Dense(self.to_owned())
+ }
+
+ /// Set `self = self | other`. In contrast to `union` returns `true` if the set contains at
+ /// least one bit that is not in `other` (i.e. `other` is not a superset of `self`).
+ ///
+ /// This is an optimization for union of a hybrid bitset.
+ fn reverse_union_sparse(&mut self, sparse: &SparseBitSet<T>) -> bool {
+ assert!(sparse.domain_size == self.domain_size);
+ self.clear_excess_bits();
+
+ let mut not_already = false;
+ // Index of the current word not yet merged.
+ let mut current_index = 0;
+ // Mask of bits that came from the sparse set in the current word.
+ let mut new_bit_mask = 0;
+ for (word_index, mask) in sparse.iter().map(|x| word_index_and_mask(*x)) {
+ // Next bit is in a word not inspected yet.
+ if word_index > current_index {
+ self.words[current_index] |= new_bit_mask;
+ // Were there any bits in the old word that did not occur in the sparse set?
+ not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
+ // Check all words we skipped for any set bit.
+ not_already |= self.words[current_index+1..word_index].iter().any(|&x| x != 0);
+ // Update next word.
+ current_index = word_index;
+ // Reset bit mask, no bits have been merged yet.
+ new_bit_mask = 0;
+ }
+ // Add bit and mark it as coming from the sparse set.
+ // self.words[word_index] |= mask;
+ new_bit_mask |= mask;
+ }
+ self.words[current_index] |= new_bit_mask;
+ // Any bits in the last inspected word that were not in the sparse set?
+ not_already |= (self.words[current_index] ^ new_bit_mask) != 0;
+ // Any bits in the tail? Note `clear_excess_bits` before.
+ not_already |= self.words[current_index+1..].iter().any(|&x| x != 0);
+
+ not_already
+ }
+}
+
+/// This is implemented by all the bitsets so that BitSet::union() can be
+/// passed any type of bitset.
+pub trait UnionIntoBitSet<T: Idx> {
+ // Performs `other = other | self`.
+ fn union_into(&self, other: &mut BitSet<T>) -> bool;
+}
+
+/// This is implemented by all the bitsets so that BitSet::subtract() can be
+/// passed any type of bitset.
+pub trait SubtractFromBitSet<T: Idx> {
+ // Performs `other = other - self`.
+ fn subtract_from(&self, other: &mut BitSet<T>) -> bool;
+}
+
+impl<T: Idx> UnionIntoBitSet<T> for BitSet<T> {
+ fn union_into(&self, other: &mut BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ bitwise(&mut other.words, &self.words, |a, b| { a | b })
+ }
+}
+
+impl<T: Idx> SubtractFromBitSet<T> for BitSet<T> {
+ fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ bitwise(&mut other.words, &self.words, |a, b| { a & !b })
+ }
+}
+
+impl<T: Idx> fmt::Debug for BitSet<T> {
+ fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
+ w.debug_list()
+ .entries(self.iter())
+ .finish()
+ }
+}
+
+impl<T: Idx> ToString for BitSet<T> {
+ fn to_string(&self) -> String {
+ let mut result = String::new();
+ let mut sep = '[';
+
+ // Note: this is a little endian printout of bytes.
+
+ // i tracks how many bits we have printed so far.
+ let mut i = 0;
+ for word in &self.words {
+ let mut word = *word;
+ for _ in 0..WORD_BYTES { // for each byte in `word`:
+ let remain = self.domain_size - i;
+ // If less than a byte remains, then mask just that many bits.
+ let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF };
+ assert!(mask <= 0xFF);
+ let byte = word & mask;
+
+ result.push_str(&format!("{}{:02x}", sep, byte));
+
+ if remain <= 8 { break; }
+ word >>= 8;
+ i += 8;
+ sep = '-';
+ }
+ sep = '|';
+ }
+ result.push(']');
+
+ result
+ }
+}
+
+pub struct BitIter<'a, T: Idx> {
+ cur: Option<(Word, usize)>,
+ iter: iter::Enumerate<slice::Iter<'a, Word>>,
+ marker: PhantomData<T>
+}
+
+impl<'a, T: Idx> Iterator for BitIter<'a, T> {
+ type Item = T;
+ fn next(&mut self) -> Option<T> {
+ loop {
+ if let Some((ref mut word, offset)) = self.cur {
+ let bit_pos = word.trailing_zeros() as usize;
+ if bit_pos != WORD_BITS {
+ let bit = 1 << bit_pos;
+ *word ^= bit;
+ return Some(T::new(bit_pos + offset))
+ }
+ }
+
+ let (i, word) = self.iter.next()?;
+ self.cur = Some((*word, WORD_BITS * i));
+ }
+ }
+}
+
+#[inline]
+fn bitwise<Op>(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool
+ where Op: Fn(Word, Word) -> Word
+{
+ assert_eq!(out_vec.len(), in_vec.len());
+ let mut changed = false;
+ for (out_elem, in_elem) in out_vec.iter_mut().zip(in_vec.iter()) {
+ let old_val = *out_elem;
+ let new_val = op(old_val, *in_elem);
+ *out_elem = new_val;
+ changed |= old_val != new_val;
+ }
+ changed
+}
+
+const SPARSE_MAX: usize = 8;
+
+/// A fixed-size bitset type with a sparse representation and a maximum of
+/// `SPARSE_MAX` elements. The elements are stored as a sorted `SmallVec` with
+/// no duplicates; although `SmallVec` can spill its elements to the heap, that
+/// never happens within this type because of the `SPARSE_MAX` limit.
+///
+/// This type is used by `HybridBitSet`; do not use directly.
+#[derive(Clone, Debug)]
+pub struct SparseBitSet<T: Idx> {
+ domain_size: usize,
+ elems: SmallVec<[T; SPARSE_MAX]>,
+}
+
+impl<T: Idx> SparseBitSet<T> {
+ fn new_empty(domain_size: usize) -> Self {
+ SparseBitSet {
+ domain_size,
+ elems: SmallVec::new()
+ }
+ }
+
+ fn len(&self) -> usize {
+ self.elems.len()
+ }
+
+ fn is_empty(&self) -> bool {
+ self.elems.len() == 0
+ }
+
+ fn contains(&self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ self.elems.contains(&elem)
+ }
+
+ fn insert(&mut self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ let changed = if let Some(i) = self.elems.iter().position(|&e| e >= elem) {
+ if self.elems[i] == elem {
+ // `elem` is already in the set.
+ false
+ } else {
+ // `elem` is smaller than one or more existing elements.
+ self.elems.insert(i, elem);
+ true
+ }
+ } else {
+ // `elem` is larger than all existing elements.
+ self.elems.push(elem);
+ true
+ };
+ assert!(self.len() <= SPARSE_MAX);
+ changed
+ }
+
+ fn remove(&mut self, elem: T) -> bool {
+ assert!(elem.index() < self.domain_size);
+ if let Some(i) = self.elems.iter().position(|&e| e == elem) {
+ self.elems.remove(i);
+ true
+ } else {
+ false
+ }
+ }
+
+ fn to_dense(&self) -> BitSet<T> {
+ let mut dense = BitSet::new_empty(self.domain_size);
+ for elem in self.elems.iter() {
+ dense.insert(*elem);
+ }
+ dense
+ }
+
+ fn iter(&self) -> slice::Iter<'_, T> {
+ self.elems.iter()
+ }
+}
+
+impl<T: Idx> UnionIntoBitSet<T> for SparseBitSet<T> {
+ fn union_into(&self, other: &mut BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ let mut changed = false;
+ for elem in self.iter() {
+ changed |= other.insert(*elem);
+ }
+ changed
+ }
+}
+
+impl<T: Idx> SubtractFromBitSet<T> for SparseBitSet<T> {
+ fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
+ assert_eq!(self.domain_size, other.domain_size);
+ let mut changed = false;
+ for elem in self.iter() {
+ changed |= other.remove(*elem);
+ }
+ changed
+ }
+}
+
+/// A fixed-size bitset type with a hybrid representation: sparse when there
+/// are up to a `SPARSE_MAX` elements in the set, but dense when there are more
+/// than `SPARSE_MAX`.
+///
+/// This type is especially efficient for sets that typically have a small
+/// number of elements, but a large `domain_size`, and are cleared frequently.
+///
+/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
+/// just be `usize`.
+///
+/// All operations that involve an element will panic if the element is equal
+/// to or greater than the domain size. All operations that involve two bitsets
+/// will panic if the bitsets have differing domain sizes.
+#[derive(Clone, Debug)]
+pub enum HybridBitSet<T: Idx> {
+ Sparse(SparseBitSet<T>),
+ Dense(BitSet<T>),
+}
+
+impl<T: Idx> HybridBitSet<T> {
+ pub fn new_empty(domain_size: usize) -> Self {
+ HybridBitSet::Sparse(SparseBitSet::new_empty(domain_size))
+ }
+
+ fn domain_size(&self) -> usize {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.domain_size,
+ HybridBitSet::Dense(dense) => dense.domain_size,
+ }
+ }
+
+ pub fn clear(&mut self) {
+ let domain_size = self.domain_size();
+ *self = HybridBitSet::new_empty(domain_size);
+ }
+
+ pub fn contains(&self, elem: T) -> bool {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.contains(elem),
+ HybridBitSet::Dense(dense) => dense.contains(elem),
+ }
+ }
+
+ pub fn superset(&self, other: &HybridBitSet<T>) -> bool {
+ match (self, other) {
+ (HybridBitSet::Dense(self_dense), HybridBitSet::Dense(other_dense)) => {
+ self_dense.superset(other_dense)
+ }
+ _ => {
+ assert!(self.domain_size() == other.domain_size());
+ other.iter().all(|elem| self.contains(elem))
+ }
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.is_empty(),
+ HybridBitSet::Dense(dense) => dense.is_empty(),
+ }
+ }
+
+ pub fn insert(&mut self, elem: T) -> bool {
+ // No need to check `elem` against `self.domain_size` here because all
+ // the match cases check it, one way or another.
+ match self {
+ HybridBitSet::Sparse(sparse) if sparse.len() < SPARSE_MAX => {
+ // The set is sparse and has space for `elem`.
+ sparse.insert(elem)
+ }
+ HybridBitSet::Sparse(sparse) if sparse.contains(elem) => {
+ // The set is sparse and does not have space for `elem`, but
+ // that doesn't matter because `elem` is already present.
+ false
+ }
+ HybridBitSet::Sparse(sparse) => {
+ // The set is sparse and full. Convert to a dense set.
+ let mut dense = sparse.to_dense();
+ let changed = dense.insert(elem);
+ assert!(changed);
+ *self = HybridBitSet::Dense(dense);
+ changed
+ }
+ HybridBitSet::Dense(dense) => dense.insert(elem),
+ }
+ }
+
+ pub fn insert_all(&mut self) {
+ let domain_size = self.domain_size();
+ match self {
+ HybridBitSet::Sparse(_) => {
+ *self = HybridBitSet::Dense(BitSet::new_filled(domain_size));
+ }
+ HybridBitSet::Dense(dense) => dense.insert_all(),
+ }
+ }
+
+ pub fn remove(&mut self, elem: T) -> bool {
+ // Note: we currently don't bother going from Dense back to Sparse.
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.remove(elem),
+ HybridBitSet::Dense(dense) => dense.remove(elem),
+ }
+ }
+
+ pub fn union(&mut self, other: &HybridBitSet<T>) -> bool {
+ match self {
+ HybridBitSet::Sparse(self_sparse) => {
+ match other {
+ HybridBitSet::Sparse(other_sparse) => {
+ // Both sets are sparse. Add the elements in
+ // `other_sparse` to `self` one at a time. This
+ // may or may not cause `self` to be densified.
+ assert_eq!(self.domain_size(), other.domain_size());
+ let mut changed = false;
+ for elem in other_sparse.iter() {
+ changed |= self.insert(*elem);
+ }
+ changed
+ }
+ HybridBitSet::Dense(other_dense) => {
+ // `self` is sparse and `other` is dense. To
+ // merge them, we have two available strategies:
+ // * Densify `self` then merge other
+ // * Clone other then integrate bits from `self`
+ // The second strategy requires dedicated method
+ // since the usual `union` returns the wrong
+ // result. In the dedicated case the computation
+ // is slightly faster if the bits of the sparse
+ // bitset map to only few words of the dense
+ // representation, i.e. indices are near each
+ // other.
+ //
+ // Benchmarking seems to suggest that the second
+ // option is worth it.
+ let mut new_dense = other_dense.clone();
+ let changed = new_dense.reverse_union_sparse(self_sparse);
+ *self = HybridBitSet::Dense(new_dense);
+ changed
+ }
+ }
+ }
+
+ HybridBitSet::Dense(self_dense) => self_dense.union(other),
+ }
+ }
+
+ /// Converts to a dense set, consuming itself in the process.
+ pub fn to_dense(self) -> BitSet<T> {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.to_dense(),
+ HybridBitSet::Dense(dense) => dense,
+ }
+ }
+
+ pub fn iter(&self) -> HybridIter<'_, T> {
+ match self {
+ HybridBitSet::Sparse(sparse) => HybridIter::Sparse(sparse.iter()),
+ HybridBitSet::Dense(dense) => HybridIter::Dense(dense.iter()),
+ }
+ }
+}
+
+impl<T: Idx> UnionIntoBitSet<T> for HybridBitSet<T> {
+ fn union_into(&self, other: &mut BitSet<T>) -> bool {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.union_into(other),
+ HybridBitSet::Dense(dense) => dense.union_into(other),
+ }
+ }
+}
+
+impl<T: Idx> SubtractFromBitSet<T> for HybridBitSet<T> {
+ fn subtract_from(&self, other: &mut BitSet<T>) -> bool {
+ match self {
+ HybridBitSet::Sparse(sparse) => sparse.subtract_from(other),
+ HybridBitSet::Dense(dense) => dense.subtract_from(other),
+ }
+ }
+}
+
+pub enum HybridIter<'a, T: Idx> {
+ Sparse(slice::Iter<'a, T>),
+ Dense(BitIter<'a, T>),
+}
+
+impl<'a, T: Idx> Iterator for HybridIter<'a, T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<T> {
+ match self {
+ HybridIter::Sparse(sparse) => sparse.next().map(|e| *e),
+ HybridIter::Dense(dense) => dense.next(),
+ }
+ }
+}
+
+/// A resizable bitset type with a dense representation.
+///
+/// `T` is an index type, typically a newtyped `usize` wrapper, but it can also
+/// just be `usize`.
+///
+/// All operations that involve an element will panic if the element is equal
+/// to or greater than the domain size.
+#[derive(Clone, Debug, PartialEq)]
+pub struct GrowableBitSet<T: Idx> {
+ bit_set: BitSet<T>,
+}
+
+impl<T: Idx> GrowableBitSet<T> {
+ /// Ensure that the set can hold at least `min_domain_size` elements.
+ pub fn ensure(&mut self, min_domain_size: usize) {
+ if self.bit_set.domain_size < min_domain_size {
+ self.bit_set.domain_size = min_domain_size;
+ }
+
+ let min_num_words = num_words(min_domain_size);
+ if self.bit_set.words.len() < min_num_words {
+ self.bit_set.words.resize(min_num_words, 0)
+ }
+ }
+
+ pub fn new_empty() -> GrowableBitSet<T> {
+ GrowableBitSet { bit_set: BitSet::new_empty(0) }
+ }
+
+ pub fn with_capacity(capacity: usize) -> GrowableBitSet<T> {
+ GrowableBitSet { bit_set: BitSet::new_empty(capacity) }
+ }
+
+ /// Returns `true` if the set has changed.
+ #[inline]
+ pub fn insert(&mut self, elem: T) -> bool {
+ self.ensure(elem.index() + 1);
+ self.bit_set.insert(elem)
+ }
+
+ #[inline]
+ pub fn contains(&self, elem: T) -> bool {
+ let (word_index, mask) = word_index_and_mask(elem);
+ if let Some(word) = self.bit_set.words.get(word_index) {
+ (word & mask) != 0
+ } else {
+ false
+ }
+ }
+}
+
+/// A fixed-size 2D bit matrix type with a dense representation.
+///
+/// `R` and `C` are index types used to identify rows and columns respectively;
+/// typically newtyped `usize` wrappers, but they can also just be `usize`.
+///
+/// All operations that involve a row and/or column index will panic if the
+/// index exceeds the relevant bound.
+#[derive(Clone, Debug, Eq, PartialEq, RustcDecodable, RustcEncodable)]
+pub struct BitMatrix<R: Idx, C: Idx> {
+ num_rows: usize,
+ num_columns: usize,
+ words: Vec<Word>,
+ marker: PhantomData<(R, C)>,
+}
+
+impl<R: Idx, C: Idx> BitMatrix<R, C> {
+ /// Creates a new `rows x columns` matrix, initially empty.
+ pub fn new(num_rows: usize, num_columns: usize) -> BitMatrix<R, C> {
+ // For every element, we need one bit for every other
+ // element. Round up to an even number of words.
+ let words_per_row = num_words(num_columns);
+ BitMatrix {
+ num_rows,
+ num_columns,
+ words: vec![0; num_rows * words_per_row],
+ marker: PhantomData,
+ }
+ }
+
+ /// Creates a new matrix, with `row` used as the value for every row.
+ pub fn from_row_n(row: &BitSet<C>, num_rows: usize) -> BitMatrix<R, C> {
+ let num_columns = row.domain_size();
+ let words_per_row = num_words(num_columns);
+ assert_eq!(words_per_row, row.words().len());
+ BitMatrix {
+ num_rows,
+ num_columns,
+ words: iter::repeat(row.words()).take(num_rows).flatten().cloned().collect(),
+ marker: PhantomData,
+ }
+ }
+
+ pub fn rows(&self) -> impl Iterator<Item = R> {
+ (0..self.num_rows).map(R::new)
+ }
+
+ /// The range of bits for a given row.
+ fn range(&self, row: R) -> (usize, usize) {
+ let words_per_row = num_words(self.num_columns);
+ let start = row.index() * words_per_row;
+ (start, start + words_per_row)
+ }
+
+ /// Sets the cell at `(row, column)` to true. Put another way, insert
+ /// `column` to the bitset for `row`.
+ ///
+ /// Returns `true` if this changed the matrix.
+ pub fn insert(&mut self, row: R, column: C) -> bool {
+ assert!(row.index() < self.num_rows && column.index() < self.num_columns);
+ let (start, _) = self.range(row);
+ let (word_index, mask) = word_index_and_mask(column);
+ let words = &mut self.words[..];
+ let word = words[start + word_index];
+ let new_word = word | mask;
+ words[start + word_index] = new_word;
+ word != new_word
+ }
+
+ /// Do the bits from `row` contain `column`? Put another way, is
+ /// the matrix cell at `(row, column)` true? Put yet another way,
+ /// if the matrix represents (transitive) reachability, can
+ /// `row` reach `column`?
+ pub fn contains(&self, row: R, column: C) -> bool {
+ assert!(row.index() < self.num_rows && column.index() < self.num_columns);
+ let (start, _) = self.range(row);
+ let (word_index, mask) = word_index_and_mask(column);
+ (self.words[start + word_index] & mask) != 0
+ }
+
+ /// Returns those indices that are true in rows `a` and `b`. This
+ /// is an O(n) operation where `n` is the number of elements
+ /// (somewhat independent from the actual size of the
+ /// intersection, in particular).
+ pub fn intersect_rows(&self, row1: R, row2: R) -> Vec<C> {
+ assert!(row1.index() < self.num_rows && row2.index() < self.num_rows);
+ let (row1_start, row1_end) = self.range(row1);
+ let (row2_start, row2_end) = self.range(row2);
+ let mut result = Vec::with_capacity(self.num_columns);
+ for (base, (i, j)) in (row1_start..row1_end).zip(row2_start..row2_end).enumerate() {
+ let mut v = self.words[i] & self.words[j];
+ for bit in 0..WORD_BITS {
+ if v == 0 {
+ break;
+ }
+ if v & 0x1 != 0 {
+ result.push(C::new(base * WORD_BITS + bit));
+ }
+ v >>= 1;
+ }
+ }
+ result
+ }
+
+ /// Adds the bits from row `read` to the bits from row `write`, and
+ /// returns `true` if anything changed.
+ ///
+ /// This is used when computing transitive reachability because if
+ /// you have an edge `write -> read`, because in that case
+ /// `write` can reach everything that `read` can (and
+ /// potentially more).
+ pub fn union_rows(&mut self, read: R, write: R) -> bool {
+ assert!(read.index() < self.num_rows && write.index() < self.num_rows);
+ let (read_start, read_end) = self.range(read);
+ let (write_start, write_end) = self.range(write);
+ let words = &mut self.words[..];
+ let mut changed = false;
+ for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) {
+ let word = words[write_index];
+ let new_word = word | words[read_index];
+ words[write_index] = new_word;
+ changed |= word != new_word;
+ }
+ changed
+ }
+
+ /// Adds the bits from `with` to the bits from row `write`, and
+ /// returns `true` if anything changed.
+ pub fn union_row_with(&mut self, with: &BitSet<C>, write: R) -> bool {
+ assert!(write.index() < self.num_rows);
+ assert_eq!(with.domain_size(), self.num_columns);
+ let (write_start, write_end) = self.range(write);
+ let mut changed = false;
+ for (read_index, write_index) in (0..with.words().len()).zip(write_start..write_end) {
+ let word = self.words[write_index];
+ let new_word = word | with.words()[read_index];
+ self.words[write_index] = new_word;
+ changed |= word != new_word;
+ }
+ changed
+ }
+
+ /// Sets every cell in `row` to true.
+ pub fn insert_all_into_row(&mut self, row: R) {
+ assert!(row.index() < self.num_rows);
+ let (start, end) = self.range(row);
+ let words = &mut self.words[..];
+ for index in start..end {
+ words[index] = !0;
+ }
+ self.clear_excess_bits(row);
+ }
+
+ /// Clear excess bits in the final word of the row.
+ fn clear_excess_bits(&mut self, row: R) {
+ let num_bits_in_final_word = self.num_columns % WORD_BITS;
+ if num_bits_in_final_word > 0 {
+ let mask = (1 << num_bits_in_final_word) - 1;
+ let (_, end) = self.range(row);
+ let final_word_idx = end - 1;
+ self.words[final_word_idx] &= mask;
+ }
+ }
+
+ /// Gets a slice of the underlying words.
+ pub fn words(&self) -> &[Word] {
+ &self.words
+ }
+
+ /// Iterates through all the columns set to true in a given row of
+ /// the matrix.
+ pub fn iter(&self, row: R) -> BitIter<'_, C> {
+ assert!(row.index() < self.num_rows);
+ let (start, end) = self.range(row);
+ BitIter {
+ cur: None,
+ iter: self.words[start..end].iter().enumerate(),
+ marker: PhantomData,
+ }
+ }
+
+ /// Returns the number of elements in `row`.
+ pub fn count(&self, row: R) -> usize {
+ let (start, end) = self.range(row);
+ self.words[start..end].iter().map(|e| e.count_ones() as usize).sum()
+ }
+}
+
+/// A fixed-column-size, variable-row-size 2D bit matrix with a moderately
+/// sparse representation.
+///
+/// Initially, every row has no explicit representation. If any bit within a
+/// row is set, the entire row is instantiated as `Some(<HybridBitSet>)`.
+/// Furthermore, any previously uninstantiated rows prior to it will be
+/// instantiated as `None`. Those prior rows may themselves become fully
+/// instantiated later on if any of their bits are set.
+///
+/// `R` and `C` are index types used to identify rows and columns respectively;
+/// typically newtyped `usize` wrappers, but they can also just be `usize`.
+#[derive(Clone, Debug)]
+pub struct SparseBitMatrix<R, C>
+where
+ R: Idx,
+ C: Idx,
+{
+ num_columns: usize,
+ rows: IndexVec<R, Option<HybridBitSet<C>>>,
+}
+
+impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
+ /// Creates a new empty sparse bit matrix with no rows or columns.
+ pub fn new(num_columns: usize) -> Self {
+ Self {
+ num_columns,
+ rows: IndexVec::new(),
+ }
+ }
+
+ fn ensure_row(&mut self, row: R) -> &mut HybridBitSet<C> {
+ // Instantiate any missing rows up to and including row `row` with an
+ // empty HybridBitSet.
+ self.rows.ensure_contains_elem(row, || None);
+
+ // Then replace row `row` with a full HybridBitSet if necessary.
+ let num_columns = self.num_columns;
+ self.rows[row].get_or_insert_with(|| HybridBitSet::new_empty(num_columns))
+ }
+
+ /// Sets the cell at `(row, column)` to true. Put another way, insert
+ /// `column` to the bitset for `row`.
+ ///
+ /// Returns `true` if this changed the matrix.
+ pub fn insert(&mut self, row: R, column: C) -> bool {
+ self.ensure_row(row).insert(column)
+ }
+
+ /// Do the bits from `row` contain `column`? Put another way, is
+ /// the matrix cell at `(row, column)` true? Put yet another way,
+ /// if the matrix represents (transitive) reachability, can
+ /// `row` reach `column`?
+ pub fn contains(&self, row: R, column: C) -> bool {
+ self.row(row).map_or(false, |r| r.contains(column))
+ }
+
+ /// Adds the bits from row `read` to the bits from row `write`, and
+ /// returns `true` if anything changed.
+ ///
+ /// This is used when computing transitive reachability because if
+ /// you have an edge `write -> read`, because in that case
+ /// `write` can reach everything that `read` can (and
+ /// potentially more).
+ pub fn union_rows(&mut self, read: R, write: R) -> bool {
+ if read == write || self.row(read).is_none() {
+ return false;
+ }
+
+ self.ensure_row(write);
+ if let (Some(read_row), Some(write_row)) = self.rows.pick2_mut(read, write) {
+ write_row.union(read_row)
+ } else {
+ unreachable!()
+ }
+ }
+
+ /// Union a row, `from`, into the `into` row.
+ pub fn union_into_row(&mut self, into: R, from: &HybridBitSet<C>) -> bool {
+ self.ensure_row(into).union(from)
+ }
+
+ /// Insert all bits in the given row.
+ pub fn insert_all_into_row(&mut self, row: R) {
+ self.ensure_row(row).insert_all();
+ }
+
+ pub fn rows(&self) -> impl Iterator<Item = R> {
+ self.rows.indices()
+ }
+
+ /// Iterates through all the columns set to true in a given row of
+ /// the matrix.
+ pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
+ self.row(row).into_iter().flat_map(|r| r.iter())
+ }
+
+ pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
+ if let Some(Some(row)) = self.rows.get(row) {
+ Some(row)
+ } else {
+ None
+ }
+ }
+}
+
+#[inline]
+fn num_words<T: Idx>(domain_size: T) -> usize {
+ (domain_size.index() + WORD_BITS - 1) / WORD_BITS
+}
+
+#[inline]
+fn word_index_and_mask<T: Idx>(elem: T) -> (usize, Word) {
+ let elem = elem.index();
+ let word_index = elem / WORD_BITS;
+ let mask = 1 << (elem % WORD_BITS);
+ (word_index, mask)
+}
--- /dev/null
+use super::*;
+
+extern crate test;
+use test::Bencher;
+
+#[test]
+fn test_new_filled() {
+ for i in 0..128 {
+ let idx_buf = BitSet::new_filled(i);
+ let elems: Vec<usize> = idx_buf.iter().collect();
+ let expected: Vec<usize> = (0..i).collect();
+ assert_eq!(elems, expected);
+ }
+}
+
+#[test]
+fn bitset_iter_works() {
+ let mut bitset: BitSet<usize> = BitSet::new_empty(100);
+ bitset.insert(1);
+ bitset.insert(10);
+ bitset.insert(19);
+ bitset.insert(62);
+ bitset.insert(63);
+ bitset.insert(64);
+ bitset.insert(65);
+ bitset.insert(66);
+ bitset.insert(99);
+ assert_eq!(
+ bitset.iter().collect::<Vec<_>>(),
+ [1, 10, 19, 62, 63, 64, 65, 66, 99]
+ );
+}
+
+#[test]
+fn bitset_iter_works_2() {
+ let mut bitset: BitSet<usize> = BitSet::new_empty(320);
+ bitset.insert(0);
+ bitset.insert(127);
+ bitset.insert(191);
+ bitset.insert(255);
+ bitset.insert(319);
+ assert_eq!(bitset.iter().collect::<Vec<_>>(), [0, 127, 191, 255, 319]);
+}
+
+#[test]
+fn union_two_sets() {
+ let mut set1: BitSet<usize> = BitSet::new_empty(65);
+ let mut set2: BitSet<usize> = BitSet::new_empty(65);
+ assert!(set1.insert(3));
+ assert!(!set1.insert(3));
+ assert!(set2.insert(5));
+ assert!(set2.insert(64));
+ assert!(set1.union(&set2));
+ assert!(!set1.union(&set2));
+ assert!(set1.contains(3));
+ assert!(!set1.contains(4));
+ assert!(set1.contains(5));
+ assert!(!set1.contains(63));
+ assert!(set1.contains(64));
+}
+
+#[test]
+fn hybrid_bitset() {
+ let mut sparse038: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+ assert!(sparse038.is_empty());
+ assert!(sparse038.insert(0));
+ assert!(sparse038.insert(1));
+ assert!(sparse038.insert(8));
+ assert!(sparse038.insert(3));
+ assert!(!sparse038.insert(3));
+ assert!(sparse038.remove(1));
+ assert!(!sparse038.is_empty());
+ assert_eq!(sparse038.iter().collect::<Vec<_>>(), [0, 3, 8]);
+
+ for i in 0..256 {
+ if i == 0 || i == 3 || i == 8 {
+ assert!(sparse038.contains(i));
+ } else {
+ assert!(!sparse038.contains(i));
+ }
+ }
+
+ let mut sparse01358 = sparse038.clone();
+ assert!(sparse01358.insert(1));
+ assert!(sparse01358.insert(5));
+ assert_eq!(sparse01358.iter().collect::<Vec<_>>(), [0, 1, 3, 5, 8]);
+
+ let mut dense10 = HybridBitSet::new_empty(256);
+ for i in 0..10 {
+ assert!(dense10.insert(i));
+ }
+ assert!(!dense10.is_empty());
+ assert_eq!(dense10.iter().collect::<Vec<_>>(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+
+ let mut dense256 = HybridBitSet::new_empty(256);
+ assert!(dense256.is_empty());
+ dense256.insert_all();
+ assert!(!dense256.is_empty());
+ for i in 0..256 {
+ assert!(dense256.contains(i));
+ }
+
+ assert!(sparse038.superset(&sparse038)); // sparse + sparse (self)
+ assert!(sparse01358.superset(&sparse038)); // sparse + sparse
+ assert!(dense10.superset(&sparse038)); // dense + sparse
+ assert!(dense10.superset(&dense10)); // dense + dense (self)
+ assert!(dense256.superset(&dense10)); // dense + dense
+
+ let mut hybrid = sparse038;
+ assert!(!sparse01358.union(&hybrid)); // no change
+ assert!(hybrid.union(&sparse01358));
+ assert!(hybrid.superset(&sparse01358) && sparse01358.superset(&hybrid));
+ assert!(!dense10.union(&sparse01358));
+ assert!(!dense256.union(&dense10));
+ let mut dense = dense10;
+ assert!(dense.union(&dense256));
+ assert!(dense.superset(&dense256) && dense256.superset(&dense));
+ assert!(hybrid.union(&dense256));
+ assert!(hybrid.superset(&dense256) && dense256.superset(&hybrid));
+
+ assert_eq!(dense256.iter().count(), 256);
+ let mut dense0 = dense256;
+ for i in 0..256 {
+ assert!(dense0.remove(i));
+ }
+ assert!(!dense0.remove(0));
+ assert!(dense0.is_empty());
+}
+
+#[test]
+fn grow() {
+ let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
+ for index in 0..65 {
+ assert!(set.insert(index));
+ assert!(!set.insert(index));
+ }
+ set.ensure(128);
+
+ // Check if the bits set before growing are still set
+ for index in 0..65 {
+ assert!(set.contains(index));
+ }
+
+ // Check if the new bits are all un-set
+ for index in 65..128 {
+ assert!(!set.contains(index));
+ }
+
+ // Check that we can set all new bits without running out of bounds
+ for index in 65..128 {
+ assert!(set.insert(index));
+ assert!(!set.insert(index));
+ }
+}
+
+#[test]
+fn matrix_intersection() {
+ let mut matrix: BitMatrix<usize, usize> = BitMatrix::new(200, 200);
+
+ // (*) Elements reachable from both 2 and 65.
+
+ matrix.insert(2, 3);
+ matrix.insert(2, 6);
+ matrix.insert(2, 10); // (*)
+ matrix.insert(2, 64); // (*)
+ matrix.insert(2, 65);
+ matrix.insert(2, 130);
+ matrix.insert(2, 160); // (*)
+
+ matrix.insert(64, 133);
+
+ matrix.insert(65, 2);
+ matrix.insert(65, 8);
+ matrix.insert(65, 10); // (*)
+ matrix.insert(65, 64); // (*)
+ matrix.insert(65, 68);
+ matrix.insert(65, 133);
+ matrix.insert(65, 160); // (*)
+
+ let intersection = matrix.intersect_rows(2, 64);
+ assert!(intersection.is_empty());
+
+ let intersection = matrix.intersect_rows(2, 65);
+ assert_eq!(intersection, &[10, 64, 160]);
+}
+
+#[test]
+fn matrix_iter() {
+ let mut matrix: BitMatrix<usize, usize> = BitMatrix::new(64, 100);
+ matrix.insert(3, 22);
+ matrix.insert(3, 75);
+ matrix.insert(2, 99);
+ matrix.insert(4, 0);
+ matrix.union_rows(3, 5);
+ matrix.insert_all_into_row(6);
+
+ let expected = [99];
+ let mut iter = expected.iter();
+ for i in matrix.iter(2) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [22, 75];
+ let mut iter = expected.iter();
+ assert_eq!(matrix.count(3), expected.len());
+ for i in matrix.iter(3) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [0];
+ let mut iter = expected.iter();
+ assert_eq!(matrix.count(4), expected.len());
+ for i in matrix.iter(4) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [22, 75];
+ let mut iter = expected.iter();
+ assert_eq!(matrix.count(5), expected.len());
+ for i in matrix.iter(5) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ assert_eq!(matrix.count(6), 100);
+ let mut count = 0;
+ for (idx, i) in matrix.iter(6).enumerate() {
+ assert_eq!(idx, i);
+ count += 1;
+ }
+ assert_eq!(count, 100);
+
+ if let Some(i) = matrix.iter(7).next() {
+ panic!("expected no elements in row, but contains element {:?}", i);
+ }
+}
+
+#[test]
+fn sparse_matrix_iter() {
+ let mut matrix: SparseBitMatrix<usize, usize> = SparseBitMatrix::new(100);
+ matrix.insert(3, 22);
+ matrix.insert(3, 75);
+ matrix.insert(2, 99);
+ matrix.insert(4, 0);
+ matrix.union_rows(3, 5);
+
+ let expected = [99];
+ let mut iter = expected.iter();
+ for i in matrix.iter(2) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [22, 75];
+ let mut iter = expected.iter();
+ for i in matrix.iter(3) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [0];
+ let mut iter = expected.iter();
+ for i in matrix.iter(4) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+
+ let expected = [22, 75];
+ let mut iter = expected.iter();
+ for i in matrix.iter(5) {
+ let j = *iter.next().unwrap();
+ assert_eq!(i, j);
+ }
+ assert!(iter.next().is_none());
+}
+
+/// Merge dense hybrid set into empty sparse hybrid set.
+#[bench]
+fn union_hybrid_sparse_empty_to_dense(b: &mut Bencher) {
+ let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+ for i in 0..10 {
+ assert!(pre_dense.insert(i));
+ }
+ let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+ b.iter(|| {
+ let dense = pre_dense.clone();
+ let mut sparse = pre_sparse.clone();
+ sparse.union(&dense);
+ })
+}
+
+/// Merge dense hybrid set into full hybrid set with same indices.
+#[bench]
+fn union_hybrid_sparse_full_to_dense(b: &mut Bencher) {
+ let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+ for i in 0..10 {
+ assert!(pre_dense.insert(i));
+ }
+ let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(256);
+ for i in 0..SPARSE_MAX {
+ assert!(pre_sparse.insert(i));
+ }
+ b.iter(|| {
+ let dense = pre_dense.clone();
+ let mut sparse = pre_sparse.clone();
+ sparse.union(&dense);
+ })
+}
+
+/// Merge dense hybrid set into full hybrid set with indices over the whole domain.
+#[bench]
+fn union_hybrid_sparse_domain_to_dense(b: &mut Bencher) {
+ let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
+ for i in 0..10 {
+ assert!(pre_dense.insert(i));
+ }
+ let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX*64);
+ for i in 0..SPARSE_MAX {
+ assert!(pre_sparse.insert(i*64));
+ }
+ b.iter(|| {
+ let dense = pre_dense.clone();
+ let mut sparse = pre_sparse.clone();
+ sparse.union(&dense);
+ })
+}
+
+/// Merge dense hybrid set into empty hybrid set where the domain is very small.
+#[bench]
+fn union_hybrid_sparse_empty_small_domain(b: &mut Bencher) {
+ let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+ for i in 0..SPARSE_MAX {
+ assert!(pre_dense.insert(i));
+ }
+ let pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+ b.iter(|| {
+ let dense = pre_dense.clone();
+ let mut sparse = pre_sparse.clone();
+ sparse.union(&dense);
+ })
+}
+
+/// Merge dense hybrid set into full hybrid set where the domain is very small.
+#[bench]
+fn union_hybrid_sparse_full_small_domain(b: &mut Bencher) {
+ let mut pre_dense: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+ for i in 0..SPARSE_MAX {
+ assert!(pre_dense.insert(i));
+ }
+ let mut pre_sparse: HybridBitSet<usize> = HybridBitSet::new_empty(SPARSE_MAX);
+ for i in 0..SPARSE_MAX {
+ assert!(pre_sparse.insert(i));
+ }
+ b.iter(|| {
+ let dense = pre_dense.clone();
+ let mut sparse = pre_sparse.clone();
+ sparse.union(&dense);
+ })
+}
--- /dev/null
+#![feature(allow_internal_unstable)]
+#![feature(unboxed_closures)]
+#![feature(test)]
+#![feature(fn_traits)]
+
+pub mod vec;
+pub mod bit_set;
--- /dev/null
+use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
+
+use std::fmt::Debug;
+use std::iter::{self, FromIterator};
+use std::slice;
+use std::marker::PhantomData;
+use std::ops::{Index, IndexMut, Range, RangeBounds};
+use std::fmt;
+use std::hash::Hash;
+use std::vec;
+use std::u32;
+
+/// Represents some newtyped `usize` wrapper.
+///
+/// Purpose: avoid mixing indexes for different bitvector domains.
+pub trait Idx: Copy + 'static + Ord + Debug + Hash {
+ fn new(idx: usize) -> Self;
+
+ fn index(self) -> usize;
+
+ fn increment_by(&mut self, amount: usize) {
+ *self = self.plus(amount);
+ }
+
+ fn plus(self, amount: usize) -> Self {
+ Self::new(self.index() + amount)
+ }
+}
+
+impl Idx for usize {
+ #[inline]
+ fn new(idx: usize) -> Self { idx }
+ #[inline]
+ fn index(self) -> usize { self }
+}
+
+impl Idx for u32 {
+ #[inline]
+ fn new(idx: usize) -> Self { assert!(idx <= u32::MAX as usize); idx as u32 }
+ #[inline]
+ fn index(self) -> usize { self as usize }
+}
+
+/// Creates a struct type `S` that can be used as an index with
+/// `IndexVec` and so on.
+///
+/// There are two ways of interacting with these indices:
+///
+/// - The `From` impls are the preferred way. So you can do
+/// `S::from(v)` with a `usize` or `u32`. And you can convert back
+/// to an integer with `u32::from(s)`.
+///
+/// - Alternatively, you can use the methods `S::new(v)` and `s.index()`
+/// to create/return a value.
+///
+/// Internally, the index uses a u32, so the index must not exceed
+/// `u32::MAX`. You can also customize things like the `Debug` impl,
+/// what traits are derived, and so forth via the macro.
+#[macro_export]
+#[allow_internal_unstable(step_trait, rustc_attrs)]
+macro_rules! newtype_index {
+ // ---- public rules ----
+
+ // Use default constants
+ ($(#[$attrs:meta])* $v:vis struct $name:ident { .. }) => (
+ $crate::newtype_index!(
+ // Leave out derives marker so we can use its absence to ensure it comes first
+ @attrs [$(#[$attrs])*]
+ @type [$name]
+ // shave off 256 indices at the end to allow space for packing these indices into enums
+ @max [0xFFFF_FF00]
+ @vis [$v]
+ @debug_format ["{}"]);
+ );
+
+ // Define any constants
+ ($(#[$attrs:meta])* $v:vis struct $name:ident { $($tokens:tt)+ }) => (
+ $crate::newtype_index!(
+ // Leave out derives marker so we can use its absence to ensure it comes first
+ @attrs [$(#[$attrs])*]
+ @type [$name]
+ // shave off 256 indices at the end to allow space for packing these indices into enums
+ @max [0xFFFF_FF00]
+ @vis [$v]
+ @debug_format ["{}"]
+ $($tokens)+);
+ );
+
+ // ---- private rules ----
+
+ // Base case, user-defined constants (if any) have already been defined
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]) => (
+ $(#[$attrs])*
+ #[derive(Copy, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)]
+ #[rustc_layout_scalar_valid_range_end($max)]
+ $v struct $type {
+ private: u32
+ }
+
+ impl Clone for $type {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ impl $type {
+ $v const MAX_AS_U32: u32 = $max;
+
+ $v const MAX: $type = $type::from_u32_const($max);
+
+ #[inline]
+ $v fn from_usize(value: usize) -> Self {
+ assert!(value <= ($max as usize));
+ unsafe {
+ $type::from_u32_unchecked(value as u32)
+ }
+ }
+
+ #[inline]
+ $v fn from_u32(value: u32) -> Self {
+ assert!(value <= $max);
+ unsafe {
+ $type::from_u32_unchecked(value)
+ }
+ }
+
+ /// Hacky variant of `from_u32` for use in constants.
+ /// This version checks the "max" constraint by using an
+ /// invalid array dereference.
+ #[inline]
+ $v const fn from_u32_const(value: u32) -> Self {
+ // This will fail at const eval time unless `value <=
+ // max` is true (in which case we get the index 0).
+ // It will also fail at runtime, of course, but in a
+ // kind of wacky way.
+ let _ = ["out of range value used"][
+ !(value <= $max) as usize
+ ];
+
+ unsafe {
+ $type { private: value }
+ }
+ }
+
+ #[inline]
+ $v const unsafe fn from_u32_unchecked(value: u32) -> Self {
+ $type { private: value }
+ }
+
+ /// Extracts the value of this index as an integer.
+ #[inline]
+ $v fn index(self) -> usize {
+ self.as_usize()
+ }
+
+ /// Extracts the value of this index as a `u32`.
+ #[inline]
+ $v fn as_u32(self) -> u32 {
+ self.private
+ }
+
+ /// Extracts the value of this index as a `usize`.
+ #[inline]
+ $v fn as_usize(self) -> usize {
+ self.as_u32() as usize
+ }
+ }
+
+ impl std::ops::Add<usize> for $type {
+ type Output = Self;
+
+ fn add(self, other: usize) -> Self {
+ Self::new(self.index() + other)
+ }
+ }
+
+ impl Idx for $type {
+ #[inline]
+ fn new(value: usize) -> Self {
+ Self::from(value)
+ }
+
+ #[inline]
+ fn index(self) -> usize {
+ usize::from(self)
+ }
+ }
+
+ impl ::std::iter::Step for $type {
+ #[inline]
+ fn steps_between(start: &Self, end: &Self) -> Option<usize> {
+ <usize as ::std::iter::Step>::steps_between(
+ &Idx::index(*start),
+ &Idx::index(*end),
+ )
+ }
+
+ #[inline]
+ fn replace_one(&mut self) -> Self {
+ ::std::mem::replace(self, Self::new(1))
+ }
+
+ #[inline]
+ fn replace_zero(&mut self) -> Self {
+ ::std::mem::replace(self, Self::new(0))
+ }
+
+ #[inline]
+ fn add_one(&self) -> Self {
+ Self::new(Idx::index(*self) + 1)
+ }
+
+ #[inline]
+ fn sub_one(&self) -> Self {
+ Self::new(Idx::index(*self) - 1)
+ }
+
+ #[inline]
+ fn add_usize(&self, u: usize) -> Option<Self> {
+ Idx::index(*self).checked_add(u).map(Self::new)
+ }
+
+ #[inline]
+ fn sub_usize(&self, u: usize) -> Option<Self> {
+ Idx::index(*self).checked_sub(u).map(Self::new)
+ }
+ }
+
+ impl From<$type> for u32 {
+ #[inline]
+ fn from(v: $type) -> u32 {
+ v.as_u32()
+ }
+ }
+
+ impl From<$type> for usize {
+ #[inline]
+ fn from(v: $type) -> usize {
+ v.as_usize()
+ }
+ }
+
+ impl From<usize> for $type {
+ #[inline]
+ fn from(value: usize) -> Self {
+ $type::from_usize(value)
+ }
+ }
+
+ impl From<u32> for $type {
+ #[inline]
+ fn from(value: u32) -> Self {
+ $type::from_u32(value)
+ }
+ }
+
+ $crate::newtype_index!(
+ @handle_debug
+ @derives [$($derives,)*]
+ @type [$type]
+ @debug_format [$debug_format]);
+ );
+
+ // base case for handle_debug where format is custom. No Debug implementation is emitted.
+ (@handle_debug
+ @derives [$($_derives:ident,)*]
+ @type [$type:ident]
+ @debug_format [custom]) => ();
+
+ // base case for handle_debug, no debug overrides found, so use default
+ (@handle_debug
+ @derives []
+ @type [$type:ident]
+ @debug_format [$debug_format:tt]) => (
+ impl ::std::fmt::Debug for $type {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ write!(fmt, $debug_format, self.as_u32())
+ }
+ }
+ );
+
+ // Debug is requested for derive, don't generate any Debug implementation.
+ (@handle_debug
+ @derives [Debug, $($derives:ident,)*]
+ @type [$type:ident]
+ @debug_format [$debug_format:tt]) => ();
+
+ // It's not Debug, so just pop it off the front of the derives stack and check the rest.
+ (@handle_debug
+ @derives [$_derive:ident, $($derives:ident,)*]
+ @type [$type:ident]
+ @debug_format [$debug_format:tt]) => (
+ $crate::newtype_index!(
+ @handle_debug
+ @derives [$($derives,)*]
+ @type [$type]
+ @debug_format [$debug_format]);
+ );
+
+ // Append comma to end of derives list if it's missing
+ (@attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ derive [$($derives:ident),*]
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ derive [$($derives,)*]
+ $($tokens)*);
+ );
+
+ // By not including the @derives marker in this list nor in the default args, we can force it
+ // to come first if it exists. When encodable is custom, just use the derives list as-is.
+ (@attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ derive [$($derives:ident,)+]
+ ENCODABLE = custom
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @attrs [$(#[$attrs])*]
+ @derives [$($derives,)+]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ );
+
+ // By not including the @derives marker in this list nor in the default args, we can force it
+ // to come first if it exists. When encodable isn't custom, add serialization traits by default.
+ (@attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ derive [$($derives:ident,)+]
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @derives [$($derives,)+ RustcEncodable,]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ $crate::newtype_index!(@decodable $type);
+ );
+
+ // The case where no derives are added, but encodable is overridden. Don't
+ // derive serialization traits
+ (@attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ ENCODABLE = custom
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @derives []
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ );
+
+ // The case where no derives are added, add serialization derives by default
+ (@attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @derives [RustcEncodable,]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ $crate::newtype_index!(@decodable $type);
+ );
+
+ (@decodable $type:ident) => (
+ impl ::rustc_serialize::Decodable for $type {
+ fn decode<D: ::rustc_serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
+ d.read_u32().map(Self::from)
+ }
+ }
+ );
+
+ // Rewrite final without comma to one that includes comma
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ $name:ident = $constant:expr) => (
+ $crate::newtype_index!(
+ @derives [$($derives,)*]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $name = $constant,);
+ );
+
+ // Rewrite final const without comma to one that includes comma
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ $(#[doc = $doc:expr])*
+ const $name:ident = $constant:expr) => (
+ $crate::newtype_index!(
+ @derives [$($derives,)*]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $(#[doc = $doc])* const $name = $constant,);
+ );
+
+ // Replace existing default for max
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$_max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ MAX = $max:expr,
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @derives [$($derives,)*]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ );
+
+ // Replace existing default for debug_format
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$_debug_format:tt]
+ DEBUG_FORMAT = $debug_format:tt,
+ $($tokens:tt)*) => (
+ $crate::newtype_index!(
+ @derives [$($derives,)*]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ );
+
+ // Assign a user-defined constant
+ (@derives [$($derives:ident,)*]
+ @attrs [$(#[$attrs:meta])*]
+ @type [$type:ident]
+ @max [$max:expr]
+ @vis [$v:vis]
+ @debug_format [$debug_format:tt]
+ $(#[doc = $doc:expr])*
+ const $name:ident = $constant:expr,
+ $($tokens:tt)*) => (
+ $(#[doc = $doc])*
+ pub const $name: $type = $type::from_u32_const($constant);
+ $crate::newtype_index!(
+ @derives [$($derives,)*]
+ @attrs [$(#[$attrs])*]
+ @type [$type]
+ @max [$max]
+ @vis [$v]
+ @debug_format [$debug_format]
+ $($tokens)*);
+ );
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct IndexVec<I: Idx, T> {
+ pub raw: Vec<T>,
+ _marker: PhantomData<fn(&I)>
+}
+
+// Whether `IndexVec` is `Send` depends only on the data,
+// not the phantom data.
+unsafe impl<I: Idx, T> Send for IndexVec<I, T> where T: Send {}
+
+impl<I: Idx, T: Encodable> Encodable for IndexVec<I, T> {
+ fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+ Encodable::encode(&self.raw, s)
+ }
+}
+
+impl<I: Idx, T: Decodable> Decodable for IndexVec<I, T> {
+ fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
+ Decodable::decode(d).map(|v| {
+ IndexVec { raw: v, _marker: PhantomData }
+ })
+ }
+}
+
+impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.raw, fmt)
+ }
+}
+
+pub type Enumerated<I, J> = iter::Map<iter::Enumerate<J>, IntoIdx<I>>;
+
+impl<I: Idx, T> IndexVec<I, T> {
+ #[inline]
+ pub fn new() -> Self {
+ IndexVec { raw: Vec::new(), _marker: PhantomData }
+ }
+
+ #[inline]
+ pub fn from_raw(raw: Vec<T>) -> Self {
+ IndexVec { raw, _marker: PhantomData }
+ }
+
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Self {
+ IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }
+ }
+
+ #[inline]
+ pub fn from_elem<S>(elem: T, universe: &IndexVec<I, S>) -> Self
+ where T: Clone
+ {
+ IndexVec { raw: vec![elem; universe.len()], _marker: PhantomData }
+ }
+
+ #[inline]
+ pub fn from_elem_n(elem: T, n: usize) -> Self
+ where T: Clone
+ {
+ IndexVec { raw: vec![elem; n], _marker: PhantomData }
+ }
+
+ #[inline]
+ pub fn push(&mut self, d: T) -> I {
+ let idx = I::new(self.len());
+ self.raw.push(d);
+ idx
+ }
+
+ #[inline]
+ pub fn pop(&mut self) -> Option<T> {
+ self.raw.pop()
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.raw.len()
+ }
+
+ /// Gives the next index that will be assigned when `push` is
+ /// called.
+ #[inline]
+ pub fn next_index(&self) -> I {
+ I::new(self.len())
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.raw.is_empty()
+ }
+
+ #[inline]
+ pub fn into_iter(self) -> vec::IntoIter<T> {
+ self.raw.into_iter()
+ }
+
+ #[inline]
+ pub fn into_iter_enumerated(self) -> Enumerated<I, vec::IntoIter<T>>
+ {
+ self.raw.into_iter().enumerate().map(IntoIdx { _marker: PhantomData })
+ }
+
+ #[inline]
+ pub fn iter(&self) -> slice::Iter<'_, T> {
+ self.raw.iter()
+ }
+
+ #[inline]
+ pub fn iter_enumerated(&self) -> Enumerated<I, slice::Iter<'_, T>>
+ {
+ self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData })
+ }
+
+ #[inline]
+ pub fn indices(&self) -> iter::Map<Range<usize>, IntoIdx<I>> {
+ (0..self.len()).map(IntoIdx { _marker: PhantomData })
+ }
+
+ #[inline]
+ pub fn iter_mut(&mut self) -> slice::IterMut<'_, T> {
+ self.raw.iter_mut()
+ }
+
+ #[inline]
+ pub fn iter_enumerated_mut(&mut self) -> Enumerated<I, slice::IterMut<'_, T>>
+ {
+ self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData })
+ }
+
+ #[inline]
+ pub fn drain<'a, R: RangeBounds<usize>>(
+ &'a mut self, range: R) -> impl Iterator<Item=T> + 'a {
+ self.raw.drain(range)
+ }
+
+ #[inline]
+ pub fn drain_enumerated<'a, R: RangeBounds<usize>>(
+ &'a mut self, range: R) -> impl Iterator<Item=(I, T)> + 'a {
+ self.raw.drain(range).enumerate().map(IntoIdx { _marker: PhantomData })
+ }
+
+ #[inline]
+ pub fn last(&self) -> Option<I> {
+ self.len().checked_sub(1).map(I::new)
+ }
+
+ #[inline]
+ pub fn shrink_to_fit(&mut self) {
+ self.raw.shrink_to_fit()
+ }
+
+ #[inline]
+ pub fn swap(&mut self, a: I, b: I) {
+ self.raw.swap(a.index(), b.index())
+ }
+
+ #[inline]
+ pub fn truncate(&mut self, a: usize) {
+ self.raw.truncate(a)
+ }
+
+ #[inline]
+ pub fn get(&self, index: I) -> Option<&T> {
+ self.raw.get(index.index())
+ }
+
+ #[inline]
+ pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
+ self.raw.get_mut(index.index())
+ }
+
+ /// Returns mutable references to two distinct elements, a and b. Panics if a == b.
+ #[inline]
+ pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) {
+ let (ai, bi) = (a.index(), b.index());
+ assert!(ai != bi);
+
+ if ai < bi {
+ let (c1, c2) = self.raw.split_at_mut(bi);
+ (&mut c1[ai], &mut c2[0])
+ } else {
+ let (c2, c1) = self.pick2_mut(b, a);
+ (c1, c2)
+ }
+ }
+
+ pub fn convert_index_type<Ix: Idx>(self) -> IndexVec<Ix, T> {
+ IndexVec {
+ raw: self.raw,
+ _marker: PhantomData,
+ }
+ }
+}
+
+impl<I: Idx, T: Clone> IndexVec<I, T> {
+ /// Grows the index vector so that it contains an entry for
+ /// `elem`; if that is already true, then has no
+ /// effect. Otherwise, inserts new values as needed by invoking
+ /// `fill_value`.
+ #[inline]
+ pub fn ensure_contains_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
+ let min_new_len = elem.index() + 1;
+ if self.len() < min_new_len {
+ self.raw.resize_with(min_new_len, fill_value);
+ }
+ }
+
+ #[inline]
+ pub fn resize(&mut self, new_len: usize, value: T) {
+ self.raw.resize(new_len, value)
+ }
+
+ #[inline]
+ pub fn resize_to_elem(&mut self, elem: I, fill_value: impl FnMut() -> T) {
+ let min_new_len = elem.index() + 1;
+ self.raw.resize_with(min_new_len, fill_value);
+ }
+}
+
+impl<I: Idx, T: Ord> IndexVec<I, T> {
+ #[inline]
+ pub fn binary_search(&self, value: &T) -> Result<I, I> {
+ match self.raw.binary_search(value) {
+ Ok(i) => Ok(Idx::new(i)),
+ Err(i) => Err(Idx::new(i)),
+ }
+ }
+}
+
+impl<I: Idx, T> Index<I> for IndexVec<I, T> {
+ type Output = T;
+
+ #[inline]
+ fn index(&self, index: I) -> &T {
+ &self.raw[index.index()]
+ }
+}
+
+impl<I: Idx, T> IndexMut<I> for IndexVec<I, T> {
+ #[inline]
+ fn index_mut(&mut self, index: I) -> &mut T {
+ &mut self.raw[index.index()]
+ }
+}
+
+impl<I: Idx, T> Default for IndexVec<I, T> {
+ #[inline]
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
+ #[inline]
+ fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) {
+ self.raw.extend(iter);
+ }
+}
+
+impl<I: Idx, T> FromIterator<T> for IndexVec<I, T> {
+ #[inline]
+ fn from_iter<J>(iter: J) -> Self where J: IntoIterator<Item=T> {
+ IndexVec { raw: FromIterator::from_iter(iter), _marker: PhantomData }
+ }
+}
+
+impl<I: Idx, T> IntoIterator for IndexVec<I, T> {
+ type Item = T;
+ type IntoIter = vec::IntoIter<T>;
+
+ #[inline]
+ fn into_iter(self) -> vec::IntoIter<T> {
+ self.raw.into_iter()
+ }
+
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a IndexVec<I, T> {
+ type Item = &'a T;
+ type IntoIter = slice::Iter<'a, T>;
+
+ #[inline]
+ fn into_iter(self) -> slice::Iter<'a, T> {
+ self.raw.iter()
+ }
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a mut IndexVec<I, T> {
+ type Item = &'a mut T;
+ type IntoIter = slice::IterMut<'a, T>;
+
+ #[inline]
+ fn into_iter(self) -> slice::IterMut<'a, T> {
+ self.raw.iter_mut()
+ }
+}
+
+pub struct IntoIdx<I: Idx> { _marker: PhantomData<fn(&I)> }
+impl<I: Idx, T> FnOnce<((usize, T),)> for IntoIdx<I> {
+ type Output = (I, T);
+
+ extern "rust-call" fn call_once(self, ((n, t),): ((usize, T),)) -> Self::Output {
+ (I::new(n), t)
+ }
+}
+
+impl<I: Idx, T> FnMut<((usize, T),)> for IntoIdx<I> {
+ extern "rust-call" fn call_mut(&mut self, ((n, t),): ((usize, T),)) -> Self::Output {
+ (I::new(n), t)
+ }
+}
+
+impl<I: Idx> FnOnce<(usize,)> for IntoIdx<I> {
+ type Output = I;
+
+ extern "rust-call" fn call_once(self, (n,): (usize,)) -> Self::Output {
+ I::new(n)
+ }
+}
+
+impl<I: Idx> FnMut<(usize,)> for IntoIdx<I> {
+ extern "rust-call" fn call_mut(&mut self, (n,): (usize,)) -> Self::Output {
+ I::new(n)
+ }
+}
&mut krate,
sess.diagnostic(),
&sess.features_untracked(),
+ sess.panic_strategy(),
+ sess.target.target.options.panic_strategy,
+ sess.opts.debugging_opts.panic_abort_tests,
)
});
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx, SizeSkeleton};
use rustc::{lint, util};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use util::nodemap::FxHashSet;
use lint::{LateContext, LintContext, LintArray};
use lint::{LintPass, LateLintPass};
-#![feature(proc_macro_hygiene)]
#![allow(rustc::default_hash_types)]
#![recursion_limit="128"]
rustc_data_structures = { path = "../librustc_data_structures" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_target = { path = "../librustc_target" }
+rustc_index = { path = "../librustc_index" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
stable_deref_trait = "1.0.0"
syntax = { path = "../libsyntax" }
use rustc::hir::map::definitions::DefPathTable;
use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader};
use rustc::mir::interpret::AllocDecodingState;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc::util::nodemap::{FxHashMap, NodeMap};
use rustc_data_structures::sync::{Lrc, RwLock, Lock};
use syntax::parse::parser::emit_unclosed_delims;
use syntax::symbol::Symbol;
use syntax_pos::{Span, FileName};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
macro_rules! provide {
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
use crate::cstore::{self, CrateMetadata, MetadataBlob, NativeLibrary, ForeignModule};
use crate::schema::*;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::sync::{Lrc, ReadGuard};
use rustc::hir::map::{DefKey, DefPath, DefPathData, DefPathHash};
use rustc::hir;
use rustc::hir::GenericParamKind;
use rustc::hir::map::definitions::DefPathTable;
use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc::middle::dependency_format::Linkage;
use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel,
metadata_symbol_name};
use rustc::session::config::SymbolManglingVersion;
use rustc::ty::{self, Ty, ReprOptions};
use rustc_target::spec::{PanicStrategy, TargetTriple};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::svh::Svh;
use syntax::{ast, attr};
rustc = { path = "../librustc" }
rustc_target = { path = "../librustc_target" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
rustc_errors = { path = "../librustc_errors" }
rustc_lexer = { path = "../librustc_lexer" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
use rustc::mir::{self, Location, Body, Local};
use rustc::ty::{RegionVid, TyCtxt};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
-use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_index::bit_set::BitSet;
use std::fmt;
use std::ops::Index;
};
use rustc::ty::{self, Ty};
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc_errors::{Applicability, DiagnosticBuilder};
use syntax_pos::Span;
use syntax::source_map::DesugaringKind;
use rustc::mir::{BasicBlock, Local, Location};
use rustc::ty::RegionVid;
-use rustc_data_structures::bit_set::BitIter;
+use rustc_index::bit_set::BitIter;
use crate::borrow_check::location::LocationIndex;
use rustc::mir::{BasicBlock, Location, Body};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
/// Maps between a MIR Location, which identifies a particular
/// statement within a basic block, to a "rich location", which
statements_before_block: IndexVec<BasicBlock, usize>,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct LocationIndex {
DEBUG_FORMAT = "LocationIndex({})"
}
use rustc::ty::{self, TyCtxt};
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::dominators::Dominators;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use smallvec::SmallVec;
use std::collections::BTreeMap;
Mutability, Place, PlaceRef, PlaceBase, ProjectionElem, Static, StaticKind
};
use rustc::ty::{self, Ty, TyCtxt};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use syntax_pos::Span;
use syntax_pos::symbol::kw;
use rustc::mir::ConstraintCategory;
use rustc::ty::RegionVid;
use rustc_data_structures::graph;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use syntax_pos::DUMMY_SP;
/// The construct graph organizes the constraints by their end-points.
use rustc::mir::ConstraintCategory;
use rustc::ty::RegionVid;
use rustc_data_structures::graph::scc::Sccs;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use std::fmt;
use std::ops::Index;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct OutlivesConstraintIndex {
DEBUG_FORMAT = "OutlivesConstraintIndex({})"
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct ConstraintSccIndex {
DEBUG_FORMAT = "ConstraintSccIndex({})"
}
use polonius_engine::Atom;
use rustc::mir::Local;
use rustc::ty::{RegionVid, TyCtxt};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::error::Error;
use std::fmt::Debug;
use std::fs::{self, File};
use rustc::hir::def_id::DefId;
use rustc::infer::region_constraints::MemberConstraint;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use std::hash::Hash;
use std::ops::Index;
use syntax_pos::Span;
end_index: usize,
}
-newtype_index! {
+rustc_index::newtype_index! {
crate struct NllMemberConstraintIndex {
DEBUG_FORMAT = "MemberConstraintIndex({})"
}
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements,
Local, Location, Body, LocalKind, BasicBlock, Promoted};
use rustc::ty::{self, RegionKind, RegionVid};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_errors::Diagnostic;
use std::fmt::Debug;
use std::env;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::mir::{ConstraintCategory, Location, Body};
use rustc::ty::{self, RegionVid};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_errors::DiagnosticBuilder;
use std::collections::VecDeque;
use syntax::errors::Applicability;
use crate::borrow_check::Upvar;
use rustc::mir::{Local, Body};
use rustc::ty::{RegionVid, TyCtxt};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use syntax::source_map::Span;
use syntax_pos::symbol::Symbol;
use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable};
use rustc::util::common::ErrorReported;
use rustc_data_structures::binary_search_util;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::graph::WithSuccessors;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::vec_graph::VecGraph;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use syntax_pos::Span;
use rustc::mir::{BasicBlock, Location, Body};
use rustc::ty::{self, RegionVid};
-use rustc_data_structures::bit_set::{HybridBitSet, SparseBitMatrix};
+use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::Idx;
+use rustc_index::vec::IndexVec;
use std::fmt::Debug;
use std::rc::Rc;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
/// A single integer representing a `Location` in the MIR control-flow
/// graph. Constructed efficiently from `RegionValueElements`.
pub struct PointIndex { DEBUG_FORMAT = "PointIndex({})" }
}
-newtype_index! {
+rustc_index::newtype_index! {
/// A single integer representing a `ty::Placeholder`.
pub struct PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" }
}
use rustc::mir::{Location, Body, Promoted};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
use rustc::mir::*;
use rustc::ty::Ty;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use syntax_pos::Span;
use super::{Locations, TypeChecker};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{Body, Local, Location};
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::vec_linked_list as vll;
/// A map that cross references each local with the locations where it
next: Option<AppearanceIndex>,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct AppearanceIndex { .. }
}
use rustc::traits::query::type_op::outlives::DropckOutlives;
use rustc::traits::query::type_op::TypeOp;
use rustc::ty::{Ty, TypeFoldable};
-use rustc_data_structures::bit_set::HybridBitSet;
+use rustc_index::bit_set::HybridBitSet;
use rustc_data_structures::fx::FxHashMap;
use std::rc::Rc;
UserTypeAnnotationIndex,
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use rustc::ty::layout::VariantIdx;
use std::rc::Rc;
use std::{fmt, iter, mem};
use rustc::ty::subst::{InternalSubsts, SubstsRef, Subst};
use rustc::ty::{self, ClosureSubsts, GeneratorSubsts, RegionVid, Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use rustc_errors::DiagnosticBuilder;
use std::iter;
use rustc::mir::*;
use rustc::ty::{CanonicalUserTypeAnnotation, Variance};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a place that we can move from etc.
//! See docs in `build/expr/mod.rs`.
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use rustc::middle::region;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc::ty::layout::VariantIdx;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use syntax::ast::Name;
use syntax_pos::Span;
use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
use crate::hair::*;
use crate::hair::pattern::compare_const_vals;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::fx::FxHashMap;
-use rustc::ty::{self, Ty, adjustment::{PointerCast}};
+use rustc::ty::{self, Ty, adjustment::PointerCast};
use rustc::ty::util::IntTypeExt;
use rustc::ty::layout::VariantIdx;
use rustc::mir::*;
use rustc::ty::subst::Subst;
use rustc::util::nodemap::HirIdMap;
use rustc_target::spec::PanicStrategy;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use std::u32;
use rustc_target::spec::abi::Abi;
use syntax::attr::{self, UnwindAttr};
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct ScopeId { .. }
}
//! locations.
use rustc::mir::{BasicBlock, Location};
-use rustc_data_structures::bit_set::{BitIter, BitSet, HybridBitSet};
+use rustc_index::bit_set::{BitIter, BitSet, HybridBitSet};
use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet};
use crate::dataflow::move_paths::{HasMoveData, MovePathIndex};
//! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
//! [#64566]: https://github.com/rust-lang/rust/pull/64566
+use std::borrow::Borrow;
use std::cmp::Ordering;
-use std::ops;
+use std::ffi::OsString;
+use std::path::{Path, PathBuf};
+use std::{fs, io, ops};
+use rustc::hir::def_id::DefId;
use rustc::mir::{self, traversal, BasicBlock, Location};
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc::ty::{self, TyCtxt};
use rustc_data_structures::work_queue::WorkQueue;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
+use syntax::symbol::sym;
use crate::dataflow::BottomValue;
+mod graphviz;
+
/// A specific kind of dataflow analysis.
///
/// To run a dataflow analysis, one must set the initial state of the `START_BLOCK` via
/// and try to keep it short.
const NAME: &'static str;
+ /// How each element of your dataflow state will be displayed during debugging.
+ ///
+ /// By default, this is the `fmt::Debug` representation of `Self::Idx`.
+ fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
+ write!(w, "{:?}", idx)
+ }
+
/// The size of each bitvector allocated for each block.
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
location: Location,
);
- /// Updates the current dataflow state with the effect of evaluating a statement.
+ /// Updates the current dataflow state with the effect of evaluating a terminator.
///
/// Note that the effect of a successful return from a `Call` terminator should **not** be
/// acounted for in this function. That should go in `apply_call_return_effect`. For example,
}
}
+type ResultsRefCursor<'a, 'mir, 'tcx, A> =
+ ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
+
/// Inspect the results of dataflow analysis.
///
/// This cursor has linear performance when visiting statements in a block in order. Visiting
/// statements within a block in reverse order is `O(n^2)`, where `n` is the number of statements
/// in that block.
-pub struct ResultsCursor<'mir, 'tcx, A>
+pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
where
A: Analysis<'tcx>,
{
body: &'mir mir::Body<'tcx>,
- results: Results<'tcx, A>,
+ results: R,
state: BitSet<A::Idx>,
pos: CursorPosition,
is_call_return_effect_applied: bool,
}
-impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
+impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx>,
+ R: Borrow<Results<'tcx, A>>,
{
/// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
- pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
+ pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
ResultsCursor {
body,
pos: CursorPosition::AtBlockStart(mir::START_BLOCK),
is_call_return_effect_applied: false,
- state: results.entry_sets[mir::START_BLOCK].clone(),
+ state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
results,
}
}
+ pub fn analysis(&self) -> &A {
+ &self.results.borrow().analysis
+ }
+
/// Resets the cursor to the start of the given `block`.
pub fn seek_to_block_start(&mut self, block: BasicBlock) {
- self.state.overwrite(&self.results.entry_sets[block]);
+ self.state.overwrite(&self.results.borrow().entry_sets[block]);
self.pos = CursorPosition::AtBlockStart(block);
self.is_call_return_effect_applied = false;
}
} = &term.kind {
if !self.is_call_return_effect_applied {
self.is_call_return_effect_applied = true;
- self.results.analysis.apply_call_return_effect(
+ self.results.borrow().analysis.apply_call_return_effect(
&mut self.state,
target.block,
func,
};
let block_data = &self.body.basic_blocks()[target_block];
- self.results.analysis.apply_partial_block_effect(
+ self.results.borrow().analysis.apply_partial_block_effect(
&mut self.state,
target_block,
block_data,
{
analysis: A,
bits_per_block: usize,
+ tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
+ def_id: DefId,
dead_unwinds: &'a BitSet<BasicBlock>,
entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
}
A: Analysis<'tcx>,
{
pub fn new(
+ tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
+ def_id: DefId,
dead_unwinds: &'a BitSet<BasicBlock>,
analysis: A,
) -> Self {
Engine {
analysis,
bits_per_block,
+ tcx,
body,
+ def_id,
dead_unwinds,
entry_sets,
}
);
}
- Results {
- analysis: self.analysis,
- entry_sets: self.entry_sets,
+ let Engine {
+ tcx,
+ body,
+ def_id,
+ analysis,
+ entry_sets,
+ ..
+ } = self;
+
+ let results = Results { analysis, entry_sets };
+
+ let attrs = tcx.get_attrs(def_id);
+ if let Some(path) = get_dataflow_graphviz_output_path(tcx, attrs, A::NAME) {
+ let result = write_dataflow_graphviz_results(body, def_id, &path, &results);
+ if let Err(e) = result {
+ warn!("Failed to write dataflow results to {}: {}", path.display(), e);
+ }
}
+
+ results
}
fn propagate_bits_into_graph_successors_of(
}
}
}
+
+/// Looks for attributes like `#[rustc_mir(borrowck_graphviz_postflow="./path/to/suffix.dot")]` and
+/// extracts the path with the given analysis name prepended to the suffix.
+///
+/// Returns `None` if no such attribute exists.
+fn get_dataflow_graphviz_output_path(
+ tcx: TyCtxt<'tcx>,
+ attrs: ty::Attributes<'tcx>,
+ analysis: &str,
+) -> Option<PathBuf> {
+ let mut rustc_mir_attrs = attrs
+ .into_iter()
+ .filter(|attr| attr.check_name(sym::rustc_mir))
+ .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
+
+ let borrowck_graphviz_postflow = rustc_mir_attrs
+ .find(|attr| attr.check_name(sym::borrowck_graphviz_postflow))?;
+
+ let path_and_suffix = match borrowck_graphviz_postflow.value_str() {
+ Some(p) => p,
+ None => {
+ tcx.sess.span_err(
+ borrowck_graphviz_postflow.span(),
+ "borrowck_graphviz_postflow requires a path",
+ );
+
+ return None;
+ }
+ };
+
+ // Change "path/suffix.dot" to "path/analysis_name_suffix.dot"
+ let mut ret = PathBuf::from(path_and_suffix.to_string());
+ let suffix = ret.file_name().unwrap();
+
+ let mut file_name: OsString = analysis.into();
+ file_name.push("_");
+ file_name.push(suffix);
+ ret.set_file_name(file_name);
+
+ Some(ret)
+}
+
+fn write_dataflow_graphviz_results<A: Analysis<'tcx>>(
+ body: &mir::Body<'tcx>,
+ def_id: DefId,
+ path: &Path,
+ results: &Results<'tcx, A>
+) -> io::Result<()> {
+ debug!("printing dataflow results for {:?} to {}", def_id, path.display());
+
+ let mut buf = Vec::new();
+ let graphviz = graphviz::Formatter::new(body, def_id, results);
+
+ dot::render(&graphviz, &mut buf)?;
+ fs::write(path, buf)
+}
--- /dev/null
+use std::cell::RefCell;
+use std::io::{self, Write};
+use std::{ops, str};
+
+use rustc::hir::def_id::DefId;
+use rustc::mir::{self, BasicBlock, Body, Location};
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::Idx;
+
+use crate::util::graphviz_safe_def_name;
+use super::{Analysis, Results, ResultsRefCursor};
+
+pub struct Formatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ body: &'a Body<'tcx>,
+ def_id: DefId,
+
+ // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
+ block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
+}
+
+impl<A> Formatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ pub fn new(
+ body: &'a Body<'tcx>,
+ def_id: DefId,
+ results: &'a Results<'tcx, A>,
+ ) -> Self {
+ let block_formatter = BlockFormatter {
+ bg: Background::Light,
+ prev_state: BitSet::new_empty(results.analysis.bits_per_block(body)),
+ results: ResultsRefCursor::new(body, results),
+ };
+
+ Formatter {
+ body,
+ def_id,
+ block_formatter: RefCell::new(block_formatter),
+ }
+ }
+}
+
+/// A pair of a basic block and an index into that basic blocks `successors`.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct CfgEdge {
+ source: BasicBlock,
+ index: usize,
+}
+
+fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
+ body[bb]
+ .terminator()
+ .successors()
+ .enumerate()
+ .map(|(index, _)| CfgEdge { source: bb, index })
+ .collect()
+}
+
+impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ type Node = BasicBlock;
+ type Edge = CfgEdge;
+
+ fn graph_id(&self) -> dot::Id<'_> {
+ let name = graphviz_safe_def_name(self.def_id);
+ dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
+ }
+
+ fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
+ dot::Id::new(format!("bb_{}", n.index())).unwrap()
+ }
+
+ fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
+ let mut label = Vec::new();
+ self.block_formatter
+ .borrow_mut()
+ .write_node_label(&mut label, self.body, *block)
+ .unwrap();
+ dot::LabelText::html(String::from_utf8(label).unwrap())
+ }
+
+ fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
+ Some(dot::LabelText::label("none"))
+ }
+
+ fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
+ let label = &self.body
+ [e.source]
+ .terminator()
+ .kind
+ .fmt_successor_labels()
+ [e.index];
+ dot::LabelText::label(label.clone())
+ }
+}
+
+impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ type Node = BasicBlock;
+ type Edge = CfgEdge;
+
+ fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
+ self.body
+ .basic_blocks()
+ .indices()
+ .collect::<Vec<_>>()
+ .into()
+ }
+
+ fn edges(&self) -> dot::Edges<'_, Self::Edge> {
+ self.body
+ .basic_blocks()
+ .indices()
+ .flat_map(|bb| outgoing_edges(self.body, bb))
+ .collect::<Vec<_>>()
+ .into()
+ }
+
+ fn source(&self, edge: &Self::Edge) -> Self::Node {
+ edge.source
+ }
+
+ fn target(&self, edge: &Self::Edge) -> Self::Node {
+ self.body
+ [edge.source]
+ .terminator()
+ .successors()
+ .nth(edge.index)
+ .copied()
+ .unwrap()
+ }
+}
+
+struct BlockFormatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ prev_state: BitSet<A::Idx>,
+ results: ResultsRefCursor<'a, 'a, 'tcx, A>,
+ bg: Background,
+}
+
+impl<A> BlockFormatter<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ fn toggle_background(&mut self) -> Background {
+ let bg = self.bg;
+ self.bg = !bg;
+ bg
+ }
+
+ fn write_node_label(
+ &mut self,
+ w: &mut impl io::Write,
+ body: &'a Body<'tcx>,
+ block: BasicBlock,
+ ) -> io::Result<()> {
+ // Sample output:
+ // +-+--------------------------------------------------+
+ // A | bb4 |
+ // +-+----------------------------------+---------------+
+ // B | MIR | STATE |
+ // +-+----------------------------------+---------------+
+ // C | | (on entry) | {_0,_2,_3} |
+ // +-+----------------------------------+---------------+
+ // D |0| 0: StorageLive(_7) | |
+ // +-+----------------------------------+---------------+
+ // |1| 1: StorageLive(_8) | |
+ // +-+----------------------------------+---------------+
+ // |2| 2: _8 = &mut _1 | +_8 |
+ // +-+----------------------------------+---------------+
+ // E |T| _7 = const Foo::twiddle(move _8) | -_8 |
+ // +-+----------------------------------+---------------+
+ // F | | (on unwind) | {_0,_2,_3,_7} |
+ // +-+----------------------------------+---------------+
+ // | | (on successful return) | +_7 |
+ // +-+----------------------------------+---------------+
+
+ write!(
+ w,
+ r#"<table border="1" cellborder="1" cellspacing="0" cellpadding="3" sides="rb">"#,
+ )?;
+
+ // A: Block info
+ write!(
+ w,
+ r#"<tr>
+ <td colspan="{num_headers}" sides="tl">bb{block_id}</td>
+ </tr>"#,
+ num_headers = 3,
+ block_id = block.index(),
+ )?;
+
+ // B: Column headings
+ write!(
+ w,
+ r#"<tr>
+ <td colspan="2" {fmt}>MIR</td>
+ <td {fmt}>STATE</td>
+ </tr>"#,
+ fmt = r##"bgcolor="#a0a0a0" sides="tl""##,
+ )?;
+
+ // C: Entry state
+ self.results.seek_to_block_start(block);
+ self.write_row_with_curr_state(w, "", "(on entry)")?;
+ self.prev_state.overwrite(self.results.get());
+
+ // D: Statement transfer functions
+ for (i, statement) in body[block].statements.iter().enumerate() {
+ let location = Location { block, statement_index: i };
+
+ let mir_col = format!("{:?}", statement);
+ let i_col = i.to_string();
+
+ self.results.seek_after(location);
+ self.write_row_with_curr_diff(w, &i_col, &mir_col)?;
+ self.prev_state.overwrite(self.results.get());
+ }
+
+ // E: Terminator transfer function
+ let terminator = body[block].terminator();
+ let location = body.terminator_loc(block);
+
+ let mut mir_col = String::new();
+ terminator.kind.fmt_head(&mut mir_col).unwrap();
+
+ self.results.seek_after(location);
+ self.write_row_with_curr_diff(w, "T", &mir_col)?;
+ self.prev_state.overwrite(self.results.get());
+
+ // F: Exit state
+ if let mir::TerminatorKind::Call { destination: Some(_), .. } = &terminator.kind {
+ self.write_row_with_curr_state(w, "", "(on unwind)")?;
+
+ self.results.seek_after_assume_call_returns(location);
+ self.write_row_with_curr_diff(w, "", "(on successful return)")?;
+ } else {
+ self.write_row_with_curr_state(w, "", "(on exit)")?;
+ }
+
+ write!(w, "</table>")
+ }
+
+ fn write_row_with_curr_state(
+ &mut self,
+ w: &mut impl io::Write,
+ i: &str,
+ mir: &str,
+ ) -> io::Result<()> {
+ let bg = self.toggle_background();
+
+ let mut out = Vec::new();
+ write!(&mut out, "{{")?;
+ pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
+ write!(&mut out, "}}")?;
+
+ write!(
+ w,
+ r#"<tr>
+ <td {fmt} align="right">{i}</td>
+ <td {fmt} align="left">{mir}</td>
+ <td {fmt} align="left">{state}</td>
+ </tr>"#,
+ fmt = &["sides=\"tl\"", bg.attr()].join(" "),
+ i = i,
+ mir = dot::escape_html(mir),
+ state = dot::escape_html(str::from_utf8(&out).unwrap()),
+ )
+ }
+
+ fn write_row_with_curr_diff(
+ &mut self,
+ w: &mut impl io::Write,
+ i: &str,
+ mir: &str,
+ ) -> io::Result<()> {
+ let bg = self.toggle_background();
+ let analysis = self.results.analysis();
+
+ let diff = BitSetDiff::compute(&self.prev_state, self.results.get());
+
+ let mut set = Vec::new();
+ pretty_print_state_elems(&mut set, analysis, diff.set.iter())?;
+
+ let mut clear = Vec::new();
+ pretty_print_state_elems(&mut clear, analysis, diff.clear.iter())?;
+
+ write!(
+ w,
+ r#"<tr>
+ <td {fmt} align="right">{i}</td>
+ <td {fmt} align="left">{mir}</td>
+ <td {fmt} align="left">"#,
+ i = i,
+ fmt = &["sides=\"tl\"", bg.attr()].join(" "),
+ mir = dot::escape_html(mir),
+ )?;
+
+ if !set.is_empty() {
+ write!(
+ w,
+ r#"<font color="darkgreen">+{}</font>"#,
+ dot::escape_html(str::from_utf8(&set).unwrap()),
+ )?;
+ }
+
+ if !set.is_empty() && !clear.is_empty() {
+ write!(w, " ")?;
+ }
+
+ if !clear.is_empty() {
+ write!(
+ w,
+ r#"<font color="red">-{}</font>"#,
+ dot::escape_html(str::from_utf8(&clear).unwrap()),
+ )?;
+ }
+
+ write!(w, "</td></tr>")
+ }
+}
+
+/// The operations required to transform one `BitSet` into another.
+struct BitSetDiff<T: Idx> {
+ set: HybridBitSet<T>,
+ clear: HybridBitSet<T>,
+}
+
+impl<T: Idx> BitSetDiff<T> {
+ fn compute(from: &BitSet<T>, to: &BitSet<T>) -> Self {
+ assert_eq!(from.domain_size(), to.domain_size());
+ let len = from.domain_size();
+
+ let mut set = HybridBitSet::new_empty(len);
+ let mut clear = HybridBitSet::new_empty(len);
+
+ // FIXME: This could be made faster if `BitSet::xor` were implemented.
+ for i in (0..len).map(|i| T::new(i)) {
+ match (from.contains(i), to.contains(i)) {
+ (false, true) => set.insert(i),
+ (true, false) => clear.insert(i),
+ _ => continue,
+ };
+ }
+
+ BitSetDiff {
+ set,
+ clear,
+ }
+ }
+}
+
+/// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
+/// list.
+fn pretty_print_state_elems<A>(
+ w: &mut impl io::Write,
+ analysis: &A,
+ elems: impl Iterator<Item = A::Idx>,
+) -> io::Result<()>
+where
+ A: Analysis<'tcx>,
+{
+ let mut first = true;
+ for idx in elems {
+ if first {
+ first = false;
+ } else {
+ write!(w, ",")?;
+ }
+
+ analysis.pretty_print_idx(w, idx)?;
+ }
+
+ Ok(())
+}
+
+/// The background color used for zebra-striping the table.
+#[derive(Clone, Copy)]
+enum Background {
+ Light,
+ Dark,
+}
+
+impl Background {
+ fn attr(self) -> &'static str {
+ match self {
+ Self::Dark => "bgcolor=\"#f0f0f0\"",
+ Self::Light => "",
+ }
+ }
+}
+
+impl ops::Not for Background {
+ type Output = Self;
+
+ fn not(self) -> Self {
+ match self {
+ Self::Light => Self::Dark,
+ Self::Dark => Self::Light,
+ }
+ }
+}
use rustc::ty::{self, TyCtxt};
use rustc::ty::RegionVid;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use crate::dataflow::{BitDenotation, BottomValue, GenKillSet};
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
use std::rc::Rc;
-newtype_index! {
+rustc_index::newtype_index! {
pub struct BorrowIndex {
DEBUG_FORMAT = "bw{}"
}
use rustc::mir::visit::Visitor;
use rustc::mir::{self, Local, Location};
use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use syntax_pos::DUMMY_SP;
use crate::dataflow::{self, GenKillSet};
use rustc::ty::TyCtxt;
use rustc::mir::{self, Body, Location};
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::Idx;
use super::MoveDataParamEnv;
use syntax::ast::{self, MetaItem};
use syntax::symbol::{Symbol, sym};
-use rustc_data_structures::bit_set::{BitSet, HybridBitSet};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::Idx;
use rustc_data_structures::work_queue::WorkQueue;
use rustc::hir::def_id::DefId;
use rustc::mir::tcx::RvalueInitializationState;
use rustc::mir::*;
use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use smallvec::{smallvec, SmallVec};
use std::collections::hash_map::Entry;
use rustc::mir::*;
use rustc::ty::{Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::{Enumerated, Idx, IndexVec};
+use rustc_index::vec::{Enumerated, Idx, IndexVec};
use smallvec::SmallVec;
use syntax_pos::Span;
mod abs_domain;
-newtype_index! {
+rustc_index::newtype_index! {
pub struct MovePathIndex {
DEBUG_FORMAT = "mp{}"
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct MoveOutIndex {
DEBUG_FORMAT = "mo{}"
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct InitIndex {
DEBUG_FORMAT = "in{}"
}
cell are synchronized.
"##,
+E0493: r##"
+A type with a `Drop` implementation was destructured when trying to initialize
+a static item.
+
+Erroneous code example:
+
+```compile_fail,E0493
+enum DropType {
+ A,
+}
+
+impl Drop for DropType {
+ fn drop(&mut self) {}
+}
+
+struct Foo {
+ field1: DropType,
+}
+
+static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error!
+```
+
+The problem here is that if the given type or one of its fields implements the
+`Drop` trait, this `Drop` implementation cannot be called during the static
+type initialization which might cause a memory leak. To prevent this issue,
+you need to instantiate all the static type's fields by hand.
+
+```
+enum DropType {
+ A,
+}
+
+impl Drop for DropType {
+ fn drop(&mut self) {}
+}
+
+struct Foo {
+ field1: DropType,
+}
+
+static FOO: Foo = Foo { field1: DropType::A }; // We initialize all fields
+ // by hand.
+```
+"##,
+
E0499: r##"
A variable was borrowed as mutable more than once. Erroneous code example:
// E0299, // mismatched types between arms
// E0471, // constant evaluation error (in pattern)
// E0385, // {} in an aliasable location
- E0493, // destructors cannot be evaluated at compile-time
E0521, // borrowed data escapes outside of closure
E0526, // shuffle indices are not constant
E0594, // cannot assign to {}
use rustc::hir;
use rustc::ty;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
impl<'tcx> Mirror<'tcx> for &'tcx hir::Block {
type Output = Block<'tcx>;
use crate::hair::cx::block;
use crate::hair::cx::to_ref::ToRef;
use crate::hair::util::UserAnnotatedTyHelpers;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc::hir::def::{CtorOf, Res, DefKind, CtorKind};
use rustc::mir::interpret::{GlobalId, ErrorHandled, ConstValue};
use rustc::ty::{self, AdtKind, Ty};
use crate::hair::*;
use crate::hair::util::UserAnnotatedTyHelpers;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc::hir::def_id::DefId;
use rustc::hir::Node;
use rustc::middle::region;
use self::WitnessPreference::*;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use super::{FieldPat, Pat, PatKind, PatRange};
use super::{PatternFoldable, PatternFolder, compare_const_vals};
use rustc::lint;
use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::mir::{UserTypeProjection};
-use rustc::mir::interpret::{GlobalId, ConstValue, sign_extend, AllocId, Pointer};
+use rustc::mir::interpret::{GlobalId, ConstValue, get_slice_bytes, sign_extend};
use rustc::traits::{ObligationCause, PredicateObligation};
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, UserType, DefIdTree};
use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations};
use rustc::ty::subst::{SubstsRef, GenericArg};
-use rustc::ty::layout::{VariantIdx, Size};
+use rustc::ty::layout::VariantIdx;
use rustc::hir::{self, RangeEnd};
use rustc::hir::def::{CtorOf, Res, DefKind, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc::hir::ptr::P;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc_data_structures::fx::FxHashSet;
use std::cmp::Ordering;
if let ty::Str = ty.kind {
match (a.val, b.val) {
- (
- ConstValue::Slice { data: alloc_a, start: offset_a, end: end_a },
- ConstValue::Slice { data: alloc_b, start: offset_b, end: end_b },
- ) => {
- let len_a = end_a - offset_a;
- let len_b = end_b - offset_b;
- let a = alloc_a.get_bytes(
- &tcx,
- // invent a pointer, only the offset is relevant anyway
- Pointer::new(AllocId(0), Size::from_bytes(offset_a as u64)),
- Size::from_bytes(len_a as u64),
- );
- let b = alloc_b.get_bytes(
- &tcx,
- // invent a pointer, only the offset is relevant anyway
- Pointer::new(AllocId(0), Size::from_bytes(offset_b as u64)),
- Size::from_bytes(len_b as u64),
- );
- if let (Ok(a), Ok(b)) = (a, b) {
- return from_bool(a == b);
- }
+ (ConstValue::Slice { .. }, ConstValue::Slice { .. }) => {
+ let a_bytes = get_slice_bytes(&tcx, a.val);
+ let b_bytes = get_slice_bytes(&tcx, b.val);
+ return from_bool(a_bytes == b_bytes);
}
_ => (),
}
use rustc::ty::subst::SubstsRef;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::query::TyCtxtAt;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc::mir::interpret::{
GlobalId, Scalar, Pointer, FrameInfo, AllocId,
InterpResult, truncate, sign_extend,
use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::{Align, Size};
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use syntax::ast::Mutability;
use syntax::source_map::Span;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::FnKind;
use rustc::hir::map::blocks::FnLikeNode;
use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap};
use rustc::util::common::time;
-use rustc_data_structures::bit_set::GrowableBitSet;
+use rustc_index::bit_set::GrowableBitSet;
use rustc_data_structures::sync::{MTRef, MTLock, ParallelIterator, par_iter};
use std::iter;
use rustc::ty::subst::{Subst, InternalSubsts};
use rustc::ty::query::Providers;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use rustc_target::spec::abi::Abi;
use syntax_pos::{Span, sym};
use rustc::ty::TyCtxt;
use rustc::mir::*;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use crate::transform::{MirPass, MirSource};
#[derive(PartialEq)]
use rustc::mir::*;
use rustc::mir::interpret::ConstValue;
use rustc::ty::{self, Ty};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use syntax_pos::DUMMY_SP;
use super::Item as ConstCx;
pub trait Qualif {
const IDX: usize;
+ /// The name of the file used to debug the dataflow analysis that computes this qualif.
+ const ANALYSIS_NAME: &'static str;
+
/// Whether this `Qualif` is cleared when a local is moved from.
const IS_CLEARED_ON_MOVE: bool = false;
impl Qualif for HasMutInterior {
const IDX: usize = 0;
+ const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
impl Qualif for NeedsDrop {
const IDX: usize = 1;
+ const ANALYSIS_NAME: &'static str = "flow_needs_drop";
const IS_CLEARED_ON_MOVE: bool = true;
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
use rustc::mir::visit::Visitor;
use rustc::mir::{self, BasicBlock, Local, Location};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use std::cell::RefCell;
use std::marker::PhantomData;
_qualif: PhantomData,
};
let results =
- dataflow::Engine::new(item.body, dead_unwinds, analysis).iterate_to_fixpoint();
+ dataflow::Engine::new(item.tcx, item.body, item.def_id, dead_unwinds, analysis)
+ .iterate_to_fixpoint();
let cursor = dataflow::ResultsCursor::new(item.body, results);
let mut qualifs_in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
{
type Idx = Local;
- const NAME: &'static str = "flow_sensitive_qualif";
+ const NAME: &'static str = Q::ANALYSIS_NAME;
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
body.local_decls.len()
use rustc::mir::*;
use rustc::ty::cast::CastTy;
use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use rustc_target::spec::abi::Abi;
use syntax::symbol::sym;
use syntax_pos::Span;
self.qualifs.needs_drop.visit_statement(statement, location);
self.qualifs.has_mut_interior.visit_statement(statement, location);
- debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
- debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
match statement.kind {
StatementKind::Assign(..) => {
self.qualifs.needs_drop.visit_terminator(terminator, location);
self.qualifs.has_mut_interior.visit_terminator(terminator, location);
- debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
- debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
self.super_terminator(terminator, location);
}
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::sync::Lrc;
use rustc::ty::query::Providers;
use syntax_pos::{Span, DUMMY_SP};
use rustc::ty::subst::InternalSubsts;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc::ty::layout::{
LayoutOf, TyLayout, LayoutError, HasTyCtxt, TargetDataLayout, HasDataLayout,
};
use rustc::hir;
use rustc::mir::*;
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use std::fmt;
use syntax_pos::Span;
use rustc::ty::layout::VariantIdx;
use rustc::ty::subst::SubstsRef;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
-use rustc_data_structures::bit_set::{BitSet, BitMatrix};
+use rustc_index::vec::{Idx, IndexVec};
+use rustc_index::bit_set::{BitSet, BitMatrix};
use std::borrow::Cow;
use std::iter;
use std::mem;
use rustc::hir::CodegenFnAttrFlags;
use rustc::hir::def_id::DefId;
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
use rustc::mir::*;
use rustc::mir::visit::*;
use rustc::mir::visit::{MutVisitor, Visitor};
use rustc::ty::{self, TyCtxt};
use rustc::util::nodemap::{FxHashMap, FxHashSet};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::mem;
use crate::transform::{MirPass, MirSource};
use crate::{build, shim};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::mir::{Body, MirPhase, Promoted};
use rustc::ty::{TyCtxt, InstanceDef};
use rustc::ty::TyCtxt;
use syntax_pos::Span;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use std::{iter, mem, usize};
//! The Qualif flags below can be used to also provide better
//! diagnostics as to why a constant rvalue wasn't promoted.
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use rustc::hir;
use rustc::ty::TyCtxt;
use rustc::mir::*;
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
use rustc::ty::{self, TyCtxt};
use rustc::hir::def_id::DefId;
use rustc::mir::{self, Body, Location};
-use rustc_data_structures::bit_set::BitSet;
+use rustc_index::bit_set::BitSet;
use crate::transform::{MirPass, MirSource};
use crate::dataflow::{do_dataflow, DebugFormatted};
//! naively generate still contains the `_a = ()` write in the unreachable block "after" the
//! return.
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext};
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::{Visitor, PlaceContext, NonUseContext};
-use rustc_data_structures::indexed_vec::{IndexVec};
+use rustc_index::vec::{IndexVec};
use crate::transform::{MirPass, MirSource};
use crate::util::patch::MirPatch;
use rustc::mir::*;
use rustc::ty::Ty;
use rustc::ty::layout::VariantIdx;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::iter::TrustedLen;
use rustc::mir::{Local, Location, Body};
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_index::vec::IndexVec;
use std::mem;
pub struct DefUseAnalysis {
use rustc::ty::layout::VariantIdx;
use rustc::ty::subst::SubstsRef;
use rustc::ty::util::IntTypeExt;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use crate::util::patch::MirPatch;
use std::convert::TryInto;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::ty::TyCtxt;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::fmt::Debug;
use std::io::{self, Write};
use rustc::mir::Local;
use rustc::mir::*;
use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::bit_set::BitSet;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::work_queue::WorkQueue;
use std::fs;
use std::io::{self, Write};
use rustc::ty::Ty;
use rustc::mir::*;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use syntax_pos::Span;
/// This struct represents a patch to MIR, which can add
use rustc::mir::visit::Visitor;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use std::fmt::Display;
use std::fmt::Write as _;
use std::fs;
_ => false,
};
- let mut bad_struct_syntax_suggestion = || {
+ let mut bad_struct_syntax_suggestion = |def_id: DefId| {
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
let mut suggested = false;
match source {
_ => {}
}
if !suggested {
+ if let Some(span) = self.r.definitions.opt_span(def_id) {
+ err.span_label(span, &format!("`{}` defined here", path_str));
+ }
err.span_label(
span,
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
);
}
} else {
- bad_struct_syntax_suggestion();
+ bad_struct_syntax_suggestion(def_id);
}
}
- (Res::Def(DefKind::Union, _), _) |
- (Res::Def(DefKind::Variant, _), _) |
- (Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _), _) if ns == ValueNS => {
- bad_struct_syntax_suggestion();
+ (Res::Def(DefKind::Union, def_id), _) |
+ (Res::Def(DefKind::Variant, def_id), _) |
+ (Res::Def(DefKind::Ctor(_, CtorKind::Fictive), def_id), _) if ns == ValueNS => {
+ bad_struct_syntax_suggestion(def_id);
}
- (Res::Def(DefKind::Ctor(_, CtorKind::Fn), _), _) if ns == ValueNS => {
+ (Res::Def(DefKind::Ctor(_, CtorKind::Fn), def_id), _) if ns == ValueNS => {
+ if let Some(span) = self.r.definitions.opt_span(def_id) {
+ err.span_label(span, &format!("`{}` defined here", path_str));
+ }
err.span_label(
span,
- format!("did you mean `{} ( /* fields */ )`?", path_str),
+ format!("did you mean `{}( /* fields */ )`?", path_str),
);
}
(Res::SelfTy(..), _) if ns == ValueNS => {
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
syntax_pos = { path = "../libsyntax_pos" }
+rustc_index = { path = "../librustc_index" }
use std::fmt;
use std::ops::{Add, Deref, Sub, Mul, AddAssign, Range, RangeInclusive};
-use rustc_data_structures::newtype_index;
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_index::vec::{Idx, IndexVec};
use syntax_pos::symbol::{sym, Symbol};
use syntax_pos::Span;
}
}
-newtype_index! {
+rustc_index::newtype_index! {
pub struct VariantIdx { .. }
}
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
+rustc_index = { path = "../librustc_index" }
// 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
//
// FIXME(60707): Consider removing hack with principled solution.
- self.check_expr_has_type_or_error(discrim, self.tcx.types.bool)
+ self.check_expr_has_type_or_error(discrim, self.tcx.types.bool, |_| {})
} else {
self.demand_discriminant_type(arms, discrim)
};
if let Some(g) = &arm.guard {
self.diverges.set(pats_diverge);
match g {
- hir::Guard::If(e) => self.check_expr_has_type_or_error(e, tcx.types.bool),
+ hir::Guard::If(e) => {
+ self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {})
+ }
};
}
kind: TypeVariableOriginKind::TypeInference,
span: discrim.span,
});
- self.check_expr_has_type_or_error(discrim, discrim_ty);
+ self.check_expr_has_type_or_error(discrim, discrim_ty, |_| {});
discrim_ty
}
}
fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No)
} else {
match self.expressions {
- Expressions::Dynamic(ref exprs) =>
- fcx.try_find_coercion_lub(cause,
- exprs,
- self.merged_ty(),
- expression,
- expression_ty),
- Expressions::UpFront(ref coercion_sites) =>
- fcx.try_find_coercion_lub(cause,
- &coercion_sites[0..self.pushed],
- self.merged_ty(),
- expression,
- expression_ty),
+ Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub(
+ cause,
+ exprs,
+ self.merged_ty(),
+ expression,
+ expression_ty,
+ ),
+ Expressions::UpFront(ref coercion_sites) => fcx.try_find_coercion_lub(
+ cause,
+ &coercion_sites[0..self.pushed],
+ self.merged_ty(),
+ expression,
+ expression_ty,
+ ),
}
}
} else {
self.pushed += 1;
}
}
- Err(err) => {
+ Err(coercion_error) => {
let (expected, found) = if label_expression_as_expected {
// In the case where this is a "forced unit", like
// `break`, we want to call the `()` "expected"
(self.final_ty.unwrap_or(self.expected_ty), expression_ty)
};
- let mut db;
+ let mut err;
match cause.code {
ObligationCauseCode::ReturnNoExpression => {
- db = struct_span_err!(
+ err = struct_span_err!(
fcx.tcx.sess, cause.span, E0069,
"`return;` in a function whose return type is not `()`");
- db.span_label(cause.span, "return type is not `()`");
+ err.span_label(cause.span, "return type is not `()`");
}
ObligationCauseCode::BlockTailExpression(blk_id) => {
let parent_id = fcx.tcx.hir().get_parent_node(blk_id);
- db = self.report_return_mismatched_types(
+ err = self.report_return_mismatched_types(
cause,
expected,
found,
- err,
+ coercion_error,
fcx,
parent_id,
expression.map(|expr| (expr, blk_id)),
);
}
ObligationCauseCode::ReturnValue(id) => {
- db = self.report_return_mismatched_types(
- cause, expected, found, err, fcx, id, None);
+ err = self.report_return_mismatched_types(
+ cause, expected, found, coercion_error, fcx, id, None);
}
_ => {
- db = fcx.report_mismatched_types(cause, expected, found, err);
+ err = fcx.report_mismatched_types(cause, expected, found, coercion_error);
}
}
if let Some(augment_error) = augment_error {
- augment_error(&mut db);
+ augment_error(&mut err);
}
// Error possibly reported in `check_assign` so avoid emitting error again.
- db.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected)).is_some());
+ err.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected))
+ .is_some());
self.final_ty = Some(fcx.tcx.types.err);
}
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
- err: TypeError<'tcx>,
+ ty_err: TypeError<'tcx>,
fcx: &FnCtxt<'a, 'tcx>,
id: hir::HirId,
expression: Option<(&'tcx hir::Expr, hir::HirId)>,
) -> DiagnosticBuilder<'a> {
- let mut db = fcx.report_mismatched_types(cause, expected, found, err);
+ let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
let mut pointing_at_return_type = false;
let mut return_sp = None;
let parent_id = fcx.tcx.hir().get_parent_node(id);
let fn_decl = if let Some((expr, blk_id)) = expression {
pointing_at_return_type = fcx.suggest_mismatched_types_on_tail(
- &mut db,
+ &mut err,
expr,
expected,
found,
blk_id,
);
let parent = fcx.tcx.hir().get(parent_id);
+ if let (Some(match_expr), true, false) = (
+ fcx.tcx.hir().get_match_if_cause(expr.hir_id),
+ expected.is_unit(),
+ pointing_at_return_type,
+ ) {
+ if match_expr.span.desugaring_kind().is_none() {
+ err.span_label(match_expr.span, "expected this to be `()`");
+ fcx.suggest_semicolon_at_end(match_expr.span, &mut err);
+ }
+ }
fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
} else {
fcx.get_fn_decl(parent_id)
if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) {
if expression.is_none() {
pointing_at_return_type |= fcx.suggest_missing_return_type(
- &mut db, &fn_decl, expected, found, can_suggest);
+ &mut err, &fn_decl, expected, found, can_suggest);
}
if !pointing_at_return_type {
return_sp = Some(fn_decl.output.span()); // `impl Trait` return type
}
}
if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) {
- db.span_label(return_sp, "expected because this return type...");
- db.span_label( *sp, format!(
+ err.span_label(return_sp, "expected because this return type...");
+ err.span_label( *sp, format!(
"...is found to be `{}` here",
fcx.resolve_type_vars_with_obligations(expected),
));
}
- db
+ err
}
pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> {
&self,
expr: &'tcx hir::Expr,
expected: Ty<'tcx>,
+ extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
) -> Ty<'tcx> {
- self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected))
+ self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
}
fn check_expr_meets_expectation_or_error(
&self,
expr: &'tcx hir::Expr,
expected: Expectation<'tcx>,
+ extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
) -> Ty<'tcx> {
let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
let mut ty = self.check_expr_with_expectation(expr, expected);
ExprKind::DropTemps(expr) => expr,
_ => expr,
};
+ extend_err(&mut err);
// Error possibly reported in `check_assign` so avoid emitting error again.
err.emit_unless(self.is_assign_to_bool(expr, expected_ty));
}
kind: TypeVariableOriginKind::MiscVariable,
span: element.span,
});
- let element_ty = self.check_expr_has_type_or_error(&element, ty);
+ let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {});
(element_ty, ty)
}
};
// the fields with the base_expr. This could cause us to hit errors later
// when certain fields are assumed to exist that in fact do not.
if !error_happened {
- self.check_expr_has_type_or_error(base_expr, adt_ty);
+ self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {});
match adt_ty.kind {
ty::Adt(adt, substs) if adt.is_struct() => {
let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
use crate::namespace::Namespace;
use rustc::infer::{self, InferCtxt, InferOk, InferResult};
use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
use rustc_target::spec::abi::Abi;
use rustc::infer::opaque_types::OpaqueTypeDecl;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
}
}
+ fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
+ err.span_suggestion_short(
+ span.shrink_to_hi(),
+ "consider using a semicolon here",
+ ";".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt) {
// Don't do all the complex logic below for `DeclItem`.
match stmt.kind {
hir::StmtKind::Item(_) => {}
hir::StmtKind::Expr(ref expr) => {
// Check with expected type of `()`.
- self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit());
+
+ self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
+ self.suggest_semicolon_at_end(expr.span, err);
+ });
}
hir::StmtKind::Semi(ref expr) => {
self.check_expr(&expr);
```
"##,
+E0533: r##"
+An item which isn't a unit struct, a variant, nor a constant has been used as a
+match pattern.
+
+Erroneous code example:
+
+```compile_fail,E0533
+struct Tortoise;
+
+impl Tortoise {
+ fn turtle(&self) -> u32 { 0 }
+}
+
+match 0u32 {
+ Tortoise::turtle => {} // Error!
+ _ => {}
+}
+if let Tortoise::turtle = 0u32 {} // Same error!
+```
+
+If you want to match against a value returned by a method, you need to bind the
+value first:
+
+```
+struct Tortoise;
+
+impl Tortoise {
+ fn turtle(&self) -> u32 { 0 }
+}
+
+match 0u32 {
+ x if x == Tortoise.turtle() => {} // Bound into `x` then we compare it!
+ _ => {}
+}
+```
+"##,
+
E0534: r##"
The `inline` attribute was malformed.
E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
// between structures with the same definition
// E0558, // replaced with a generic attribute input check
- E0533, // `{}` does not name a unit variant, unit struct or a constant
// E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
E0564, // only named lifetimes are allowed in `impl Trait`,
// but `{}` was found in the type `{}`
mod auto_trait;
mod blanket_impl;
-use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_index::vec::{IndexVec, Idx};
use rustc_target::spec::abi::Abi;
use rustc_typeck::hir_ty_to_ty;
use rustc::infer::region_constraints::{RegionConstraintData, Constraint};
let write = |p, c| { cx.shared.fs.write(p, c) };
if (*cx.shared).layout.logo.is_empty() {
- write(cx.path("rust-log.png"), static_files::RUST_LOGO)?;
+ write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?;
}
if (*cx.shared).layout.favicon.is_empty() {
write(cx.path("favicon.ico"), static_files::RUST_FAVICON)?;
extern crate env_logger;
extern crate rustc;
extern crate rustc_data_structures;
+extern crate rustc_index;
extern crate rustc_driver;
extern crate rustc_resolve;
extern crate rustc_lint;
syntax_pos = { path = "../libsyntax_pos" }
errors = { path = "../librustc_errors", package = "rustc_errors" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
rustc_lexer = { path = "../librustc_lexer" }
rustc_target = { path = "../librustc_target" }
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
use crate::tokenstream::TokenStream;
use crate::ThinVec;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_index::vec::Idx;
#[cfg(target_arch = "x86_64")]
use rustc_data_structures::static_assert_size;
use rustc_target::spec::abi::Abi;
// hack to ensure that we don't try to access the private parts of `NodeId` in this module
mod node_id_inner {
- use rustc_data_structures::indexed_vec::Idx;
- use rustc_data_structures::newtype_index;
- newtype_index! {
+ use rustc_index::vec::Idx;
+ rustc_index::newtype_index! {
pub struct NodeId {
ENCODABLE = custom
DEBUG_FORMAT = "NodeId({})"
}
}
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
+pub struct AttrItem {
+ pub path: Path,
+ pub tokens: TokenStream,
+}
+
/// Metadata associated with an item.
/// Doc-comments are promoted to attributes that have `is_sugared_doc = true`.
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Attribute {
+ pub item: AttrItem,
pub id: AttrId,
pub style: AttrStyle,
- pub path: Path,
- pub tokens: TokenStream,
pub is_sugared_doc: bool,
pub span: Span,
}
+// Compatibility impl to avoid churn, consider removing.
+impl std::ops::Deref for Attribute {
+ type Target = AttrItem;
+ fn deref(&self) -> &Self::Target { &self.item }
+}
+
/// `TraitRef`s appear in impls.
///
/// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all
pub use crate::ast::Attribute;
use crate::ast;
-use crate::ast::{AttrId, AttrStyle, Name, Ident, Path, PathSegment};
+use crate::ast::{AttrItem, AttrId, AttrStyle, Name, Ident, Path, PathSegment};
use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem};
use crate::ast::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam};
use crate::mut_visit::visit_clobber;
}
}
-impl Attribute {
- /// Extracts the `MetaItem` from inside this `Attribute`.
- pub fn meta(&self) -> Option<MetaItem> {
+impl AttrItem {
+ crate fn meta(&self, span: Span) -> Option<MetaItem> {
let mut tokens = self.tokens.trees().peekable();
Some(MetaItem {
path: self.path.clone(),
} else {
return None;
},
- span: self.span,
+ span,
})
}
+}
+
+impl Attribute {
+ /// Extracts the MetaItem from inside this Attribute.
+ pub fn meta(&self) -> Option<MetaItem> {
+ self.item.meta(self.span)
+ }
pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T>
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
DUMMY_SP,
);
f(&Attribute {
+ item: AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
id: self.id,
style: self.style,
- path: meta.path,
- tokens: meta.kind.tokens(meta.span),
is_sugared_doc: true,
span: self.span,
})
pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
Attribute {
+ item: AttrItem { path, tokens },
id: mk_attr_id(),
style,
- path,
- tokens,
is_sugared_doc: false,
span,
}
let lit_kind = LitKind::Str(text, ast::StrStyle::Cooked);
let lit = Lit::from_lit_kind(lit_kind, span);
Attribute {
+ item: AttrItem {
+ path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)),
+ tokens: MetaItemKind::NameValue(lit).tokens(span),
+ },
id: mk_attr_id(),
style,
- path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)),
- tokens: MetaItemKind::NameValue(lit).tokens(span),
is_sugared_doc: true,
span,
}
}
Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident),
- token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()),
+ token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
token::Nonterminal::NtPath(ref path) => path.clone(),
_ => return None,
},
while !parser.check(&token::CloseDelim(token::Paren)) {
let lo = parser.token.span.lo();
- let (path, tokens) = parser.parse_meta_item_unrestricted()?;
- expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
+ let item = parser.parse_attr_item()?;
+ expanded_attrs.push((item, parser.prev_span.with_lo(lo)));
parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
}
// `cfg_attr` inside of another `cfg_attr`. E.g.
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
expanded_attrs.into_iter()
- .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
+ .flat_map(|(item, span)| self.process_cfg_attr(ast::Attribute {
+ item,
id: attr::mk_attr_id(),
style: attr.style,
- path,
- tokens,
is_sugared_doc: false,
span,
}))
```
"##,
+E0550: r##"
+More than one `deprecated` attribute has been put on an item.
+
+Erroneous code example:
+
+```compile_fail,E0550
+#[deprecated(note = "because why not?")]
+#[deprecated(note = "right?")] // error!
+fn the_banished() {}
+```
+
+The `deprecated` attribute can only be present **once** on an item.
+
+```
+#[deprecated(note = "because why not, right?")]
+fn the_banished() {} // ok!
+```
+"##,
+
E0552: r##"
A unrecognized representation attribute was used.
// rustc_deprecated attribute must be paired with either stable or unstable
// attribute
E0549,
- E0550, // multiple deprecated attributes
E0551, // incorrect meta item
E0553, // multiple rustc_const_unstable attributes
// E0555, // replaced with a generic attribute input check
-use crate::ast::{self, Block, Ident, LitKind, NodeId, PatKind, Path};
+use crate::ast::{self, AttrItem, Block, Ident, LitKind, NodeId, PatKind, Path};
use crate::ast::{MacStmtStyle, StmtKind, ItemKind};
use crate::attr::{self, HasAttrs};
use crate::source_map::respan;
}
fn expand_invoc(&mut self, invoc: Invocation, ext: &SyntaxExtensionKind) -> AstFragment {
- let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
- if fragment_kind == AstFragmentKind::ForeignItems && !self.cx.ecfg.macros_in_extern() {
- if let SyntaxExtensionKind::NonMacroAttr { .. } = ext {} else {
- emit_feature_err(&self.cx.parse_sess, sym::macros_in_extern,
- span, GateIssue::Language,
- "macro invocations in `extern {}` blocks are experimental");
- }
- }
-
if self.cx.current_expansion.depth > self.cx.ecfg.recursion_limit {
let expn_data = self.cx.current_expansion.id.expn_data();
let suggested_limit = self.cx.ecfg.recursion_limit * 2;
FatalError.raise();
}
+ let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
match invoc.kind {
InvocationKind::Bang { mac, .. } => match ext {
SyntaxExtensionKind::Bang(expander) => {
| Annotatable::Variant(..)
=> panic!("unexpected annotatable"),
})), DUMMY_SP).into();
- let input = self.extract_proc_macro_attr_input(attr.tokens, span);
+ let input = self.extract_proc_macro_attr_input(attr.item.tokens, span);
let tok_result = expander.expand(self.cx, span, input, item_tok);
- let res = self.parse_ast_fragment(tok_result, fragment_kind, &attr.path, span);
+ let res =
+ self.parse_ast_fragment(tok_result, fragment_kind, &attr.item.path, span);
self.gate_proc_macro_expansion(span, &res);
res
}
fn gate_proc_macro_expansion_kind(&self, span: Span, kind: AstFragmentKind) {
let kind = match kind {
- AstFragmentKind::Expr => "expressions",
+ AstFragmentKind::Expr |
AstFragmentKind::OptExpr => "expressions",
AstFragmentKind::Pat => "patterns",
- AstFragmentKind::Ty => "types",
AstFragmentKind::Stmts => "statements",
- AstFragmentKind::Items => return,
- AstFragmentKind::TraitItems => return,
- AstFragmentKind::ImplItems => return,
+ AstFragmentKind::Ty |
+ AstFragmentKind::Items |
+ AstFragmentKind::TraitItems |
+ AstFragmentKind::ImplItems |
AstFragmentKind::ForeignItems => return,
AstFragmentKind::Arms
| AstFragmentKind::Fields
let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items);
*at = attr::Attribute {
+ item: AttrItem { path: meta.path, tokens: meta.kind.tokens(meta.span) },
span: at.span,
id: at.id,
style: at.style,
- path: meta.path,
- tokens: meta.kind.tokens(meta.span),
is_sugared_doc: false,
};
} else {
}
}
- fn macros_in_extern(&self) -> bool {
- self.features.map_or(false, |features| features.macros_in_extern)
- }
fn proc_macro_hygiene(&self) -> bool {
self.features.map_or(false, |features| features.proc_macro_hygiene)
}
FatalError.raise()
}
sym::path => token::NtPath(panictry!(p.parse_path(PathStyle::Type))),
- sym::meta => token::NtMeta(panictry!(p.parse_meta_item())),
+ sym::meta => token::NtMeta(panictry!(p.parse_attr_item())),
sym::vis => token::NtVis(panictry!(p.parse_visibility(true))),
sym::lifetime => if p.check_lifetime() {
token::NtLifetime(p.expect_lifetime().ident)
(accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287), None),
/// Allows attributes in formal function parameters.
(accepted, param_attrs, "1.39.0", Some(60406), None),
+ // Allows macro invocations in `extern {}` blocks.
+ (accepted, macros_in_extern, "1.40.0", Some(49476), None),
// -------------------------------------------------------------------------
// feature-group-end: accepted features
/// Allows infering `'static` outlives requirements (RFC 2093).
(active, infer_static_outlives_requirements, "1.26.0", Some(54185), None),
- /// Allows macro invocations in `extern {}` blocks.
- (active, macros_in_extern, "1.27.0", Some(49476), None),
-
/// Allows accessing fields of unions inside `const` functions.
(active, const_fn_union, "1.27.0", Some(51909), None),
pub use errors;
use rustc_data_structures::sync::Lock;
-use rustc_data_structures::bit_set::GrowableBitSet;
+use rustc_index::bit_set::GrowableBitSet;
pub use rustc_data_structures::thin_vec::ThinVec;
use ast::AttrId;
use syntax_pos::edition::Edition;
}
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
- let Attribute { id: _, style: _, path, tokens, is_sugared_doc: _, span } = attr;
+ let Attribute { item: AttrItem { path, tokens }, id: _, style: _, is_sugared_doc: _, span }
+ = attr;
vis.visit_path(path);
vis.visit_tts(tokens);
vis.visit_span(span);
token::NtIdent(ident, _is_raw) => vis.visit_ident(ident),
token::NtLifetime(ident) => vis.visit_ident(ident),
token::NtLiteral(expr) => vis.visit_expr(expr),
- token::NtMeta(meta) => vis.visit_meta_item(meta),
+ token::NtMeta(AttrItem { path, tokens }) => {
+ vis.visit_path(path);
+ vis.visit_tts(tokens);
+ }
token::NtPath(path) => vis.visit_path(path),
token::NtTT(tt) => vis.visit_tt(tt),
token::NtImplItem(item) =>
debug!("parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}",
inner_parse_policy,
self.token);
- let (span, path, tokens, style) = match self.token.kind {
+ let (span, item, style) = match self.token.kind {
token::Pound => {
let lo = self.token.span;
self.bump();
};
self.expect(&token::OpenDelim(token::Bracket))?;
- let (path, tokens) = self.parse_meta_item_unrestricted()?;
+ let item = self.parse_attr_item()?;
self.expect(&token::CloseDelim(token::Bracket))?;
let hi = self.prev_span;
}
}
- (attr_sp, path, tokens, style)
+ (attr_sp, item, style)
}
_ => {
let token_str = self.this_token_to_string();
};
Ok(ast::Attribute {
+ item,
id: attr::mk_attr_id(),
style,
- path,
- tokens,
is_sugared_doc: false,
span,
})
/// PATH `[` TOKEN_STREAM `]`
/// PATH `{` TOKEN_STREAM `}`
/// PATH
- /// PATH `=` TOKEN_TREE
+ /// PATH `=` UNSUFFIXED_LIT
/// The delimiters or `=` are still put into the resulting token stream.
- pub fn parse_meta_item_unrestricted(&mut self) -> PResult<'a, (ast::Path, TokenStream)> {
- let meta = match self.token.kind {
+ pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> {
+ let item = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
- Nonterminal::NtMeta(ref meta) => Some(meta.clone()),
+ Nonterminal::NtMeta(ref item) => Some(item.clone()),
_ => None,
},
_ => None,
};
- Ok(if let Some(meta) = meta {
+ Ok(if let Some(item) = item {
self.bump();
- (meta.path, meta.kind.tokens(meta.span))
+ item
} else {
let path = self.parse_path(PathStyle::Mod)?;
let tokens = if self.check(&token::OpenDelim(DelimToken::Paren)) ||
} else {
TokenStream::empty()
};
- (path, tokens)
+ ast::AttrItem { path, tokens }
})
}
/// Matches the following grammar (per RFC 1559).
///
- /// meta_item : IDENT ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
+ /// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
/// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
let nt_meta = match self.token.kind {
_ => None,
};
- if let Some(meta) = nt_meta {
- self.bump();
- return Ok(meta);
+ if let Some(item) = nt_meta {
+ return match item.meta(item.path.span) {
+ Some(meta) => {
+ self.bump();
+ Ok(meta)
+ }
+ None => self.unexpected(),
+ }
}
let lo = self.token.span;
/// This version of parse param doesn't necessarily require identifier names.
fn parse_param_general(
&mut self,
+ is_self_allowed: bool,
is_trait_item: bool,
allow_c_variadic: bool,
is_name_required: impl Fn(&token::Token) -> bool,
) -> PResult<'a, Param> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
+
+ // Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
if let Some(mut param) = self.parse_self_param()? {
param.attrs = attrs.into();
- return self.recover_bad_self_param(param, is_trait_item);
+ return if is_self_allowed {
+ Ok(param)
+ } else {
+ self.recover_bad_self_param(param, is_trait_item)
+ };
}
let is_name_required = is_name_required(&self.token);
}
};
match p.parse_param_general(
+ false,
false,
allow_c_variadic,
do_not_enforce_named_arguments_for_c_variadic
Ok(Some(Param::from_self(ThinVec::default(), eself, eself_ident)))
}
- /// Returns the parsed optional self parameter with attributes and whether a self
- /// shortcut was used.
- fn parse_self_parameter_with_attrs(&mut self) -> PResult<'a, Option<Param>> {
- let attrs = self.parse_outer_attributes()?;
- let param_opt = self.parse_self_param()?;
- Ok(param_opt.map(|mut param| {
- param.attrs = attrs.into();
- param
- }))
- }
-
/// Parses the parameter list and result type of a function that may have a `self` parameter.
- fn parse_fn_decl_with_self<F>(&mut self, parse_param_fn: F) -> PResult<'a, P<FnDecl>>
- where F: FnMut(&mut Parser<'a>) -> PResult<'a, Param>,
- {
- self.expect(&token::OpenDelim(token::Paren))?;
-
- // Parse optional self argument.
- let self_param = self.parse_self_parameter_with_attrs()?;
-
- // Parse the rest of the function parameter list.
- let sep = SeqSep::trailing_allowed(token::Comma);
- let (mut fn_inputs, recovered) = if let Some(self_param) = self_param {
- if self.check(&token::CloseDelim(token::Paren)) {
- (vec![self_param], false)
- } else if self.eat(&token::Comma) {
- let mut fn_inputs = vec![self_param];
- let (mut input, _, recovered) = self.parse_seq_to_before_end(
- &token::CloseDelim(token::Paren), sep, parse_param_fn)?;
- fn_inputs.append(&mut input);
- (fn_inputs, recovered)
- } else {
- match self.expect_one_of(&[], &[]) {
- Err(err) => return Err(err),
- Ok(recovered) => (vec![self_param], recovered),
- }
- }
- } else {
- let (input, _, recovered) =
- self.parse_seq_to_before_end(&token::CloseDelim(token::Paren),
- sep,
- parse_param_fn)?;
- (input, recovered)
- };
+ fn parse_fn_decl_with_self(
+ &mut self,
+ is_name_required: impl Copy + Fn(&token::Token) -> bool,
+ ) -> PResult<'a, P<FnDecl>> {
+ // Parse the arguments, starting out with `self` being allowed...
+ let mut is_self_allowed = true;
+ let (mut inputs, _): (Vec<_>, _) = self.parse_paren_comma_seq(|p| {
+ let res = p.parse_param_general(is_self_allowed, true, false, is_name_required);
+ // ...but now that we've parsed the first argument, `self` is no longer allowed.
+ is_self_allowed = false;
+ res
+ })?;
- if !recovered {
- // Parse closing paren and return type.
- self.expect(&token::CloseDelim(token::Paren))?;
- }
// Replace duplicated recovered params with `_` pattern to avoid unecessary errors.
- self.deduplicate_recovered_params_names(&mut fn_inputs);
+ self.deduplicate_recovered_params_names(&mut inputs);
Ok(P(FnDecl {
- inputs: fn_inputs,
+ inputs,
output: self.parse_ret_ty(true)?,
}))
}
} else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
let ident = self.parse_ident().unwrap();
self.bump(); // `(`
- let kw_name = if let Ok(Some(_)) = self.parse_self_parameter_with_attrs()
- .map_err(|mut e| e.cancel())
- {
- "method"
- } else {
- "function"
- };
+ let kw_name = self.recover_first_param();
self.consume_block(token::Paren);
let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
self.eat_to_tokens(&[&token::Gt]);
self.bump(); // `>`
let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
- if let Ok(Some(_)) = self.parse_self_parameter_with_attrs()
- .map_err(|mut e| e.cancel())
- {
- ("fn", "method", false)
- } else {
- ("fn", "function", false)
- }
+ ("fn", self.recover_first_param(), false)
} else if self.check(&token::OpenDelim(token::Brace)) {
("struct", "struct", false)
} else {
self.parse_macro_use_or_failure(attrs, macros_allowed, attributes_allowed, lo, visibility)
}
+ fn recover_first_param(&mut self) -> &'static str {
+ match self.parse_outer_attributes()
+ .and_then(|_| self.parse_self_param())
+ .map_err(|mut e| e.cancel())
+ {
+ Ok(Some(_)) => "method",
+ _ => "function",
+ }
+ }
+
/// This is the fall-through for parsing items.
fn parse_macro_use_or_failure(
&mut self,
let (constness, unsafety, asyncness, abi) = self.parse_fn_front_matter()?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
- let decl = self.parse_fn_decl_with_self(|p| {
- p.parse_param_general(true, false, |_| true)
- })?;
+ let decl = self.parse_fn_decl_with_self(|_| true)?;
generics.where_clause = self.parse_where_clause()?;
*at_end = true;
let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
- let decl = self.parse_fn_decl_with_self(|p: &mut Parser<'a>| {
- // This is somewhat dubious; We don't want to allow
- // argument names to be left off if there is a
- // definition...
-
- // We don't allow argument names to be left off in edition 2018.
- let is_name_required = p.token.span.rust_2018();
- p.parse_param_general(true, false, |_| is_name_required)
- })?;
+ // This is somewhat dubious; We don't want to allow
+ // argument names to be left off if there is a definition...
+ //
+ // We don't allow argument names to be left off in edition 2018.
+ let decl = self.parse_fn_decl_with_self(|t| t.span.rust_2018())?;
generics.where_clause = self.parse_where_clause()?;
let sig = ast::MethodSig {
/// `Expected` for function and lambda parameter patterns.
pub(super) const PARAM_EXPECTED: Expected = Some("parameter name");
+const WHILE_PARSING_OR_MSG: &str = "while parsing this or-pattern starting here";
+
/// Whether or not an or-pattern should be gated when occurring in the current context.
#[derive(PartialEq)]
pub enum GateOr { Yes, No }
/// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level.
pub(super) fn parse_top_pat(&mut self, gate_or: GateOr) -> PResult<'a, P<Pat>> {
// Allow a '|' before the pats (RFCs 1925, 2530, and 2535).
- let gated_leading_vert = self.eat_or_separator() && gate_or == GateOr::Yes;
+ let gated_leading_vert = self.eat_or_separator(None) && gate_or == GateOr::Yes;
let leading_vert_span = self.prev_span;
// Parse the possibly-or-pattern.
/// Parse the pattern for a function or function pointer parameter.
/// Special recovery is provided for or-patterns and leading `|`.
pub(super) fn parse_fn_param_pat(&mut self) -> PResult<'a, P<Pat>> {
- self.recover_leading_vert("not allowed in a parameter pattern");
+ self.recover_leading_vert(None, "not allowed in a parameter pattern");
let pat = self.parse_pat_with_or(PARAM_EXPECTED, GateOr::No, RecoverComma::No)?;
if let PatKind::Or(..) = &pat.kind {
gate_or: GateOr,
rc: RecoverComma,
) -> PResult<'a, P<Pat>> {
- // Parse the first pattern.
+ // Parse the first pattern (`p_0`).
let first_pat = self.parse_pat(expected)?;
self.maybe_recover_unexpected_comma(first_pat.span, rc)?;
return Ok(first_pat)
}
+ // Parse the patterns `p_1 | ... | p_n` where `n > 0`.
let lo = first_pat.span;
let mut pats = vec![first_pat];
- while self.eat_or_separator() {
+ while self.eat_or_separator(Some(lo)) {
let pat = self.parse_pat(expected).map_err(|mut err| {
- err.span_label(lo, "while parsing this or-pattern starting here");
+ err.span_label(lo, WHILE_PARSING_OR_MSG);
err
})?;
self.maybe_recover_unexpected_comma(pat.span, rc)?;
/// Eat the or-pattern `|` separator.
/// If instead a `||` token is encountered, recover and pretend we parsed `|`.
- fn eat_or_separator(&mut self) -> bool {
+ fn eat_or_separator(&mut self, lo: Option<Span>) -> bool {
+ if self.recover_trailing_vert(lo) {
+ return false;
+ }
+
match self.token.kind {
token::OrOr => {
// Found `||`; Recover and pretend we parsed `|`.
- self.ban_unexpected_or_or();
+ self.ban_unexpected_or_or(lo);
self.bump();
true
}
}
}
+ /// Recover if `|` or `||` is the current token and we have one of the
+ /// tokens `=>`, `if`, `=`, `:`, `;`, `,`, `]`, `)`, or `}` ahead of us.
+ ///
+ /// These tokens all indicate that we reached the end of the or-pattern
+ /// list and can now reliably say that the `|` was an illegal trailing vert.
+ /// Note that there are more tokens such as `@` for which we know that the `|`
+ /// is an illegal parse. However, the user's intent is less clear in that case.
+ fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool {
+ let is_end_ahead = self.look_ahead(1, |token| match &token.kind {
+ token::FatArrow // e.g. `a | => 0,`.
+ | token::Ident(kw::If, false) // e.g. `a | if expr`.
+ | token::Eq // e.g. `let a | = 0`.
+ | token::Semi // e.g. `let a |;`.
+ | token::Colon // e.g. `let a | :`.
+ | token::Comma // e.g. `let (a |,)`.
+ | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`.
+ | token::CloseDelim(token::Paren) // e.g. `let (a | )`.
+ | token::CloseDelim(token::Brace) => true, // e.g. `let A { f: a | }`.
+ _ => false,
+ });
+ match (is_end_ahead, &self.token.kind) {
+ (true, token::BinOp(token::Or)) | (true, token::OrOr) => {
+ self.ban_illegal_vert(lo, "trailing", "not allowed in an or-pattern");
+ self.bump();
+ true
+ }
+ _ => false,
+ }
+ }
+
/// We have parsed `||` instead of `|`. Error and suggest `|` instead.
- fn ban_unexpected_or_or(&mut self) {
- self.struct_span_err(self.token.span, "unexpected token `||` after pattern")
- .span_suggestion(
- self.token.span,
- "use a single `|` to separate multiple alternative patterns",
- "|".to_owned(),
- Applicability::MachineApplicable
- )
- .emit();
+ fn ban_unexpected_or_or(&mut self, lo: Option<Span>) {
+ let mut err = self.struct_span_err(self.token.span, "unexpected token `||` after pattern");
+ err.span_suggestion(
+ self.token.span,
+ "use a single `|` to separate multiple alternative patterns",
+ "|".to_owned(),
+ Applicability::MachineApplicable
+ );
+ if let Some(lo) = lo {
+ err.span_label(lo, WHILE_PARSING_OR_MSG);
+ }
+ err.emit();
}
/// Some special error handling for the "top-level" patterns in a match arm,
/// Recursive possibly-or-pattern parser with recovery for an erroneous leading `|`.
/// See `parse_pat_with_or` for details on parsing or-patterns.
fn parse_pat_with_or_inner(&mut self) -> PResult<'a, P<Pat>> {
- self.recover_leading_vert("only allowed in a top-level pattern");
+ self.recover_leading_vert(None, "only allowed in a top-level pattern");
self.parse_pat_with_or(None, GateOr::Yes, RecoverComma::No)
}
/// Recover if `|` or `||` is here.
/// The user is thinking that a leading `|` is allowed in this position.
- fn recover_leading_vert(&mut self, ctx: &str) {
+ fn recover_leading_vert(&mut self, lo: Option<Span>, ctx: &str) {
if let token::BinOp(token::Or) | token::OrOr = self.token.kind {
- let span = self.token.span;
- let rm_msg = format!("remove the `{}`", pprust::token_to_string(&self.token));
-
- self.struct_span_err(span, &format!("a leading `|` is {}", ctx))
- .span_suggestion(span, &rm_msg, String::new(), Applicability::MachineApplicable)
- .emit();
-
+ self.ban_illegal_vert(lo, "leading", ctx);
self.bump();
}
}
+ /// A `|` or possibly `||` token shouldn't be here. Ban it.
+ fn ban_illegal_vert(&mut self, lo: Option<Span>, pos: &str, ctx: &str) {
+ let span = self.token.span;
+ let mut err = self.struct_span_err(span, &format!("a {} `|` is {}", pos, ctx));
+ err.span_suggestion(
+ span,
+ &format!("remove the `{}`", pprust::token_to_string(&self.token)),
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ if let Some(lo) = lo {
+ err.span_label(lo, WHILE_PARSING_OR_MSG);
+ }
+ if let token::OrOr = self.token.kind {
+ err.note("alternatives in or-patterns are separated with `|`, not `||`");
+ }
+ err.emit();
+ }
+
/// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are
/// allowed).
fn parse_pat_with_range_pat(
self.bump();
self.parse_pat_range_to(RangeEnd::Included(RangeSyntax::DotDotDot), "...")?
}
- // At this point, token != &, &&, (, [
+ // At this point, token != `&`, `&&`, `(`, `[`, `..`, `..=`, or `...`.
_ => if self.eat_keyword(kw::Underscore) {
// Parse _
PatKind::Wild
pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, Path> {
let meta_ident = match self.token.kind {
token::Interpolated(ref nt) => match **nt {
- token::NtMeta(ref meta) => match meta.kind {
- ast::MetaItemKind::Word => Some(meta.path.clone()),
- _ => None,
+ token::NtMeta(ref item) => match item.tokens.is_empty() {
+ true => Some(item.path.clone()),
+ false => None,
},
_ => None,
},
NtLifetime(ast::Ident),
NtLiteral(P<ast::Expr>),
/// Stuff inside brackets for attributes
- NtMeta(ast::MetaItem),
+ NtMeta(ast::AttrItem),
NtPath(ast::Path),
NtVis(ast::Visibility),
NtTT(TokenTree),
crate fn nonterminal_to_string(nt: &Nonterminal) -> String {
match *nt {
token::NtExpr(ref e) => expr_to_string(e),
- token::NtMeta(ref e) => meta_item_to_string(e),
+ token::NtMeta(ref e) => attr_item_to_string(e),
token::NtTy(ref e) => ty_to_string(e),
token::NtPath(ref e) => path_to_string(e),
token::NtItem(ref e) => item_to_string(e),
to_string(|s| s.print_meta_list_item(li))
}
-pub fn meta_item_to_string(mi: &ast::MetaItem) -> String {
- to_string(|s| s.print_meta_item(mi))
+fn attr_item_to_string(ai: &ast::AttrItem) -> String {
+ to_string(|s| s.print_attr_item(ai, ai.path.span))
}
pub fn attribute_to_string(attr: &ast::Attribute) -> String {
ast::AttrStyle::Inner => self.word("#!["),
ast::AttrStyle::Outer => self.word("#["),
}
- self.ibox(0);
- match attr.tokens.trees().next() {
- Some(TokenTree::Delimited(_, delim, tts)) => {
- self.print_mac_common(
- Some(MacHeader::Path(&attr.path)), false, None, delim, tts, true, attr.span
- );
- }
- tree => {
- self.print_path(&attr.path, false, 0);
- if tree.is_some() {
- self.space();
- self.print_tts(attr.tokens.clone(), true);
- }
+ self.print_attr_item(&attr.item, attr.span);
+ self.word("]");
+ }
+ }
+
+ fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) {
+ self.ibox(0);
+ match item.tokens.trees().next() {
+ Some(TokenTree::Delimited(_, delim, tts)) => {
+ self.print_mac_common(
+ Some(MacHeader::Path(&item.path)), false, None, delim, tts, true, span
+ );
+ }
+ tree => {
+ self.print_path(&item.path, false, 0);
+ if tree.is_some() {
+ self.space();
+ self.print_tts(item.tokens.clone(), true);
}
}
- self.end();
- self.word("]");
}
+ self.end();
}
fn print_meta_list_item(&mut self, item: &ast::NestedMetaItem) {
//! Attributes injected into the crate root from command line using `-Z crate-attr`.
-use syntax::ast::{self, AttrStyle};
+use syntax::ast::{self, AttrItem, AttrStyle};
use syntax::attr::mk_attr;
use syntax::panictry;
use syntax::parse::{self, token, ParseSess};
);
let start_span = parser.token.span;
- let (path, tokens) = panictry!(parser.parse_meta_item_unrestricted());
+ let AttrItem { path, tokens } = panictry!(parser.parse_attr_item());
let end_span = parser.token.span;
if parser.token != token::Eof {
parse_sess.span_diagnostic
use log::debug;
use smallvec::{smallvec, SmallVec};
+use rustc_target::spec::PanicStrategy;
use syntax::ast::{self, Ident};
use syntax::attr;
use syntax::entry::{self, EntryPointType};
struct TestCtxt<'a> {
ext_cx: ExtCtxt<'a>,
+ panic_strategy: PanicStrategy,
def_site: Span,
test_cases: Vec<Test>,
reexport_test_harness_main: Option<Symbol>,
krate: &mut ast::Crate,
span_diagnostic: &errors::Handler,
features: &Features,
+ panic_strategy: PanicStrategy,
+ platform_panic_strategy: PanicStrategy,
+ enable_panic_abort_tests: bool,
) {
// Check for #![reexport_test_harness_main = "some_name"] which gives the
// main test function the name `some_name` without hygiene. This needs to be
let test_runner = get_test_runner(span_diagnostic, &krate);
if should_test {
+ let panic_strategy = match (panic_strategy, enable_panic_abort_tests) {
+ (PanicStrategy::Abort, true) =>
+ PanicStrategy::Abort,
+ (PanicStrategy::Abort, false) if panic_strategy == platform_panic_strategy => {
+ // Silently allow compiling with panic=abort on these platforms,
+ // but with old behavior (abort if a test fails).
+ PanicStrategy::Unwind
+ }
+ (PanicStrategy::Abort, false) => {
+ span_diagnostic.err("building tests with panic=abort is not yet supported");
+ PanicStrategy::Unwind
+ }
+ (PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
+ };
generate_test_harness(sess, resolver, reexport_test_harness_main,
- krate, features, test_runner)
+ krate, features, panic_strategy, test_runner)
}
}
reexport_test_harness_main: Option<Symbol>,
krate: &mut ast::Crate,
features: &Features,
+ panic_strategy: PanicStrategy,
test_runner: Option<ast::Path>) {
let mut econfig = ExpansionConfig::default("test".to_string());
econfig.features = Some(features);
let cx = TestCtxt {
ext_cx,
+ panic_strategy,
def_site,
test_cases: Vec::new(),
reexport_test_harness_main,
let ecx = &cx.ext_cx;
let test_id = Ident::new(sym::test, sp);
+ let runner_name = match cx.panic_strategy {
+ PanicStrategy::Unwind => "test_main_static",
+ PanicStrategy::Abort => "test_main_static_abort",
+ };
+
// test::test_main_static(...)
let mut test_runner = cx.test_runner.clone().unwrap_or(
- ecx.path(sp, vec![test_id, ecx.ident_of("test_main_static", sp)]));
+ ecx.path(sp, vec![test_id, ecx.ident_of(runner_name, sp)]));
test_runner.span = sp;
rustc_serialize = { path = "../libserialize", package = "serialize" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
+rustc_index = { path = "../librustc_index" }
arena = { path = "../libarena" }
scoped-tls = "1.0"
unicode-width = "0.1.4"
use arena::DroplessArena;
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
-use rustc_data_structures::newtype_index;
+use rustc_index::vec::Idx;
use rustc_macros::symbols;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_serialize::{UseSpecializedDecodable, UseSpecializedEncodable};
/// ```
/// Internally, a symbol is implemented as an index, and all operations
/// (including hashing, equality, and ordering) operate on that index. The use
-/// of `newtype_index!` means that `Option<Symbol>` only takes up 4 bytes,
-/// because `newtype_index!` reserves the last 256 values for tagging purposes.
+/// of `rustc_index::newtype_index!` means that `Option<Symbol>` only takes up 4 bytes,
+/// because `rustc_index::newtype_index!` reserves the last 256 values for tagging purposes.
///
-/// Note that `Symbol` cannot directly be a `newtype_index!` because it
+/// Note that `Symbol` cannot directly be a `rustc_index::newtype_index!` because it
/// implements `fmt::Debug`, `Encodable`, and `Decodable` in special ways.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(SymbolIndex);
-newtype_index! {
+rustc_index::newtype_index! {
pub struct SymbolIndex { .. }
}
) -> io::Result<()>;
fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool>;
}
+
+pub(crate) fn write_stderr_delimiter(test_output: &mut Vec<u8>, test_name: &TestName) {
+ match test_output.last() {
+ Some(b'\n') => (),
+ Some(_) => test_output.push(b'\n'),
+ None => (),
+ }
+ write!(test_output, "---- {} stderr ----\n", test_name).unwrap();
+}
#![unstable(feature = "test", issue = "50297")]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))]
#![feature(asm)]
-#![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc, rustc_private))]
+#![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc))]
+#![feature(rustc_private)]
#![feature(nll)]
#![feature(set_stdio)]
#![feature(panic_unwind)]
extern crate libc;
use term;
-// FIXME(#54291): rustc and/or LLVM don't yet support building with panic-unwind
-// on aarch64-pc-windows-msvc, or thumbv7a-pc-windows-msvc
-// so we don't link libtest against libunwind (for the time being)
-// even though it means that libtest won't be fully functional on
-// these platforms.
-//
-// See also: https://github.com/rust-lang/rust/issues/54190#issuecomment-422904437
-#[cfg(not(all(windows, any(target_arch = "aarch64", target_arch = "arm"))))]
-extern crate panic_unwind;
-
pub use self::ColorConfig::*;
use self::NamePadding::*;
use self::OutputLocation::*;
use std::fs::File;
use std::io;
use std::io::prelude::*;
-use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo};
use std::path::PathBuf;
use std::process;
-use std::process::Termination;
+use std::process::{ExitStatus, Command, Termination};
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
const TEST_WARN_TIMEOUT_S: u64 = 60;
const QUIET_MODE_MAX_COLUMN: usize = 100; // insert a '\n' after 100 tests in quiet mode
+const SECONDARY_TEST_INVOKER_VAR: &'static str = "__RUST_TEST_INVOKE";
+
+// Return codes for secondary process.
+// Start somewhere other than 0 so we know the return code means what we think
+// it means.
+const TR_OK: i32 = 50;
+const TR_FAILED: i32 = 51;
+
// to be used by rustc to compile tests in libtest
pub mod test {
pub use crate::{
assert_test_result, filter_tests, parse_opts, run_test, test_main, test_main_static,
- Bencher, DynTestFn, DynTestName, Metric, MetricMap, Options, RunIgnored, ShouldPanic,
- StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestName, TestOpts,
- TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk,
+ Bencher, DynTestFn, DynTestName, Metric, MetricMap, Options, RunIgnored, RunStrategy,
+ ShouldPanic, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, TestDescAndFn, TestName,
+ TestOpts, TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk,
};
}
#[derive(Copy, Clone, Debug)]
pub struct Options {
display_output: bool,
+ panic_abort: bool,
}
impl Options {
pub fn new() -> Options {
Options {
display_output: false,
+ panic_abort: false,
}
}
self.display_output = display_output;
self
}
+
+ pub fn panic_abort(mut self, panic_abort: bool) -> Options {
+ self.panic_abort = panic_abort;
+ self
+ }
}
// The default console test runner. It accepts the command line
}
}
-// A variant optimized for invocation with a static test vector.
-// This will panic (intentionally) when fed any dynamic tests, because
-// it is copying the static values out into a dynamic vector and cannot
-// copy dynamic values. It is doing this because from this point on
-// a Vec<TestDescAndFn> is used in order to effect ownership-transfer
-// semantics into parallel test runners, which in turn requires a Vec<>
-// rather than a &[].
+/// A variant optimized for invocation with a static test vector.
+/// This will panic (intentionally) when fed any dynamic tests.
+///
+/// This is the entry point for the main function generated by `rustc --test`
+/// when panic=unwind.
pub fn test_main_static(tests: &[&TestDescAndFn]) {
let args = env::args().collect::<Vec<_>>();
- let owned_tests = tests
- .iter()
- .map(|t| match t.testfn {
- StaticTestFn(f) => TestDescAndFn {
- testfn: StaticTestFn(f),
- desc: t.desc.clone(),
- },
- StaticBenchFn(f) => TestDescAndFn {
- testfn: StaticBenchFn(f),
- desc: t.desc.clone(),
- },
- _ => panic!("non-static tests passed to test::test_main_static"),
- })
- .collect();
+ let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect();
test_main(&args, owned_tests, None)
}
+/// A variant optimized for invocation with a static test vector.
+/// This will panic (intentionally) when fed any dynamic tests.
+///
+/// Runs tests in panic=abort mode, which involves spawning subprocesses for
+/// tests.
+///
+/// This is the entry point for the main function generated by `rustc --test`
+/// when panic=abort.
+pub fn test_main_static_abort(tests: &[&TestDescAndFn]) {
+ // If we're being run in SpawnedSecondary mode, run the test here. run_test
+ // will then exit the process.
+ if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) {
+ let test = tests
+ .iter()
+ .filter(|test| test.desc.name.as_slice() == name)
+ .map(make_owned_test)
+ .next()
+ .expect("couldn't find a test with the provided name");
+ let TestDescAndFn { desc, testfn } = test;
+ let testfn = match testfn {
+ StaticTestFn(f) => f,
+ _ => panic!("only static tests are supported"),
+ };
+ run_test_in_spawned_subprocess(desc, Box::new(testfn));
+ }
+
+ let args = env::args().collect::<Vec<_>>();
+ let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect();
+ test_main(&args, owned_tests, Some(Options::new().panic_abort(true)))
+}
+
+/// Clones static values for putting into a dynamic vector, which test_main()
+/// needs to hand out ownership of tests to parallel test runners.
+///
+/// This will panic when fed any dynamic tests, because they cannot be cloned.
+fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn {
+ match test.testfn {
+ StaticTestFn(f) => TestDescAndFn {
+ testfn: StaticTestFn(f),
+ desc: test.desc.clone(),
+ },
+ StaticBenchFn(f) => TestDescAndFn {
+ testfn: StaticBenchFn(f),
+ desc: test.desc.clone(),
+ },
+ _ => panic!("non-static tests passed to test::test_main_static"),
+ }
+}
+
/// Invoked when unit tests terminate. Should panic if the unit
/// Tests is considered a failure. By default, invokes `report()`
/// and checks for a `0` result.
}
}
+#[derive(Clone, Copy)]
+pub enum RunStrategy {
+ /// Runs the test in the current process, and sends the result back over the
+ /// supplied channel.
+ InProcess,
+
+ /// Spawns a subprocess to run the test, and sends the result back over the
+ /// supplied channel. Requires argv[0] to exist and point to the binary
+ /// that's currently running.
+ SpawnPrimary,
+}
+
pub fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
where
F: FnMut(TestEvent) -> io::Result<()>,
let mut pending = 0;
let (tx, rx) = channel::<MonitorMsg>();
+ let run_strategy = if opts.options.panic_abort {
+ RunStrategy::SpawnPrimary
+ } else {
+ RunStrategy::InProcess
+ };
let mut running_tests: TestMap = HashMap::default();
while !remaining.is_empty() {
let test = remaining.pop().unwrap();
callback(TeWait(test.desc.clone()))?;
- run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::No);
+ run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::No);
let (test, result, exec_time, stdout) = rx.recv().unwrap();
callback(TeResult(test, result, exec_time, stdout))?;
}
let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
running_tests.insert(test.desc.clone(), timeout);
callback(TeWait(test.desc.clone()))?; //here no pad
- run_test(opts, !opts.run_tests, test, tx.clone(), Concurrent::Yes);
+ run_test(opts, !opts.run_tests, test, run_strategy, tx.clone(), Concurrent::Yes);
pending += 1;
}
// All benchmarks run at the end, in serial.
for b in filtered_benchs {
callback(TeWait(b.desc.clone()))?;
- run_test(opts, false, b, tx.clone(), Concurrent::No);
+ run_test(opts, false, b, run_strategy, tx.clone(), Concurrent::No);
let (test, result, exec_time, stdout) = rx.recv().unwrap();
callback(TeResult(test, result, exec_time, stdout))?;
}
opts: &TestOpts,
force_ignore: bool,
test: TestDescAndFn,
+ strategy: RunStrategy,
monitor_ch: Sender<MonitorMsg>,
concurrency: Concurrent,
) {
let TestDescAndFn { desc, testfn } = test;
- let ignore_because_panic_abort = cfg!(target_arch = "wasm32")
+ let ignore_because_no_process_support = cfg!(target_arch = "wasm32")
&& !cfg!(target_os = "emscripten")
&& desc.should_panic != ShouldPanic::No;
- if force_ignore || desc.ignore || ignore_because_panic_abort {
+ if force_ignore || desc.ignore || ignore_because_no_process_support {
monitor_ch.send((desc, TrIgnored, None, Vec::new())).unwrap();
return;
}
fn run_test_inner(
desc: TestDesc,
- monitor_ch: Sender<MonitorMsg>,
nocapture: bool,
report_time: bool,
+ strategy: RunStrategy,
+ monitor_ch: Sender<MonitorMsg>,
testfn: Box<dyn FnOnce() + Send>,
concurrency: Concurrent,
) {
- // Buffer for capturing standard I/O
- let data = Arc::new(Mutex::new(Vec::new()));
- let data2 = data.clone();
-
let name = desc.name.clone();
- let runtest = move || {
- let oldio = if !nocapture {
- Some((
- io::set_print(Some(Box::new(Sink(data2.clone())))),
- io::set_panic(Some(Box::new(Sink(data2)))),
- ))
- } else {
- None
- };
-
- let start = if report_time {
- Some(Instant::now())
- } else {
- None
- };
- let result = catch_unwind(AssertUnwindSafe(testfn));
- let exec_time = start.map(|start| {
- let duration = start.elapsed();
- TestExecTime(duration)
- });
-
- if let Some((printio, panicio)) = oldio {
- io::set_print(printio);
- io::set_panic(panicio);
- };
- let test_result = calc_result(&desc, result);
- let stdout = data.lock().unwrap().to_vec();
- monitor_ch
- .send((desc.clone(), test_result, exec_time, stdout))
- .unwrap();
+ let runtest = move || {
+ match strategy {
+ RunStrategy::InProcess =>
+ run_test_in_process(desc, nocapture, report_time, testfn, monitor_ch),
+ RunStrategy::SpawnPrimary => spawn_test_subprocess(desc, report_time, monitor_ch),
+ }
};
// If the platform is single-threaded we're just going to run
match testfn {
DynBenchFn(bencher) => {
+ // Benchmarks aren't expected to panic, so we run them all in-process.
crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
bencher.run(harness)
});
}
StaticBenchFn(benchfn) => {
+ // Benchmarks aren't expected to panic, so we run them all in-process.
crate::bench::benchmark(desc, monitor_ch, opts.nocapture, |harness| {
(benchfn.clone())(harness)
});
}
DynTestFn(f) => {
- let cb = move || __rust_begin_short_backtrace(f);
+ match strategy {
+ RunStrategy::InProcess => (),
+ _ => panic!("Cannot run dynamic test fn out-of-process"),
+ };
run_test_inner(
desc,
- monitor_ch,
opts.nocapture,
opts.report_time,
- Box::new(cb),
- concurrency,
- )
+ strategy,
+ monitor_ch,
+ Box::new(move || __rust_begin_short_backtrace(f)),
+ concurrency
+ );
}
StaticTestFn(f) => run_test_inner(
desc,
- monitor_ch,
opts.nocapture,
opts.report_time,
+ strategy,
+ monitor_ch,
Box::new(move || __rust_begin_short_backtrace(f)),
concurrency,
),
f()
}
-fn calc_result(desc: &TestDesc, task_result: Result<(), Box<dyn Any + Send>>) -> TestResult {
+fn calc_result<'a>(desc: &TestDesc,
+ task_result: Result<(), &'a (dyn Any + 'static + Send)>)
+-> TestResult {
match (&desc.should_panic, task_result) {
(&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TrOk,
(&ShouldPanic::YesWithMessage(msg), Err(ref err)) => {
}
}
+fn get_result_from_exit_code(desc: &TestDesc, code: i32) -> TestResult {
+ match (desc.allow_fail, code) {
+ (_, TR_OK) => TrOk,
+ (true, TR_FAILED) => TrAllowedFail,
+ (false, TR_FAILED) => TrFailed,
+ (_, _) => TrFailedMsg(format!("got unexpected return code {}", code)),
+ }
+}
+
+fn run_test_in_process(desc: TestDesc,
+ nocapture: bool,
+ report_time: bool,
+ testfn: Box<dyn FnOnce() + Send>,
+ monitor_ch: Sender<MonitorMsg>) {
+ // Buffer for capturing standard I/O
+ let data = Arc::new(Mutex::new(Vec::new()));
+
+ let oldio = if !nocapture {
+ Some((
+ io::set_print(Some(Box::new(Sink(data.clone())))),
+ io::set_panic(Some(Box::new(Sink(data.clone())))),
+ ))
+ } else {
+ None
+ };
+
+ let start = if report_time {
+ Some(Instant::now())
+ } else {
+ None
+ };
+ let result = catch_unwind(AssertUnwindSafe(testfn));
+ let exec_time = start.map(|start| {
+ let duration = start.elapsed();
+ TestExecTime(duration)
+ });
+
+ if let Some((printio, panicio)) = oldio {
+ io::set_print(printio);
+ io::set_panic(panicio);
+ }
+
+ let test_result = match result {
+ Ok(()) => calc_result(&desc, Ok(())),
+ Err(e) => calc_result(&desc, Err(e.as_ref())),
+ };
+ let stdout = data.lock().unwrap().to_vec();
+ monitor_ch.send((desc.clone(), test_result, exec_time, stdout)).unwrap();
+}
+
+fn spawn_test_subprocess(desc: TestDesc, report_time: bool, monitor_ch: Sender<MonitorMsg>) {
+ let (result, test_output, exec_time) = (|| {
+ let args = env::args().collect::<Vec<_>>();
+ let current_exe = &args[0];
+
+ let start = if report_time {
+ Some(Instant::now())
+ } else {
+ None
+ };
+ let output = match Command::new(current_exe)
+ .env(SECONDARY_TEST_INVOKER_VAR, desc.name.as_slice())
+ .output() {
+ Ok(out) => out,
+ Err(e) => {
+ let err = format!("Failed to spawn {} as child for test: {:?}", args[0], e);
+ return (TrFailed, err.into_bytes(), None);
+ }
+ };
+ let exec_time = start.map(|start| {
+ let duration = start.elapsed();
+ TestExecTime(duration)
+ });
+
+ let std::process::Output { stdout, stderr, status } = output;
+ let mut test_output = stdout;
+ formatters::write_stderr_delimiter(&mut test_output, &desc.name);
+ test_output.extend_from_slice(&stderr);
+
+ let result = match (|| -> Result<TestResult, String> {
+ let exit_code = get_exit_code(status)?;
+ Ok(get_result_from_exit_code(&desc, exit_code))
+ })() {
+ Ok(r) => r,
+ Err(e) => {
+ write!(&mut test_output, "Unexpected error: {}", e).unwrap();
+ TrFailed
+ }
+ };
+
+ (result, test_output, exec_time)
+ })();
+
+ monitor_ch.send((desc.clone(), result, exec_time, test_output)).unwrap();
+}
+
+fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Send>) -> ! {
+ let builtin_panic_hook = panic::take_hook();
+ let record_result = Arc::new(move |panic_info: Option<&'_ PanicInfo<'_>>| {
+ let test_result = match panic_info {
+ Some(info) => calc_result(&desc, Err(info.payload())),
+ None => calc_result(&desc, Ok(())),
+ };
+
+ // We don't support serializing TrFailedMsg, so just
+ // print the message out to stderr.
+ if let TrFailedMsg(msg) = &test_result {
+ eprintln!("{}", msg);
+ }
+
+ if let Some(info) = panic_info {
+ builtin_panic_hook(info);
+ }
+
+ if let TrOk = test_result {
+ process::exit(TR_OK);
+ } else {
+ process::exit(TR_FAILED);
+ }
+ });
+ let record_result2 = record_result.clone();
+ panic::set_hook(Box::new(move |info| record_result2(Some(&info))));
+ testfn();
+ record_result(None);
+ unreachable!("panic=abort callback should have exited the process")
+}
+
+#[cfg(not(unix))]
+fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
+ status.code().ok_or("received no exit code from child process".into())
+}
+
+#[cfg(unix)]
+fn get_exit_code(status: ExitStatus) -> Result<i32, String> {
+ use std::os::unix::process::ExitStatusExt;
+ match status.code() {
+ Some(code) => Ok(code),
+ None => match status.signal() {
+ Some(signal) => Err(format!("child process exited with signal {}", signal)),
+ None => Err("child process exited with unknown signal".into()),
+ }
+ }
+}
+
#[derive(Clone, PartialEq)]
pub struct MetricMap(BTreeMap<String, Metric>);
}
pub mod bench {
- use super::{BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestResult};
+ use super::{
+ BenchMode, BenchSamples, Bencher, MonitorMsg, Sender, Sink, TestDesc, TestResult
+ };
use crate::stats;
use std::cmp;
use std::io;
};
let data = Arc::new(Mutex::new(Vec::new()));
- let data2 = data.clone();
-
let oldio = if !nocapture {
Some((
- io::set_print(Some(Box::new(Sink(data2.clone())))),
- io::set_panic(Some(Box::new(Sink(data2)))),
+ io::set_print(Some(Box::new(Sink(data.clone())))),
+ io::set_panic(Some(Box::new(Sink(data.clone())))),
))
} else {
None
if let Some((printio, panicio)) = oldio {
io::set_print(printio);
io::set_panic(panicio);
- };
+ }
let test_result = match result {
//bs.bench(f) {
use super::*;
use crate::test::{
- filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored,
+ filter_tests, parse_opts, run_test, DynTestFn, DynTestName, MetricMap, RunIgnored, RunStrategy,
ShouldPanic, StaticTestName, TestDesc, TestDescAndFn, TestOpts, TrFailedMsg,
TrIgnored, TrOk,
};
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res != TrOk);
}
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res == TrIgnored);
}
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res == TrOk);
}
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res == TrOk);
}
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res == TrFailedMsg(format!("{} '{}'", failed_msg, expected)));
}
testfn: DynTestFn(Box::new(f)),
};
let (tx, rx) = channel();
- run_test(&TestOpts::new(), false, desc, tx, Concurrent::No);
+ run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, res, _, _) = rx.recv().unwrap();
assert!(res == TrFailedMsg("test did not panic as expected".to_string()));
}
..TestOpts::new()
};
let (tx, rx) = channel();
- run_test(&test_opts, false, desc, tx, Concurrent::No);
+ run_test(&test_opts, false, desc, RunStrategy::InProcess, tx, Concurrent::No);
let (_, _, exec_time, _) = rx.recv().unwrap();
exec_time
}
#![feature(rustc_private)]
-extern crate rustc_data_structures;
+extern crate rustc_index;
extern crate serialize as rustc_serialize;
-use rustc_data_structures::{newtype_index, indexed_vec::Idx};
+use rustc_index::{newtype_index, vec::Idx};
newtype_index!(struct MyIdx { MAX = 0xFFFF_FFFA });
+++ /dev/null
-// ignore-wasm32
-
-#![feature(decl_macro)]
-
-macro_rules! returns_isize(
- ($ident:ident) => (
- fn $ident() -> isize;
- )
-);
-
-macro takes_u32_returns_u32($ident:ident) {
- fn $ident (arg: u32) -> u32;
-}
-
-macro_rules! emits_nothing(
- () => ()
-);
-
-fn main() {
- assert_eq!(unsafe { rust_get_test_int() }, 0isize);
- assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEFu32);
-}
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern {
- returns_isize!(rust_get_test_int);
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- emits_nothing!();
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
-}
+++ /dev/null
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:26:5
- |
-LL | returns_isize!(rust_get_test_int);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:28:5
- |
-LL | takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:30:5
- |
-LL | emits_nothing!();
- | ^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
+++ /dev/null
-// force-host
-// no-prefer-dynamic
-
-// Proc macros commonly used by tests.
-// `panic`/`print` -> `panic_bang`/`print_bang` to avoid conflicts with standard macros.
-
-#![crate_type = "proc-macro"]
-
-extern crate proc_macro;
-use proc_macro::TokenStream;
-
-// Macro that return empty token stream.
-
-#[proc_macro]
-pub fn empty(_: TokenStream) -> TokenStream {
- TokenStream::new()
-}
-
-#[proc_macro_attribute]
-pub fn empty_attr(_: TokenStream, _: TokenStream) -> TokenStream {
- TokenStream::new()
-}
-
-#[proc_macro_derive(Empty, attributes(empty_helper))]
-pub fn empty_derive(_: TokenStream) -> TokenStream {
- TokenStream::new()
-}
-
-// Macro that panics.
-
-#[proc_macro]
-pub fn panic_bang(_: TokenStream) -> TokenStream {
- panic!("panic-bang");
-}
-
-#[proc_macro_attribute]
-pub fn panic_attr(_: TokenStream, _: TokenStream) -> TokenStream {
- panic!("panic-attr");
-}
-
-#[proc_macro_derive(Panic, attributes(panic_helper))]
-pub fn panic_derive(_: TokenStream) -> TokenStream {
- panic!("panic-derive");
-}
-
-// Macros that return the input stream.
-
-#[proc_macro]
-pub fn identity(input: TokenStream) -> TokenStream {
- input
-}
-
-#[proc_macro_attribute]
-pub fn identity_attr(_: TokenStream, input: TokenStream) -> TokenStream {
- input
-}
-
-#[proc_macro_derive(Identity, attributes(identity_helper))]
-pub fn identity_derive(input: TokenStream) -> TokenStream {
- input
-}
-
-// Macros that iterate and re-collect the input stream.
-
-#[proc_macro]
-pub fn recollect(input: TokenStream) -> TokenStream {
- input.into_iter().collect()
-}
-
-#[proc_macro_attribute]
-pub fn recollect_attr(_: TokenStream, input: TokenStream) -> TokenStream {
- input.into_iter().collect()
-}
-
-#[proc_macro_derive(Recollect, attributes(recollect_helper))]
-pub fn recollect_derive(input: TokenStream) -> TokenStream {
- input.into_iter().collect()
-}
-
-// Macros that print their input in the original and re-collected forms (if they differ).
-
-fn print_helper(input: TokenStream, kind: &str) -> TokenStream {
- let input_display = format!("{}", input);
- let input_debug = format!("{:#?}", input);
- let recollected = input.into_iter().collect();
- let recollected_display = format!("{}", recollected);
- let recollected_debug = format!("{:#?}", recollected);
- println!("PRINT-{} INPUT (DISPLAY): {}", kind, input_display);
- if recollected_display != input_display {
- println!("PRINT-{} RE-COLLECTED (DISPLAY): {}", kind, recollected_display);
- }
- println!("PRINT-{} INPUT (DEBUG): {}", kind, input_debug);
- if recollected_debug != input_debug {
- println!("PRINT-{} RE-COLLECTED (DEBUG): {}", kind, recollected_debug);
- }
- recollected
-}
-
-#[proc_macro]
-pub fn print_bang(input: TokenStream) -> TokenStream {
- print_helper(input, "BANG")
-}
-
-#[proc_macro_attribute]
-pub fn print_attr(_: TokenStream, input: TokenStream) -> TokenStream {
- print_helper(input, "ATTR")
-}
-
-#[proc_macro_derive(Print, attributes(print_helper))]
-pub fn print_derive(input: TokenStream) -> TokenStream {
- print_helper(input, "DERIVE")
-}
+++ /dev/null
-// aux-build:test-macros.rs
-// ignore-wasm32
-
-#[macro_use]
-extern crate test_macros;
-
-fn main() {
- assert_eq!(unsafe { rust_get_test_int() }, 0isize);
- assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF);
-}
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern {
- #[empty_attr]
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- fn some_definitely_unknown_symbol_which_should_be_removed();
-
- #[identity_attr]
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- fn rust_get_test_int() -> isize;
-
- identity!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
-}
+++ /dev/null
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:14:5
- |
-LL | #[empty_attr]
- | ^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:18:5
- |
-LL | #[identity_attr]
- | ^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/macros-in-extern.rs:22:5
- |
-LL | identity!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
// check that macro expanded code works
macro_rules! if_cfg {
- ($cfg:meta $ib:block else $eb:block) => {
+ ($cfg:meta? $ib:block else $eb:block) => {
{
let r;
#[cfg($cfg)]
}
}
- let n = if_cfg!(unset {
+ let n = if_cfg!(unset? {
413
} else {
612
error: aborting due to 17 previous errors
-Some errors have detailed explanations: E0010, E0015, E0019, E0507.
+Some errors have detailed explanations: E0010, E0015, E0019, E0493, E0507.
For more information about an error, try `rustc --explain E0010`.
--- /dev/null
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
+
+struct ConstString<const T: &'static str>;
+struct ConstBytes<const T: &'static [u8]>;
+
+pub fn main() {
+ let _: ConstString<"Hello"> = ConstString::<"Hello">;
+ let _: ConstString<"Hello"> = ConstString::<"World">; //~ ERROR mismatched types
+ let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↦">;
+ let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">; //~ ERROR mismatched types
+ let _: ConstBytes<b"AAA"> = ConstBytes::<{&[0x41, 0x41, 0x41]}>;
+ let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">; //~ ERROR mismatched types
+}
--- /dev/null
+warning: the feature `const_generics` is incomplete and may cause the compiler to crash
+ --> $DIR/slice-const-param-mismatch.rs:1:12
+ |
+LL | #![feature(const_generics)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+ --> $DIR/slice-const-param-mismatch.rs:9:35
+ |
+LL | let _: ConstString<"Hello"> = ConstString::<"World">;
+ | ^^^^^^^^^^^^^^^^^^^^^^ expected `"Hello"`, found `"World"`
+ |
+ = note: expected type `ConstString<>`
+ found type `ConstString<>`
+
+error[E0308]: mismatched types
+ --> $DIR/slice-const-param-mismatch.rs:11:33
+ |
+LL | let _: ConstString<"ℇ㇈↦"> = ConstString::<"ℇ㇈↥">;
+ | ^^^^^^^^^^^^^^^^^^^^^ expected `"ℇ㇈↦"`, found `"ℇ㇈↥"`
+ |
+ = note: expected type `ConstString<>`
+ found type `ConstString<>`
+
+error[E0308]: mismatched types
+ --> $DIR/slice-const-param-mismatch.rs:13:33
+ |
+LL | let _: ConstBytes<b"AAA"> = ConstBytes::<b"BBB">;
+ | ^^^^^^^^^^^^^^^^^^^^ expected `b"AAA"`, found `b"BBB"`
+ |
+ = note: expected type `ConstBytes<>`
+ found type `ConstBytes<>`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-pass
+
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
+
+pub fn function_with_str<const STRING: &'static str>() -> &'static str {
+ STRING
+}
+
+pub fn function_with_bytes<const BYTES: &'static [u8]>() -> &'static [u8] {
+ BYTES
+}
+
+pub fn main() {
+ assert_eq!(function_with_str::<"Rust">(), "Rust");
+ assert_eq!(function_with_str::<"ℇ㇈↦">(), "ℇ㇈↦");
+ assert_eq!(function_with_bytes::<b"AAAA">(), &[0x41, 0x41, 0x41, 0x41]);
+ assert_eq!(function_with_bytes::<{&[0x41, 0x41, 0x41, 0x41]}>(), b"AAAA");
+}
--- /dev/null
+warning: the feature `const_generics` is incomplete and may cause the compiler to crash
+ --> $DIR/slice-const-param.rs:3:12
+ |
+LL | #![feature(const_generics)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+
error: aborting due to 4 previous errors
+For more information about this error, try `rustc --explain E0493`.
--- /dev/null
+enum Foo {
+ A = foo(), //~ ERROR: type annotations needed
+ B = foo(), //~ ERROR: type annotations needed
+}
+
+const fn foo<T>() -> isize {
+ 0
+}
+
+fn main() {}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/issue-64662.rs:2:9
+ |
+LL | A = foo(),
+ | ^^^ cannot infer type for `T`
+
+error[E0282]: type annotations needed
+ --> $DIR/issue-64662.rs:3:9
+ |
+LL | B = foo(),
+ | ^^^ cannot infer type for `T`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
error: aborting due to 37 previous errors
-Some errors have detailed explanations: E0515, E0723.
-For more information about an error, try `rustc --explain E0515`.
+Some errors have detailed explanations: E0493, E0515, E0723.
+For more information about an error, try `rustc --explain E0493`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0493`.
error: aborting due to 9 previous errors
-Some errors have detailed explanations: E0538, E0541, E0565.
+Some errors have detailed explanations: E0538, E0541, E0550, E0565.
For more information about an error, try `rustc --explain E0538`.
error[E0423]: expected value, found struct `Empty1`
--> $DIR/empty-struct-braces-expr.rs:15:14
|
+LL | struct Empty1 {}
+ | ---------------- `Empty1` defined here
+...
LL | let e1 = Empty1;
| ^^^^^^
| |
error[E0423]: expected function, found struct `Empty1`
--> $DIR/empty-struct-braces-expr.rs:16:14
|
+LL | struct Empty1 {}
+ | ---------------- `Empty1` defined here
+...
LL | let e1 = Empty1();
| ^^^^^^
| |
error[E0423]: expected value, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-expr.rs:17:14
|
+LL | Empty3 {}
+ | --------- `E::Empty3` defined here
+...
LL | let e3 = E::Empty3;
| ^^^^^^^^^ did you mean `E::Empty3 { /* fields */ }`?
error[E0423]: expected function, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-expr.rs:18:14
|
+LL | Empty3 {}
+ | --------- `E::Empty3` defined here
+...
LL | let e3 = E::Empty3();
| ^^^^^^^^^ did you mean `E::Empty3 { /* fields */ }`?
error[E0532]: expected unit struct/variant or constant, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-pat-1.rs:24:9
|
+LL | Empty3 {}
+ | --------- `E::Empty3` defined here
+...
LL | E::Empty3 => ()
| ^^^^^^^^^ did you mean `E::Empty3 { /* fields */ }`?
error[E0532]: expected tuple struct/variant, found struct `Empty1`
--> $DIR/empty-struct-braces-pat-2.rs:15:9
|
+LL | struct Empty1 {}
+ | ---------------- `Empty1` defined here
+...
LL | Empty1() => ()
| ^^^^^^
| |
error[E0532]: expected tuple struct/variant, found struct `Empty1`
--> $DIR/empty-struct-braces-pat-2.rs:21:9
|
+LL | struct Empty1 {}
+ | ---------------- `Empty1` defined here
+...
LL | Empty1(..) => ()
| ^^^^^^
| |
error[E0532]: expected tuple struct/variant, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-pat-3.rs:17:9
|
+LL | Empty3 {}
+ | --------- `E::Empty3` defined here
+...
LL | E::Empty3() => ()
| ^^^^^^^^^ did you mean `E::Empty3 { /* fields */ }`?
error[E0532]: expected tuple struct/variant, found struct variant `E::Empty3`
--> $DIR/empty-struct-braces-pat-3.rs:25:9
|
+LL | Empty3 {}
+ | --------- `E::Empty3` defined here
+...
LL | E::Empty3(..) => ()
| ^^^^^^^^^ did you mean `E::Empty3 { /* fields */ }`?
error[E0532]: expected unit struct/variant or constant, found tuple variant `E::Empty4`
--> $DIR/empty-struct-tuple-pat.rs:29:9
|
+LL | Empty4()
+ | -------- `E::Empty4` defined here
+...
LL | E::Empty4 => ()
- | ^^^^^^^^^ did you mean `E::Empty4 ( /* fields */ )`?
+ | ^^^^^^^^^ did you mean `E::Empty4( /* fields */ )`?
error[E0532]: expected unit struct/variant or constant, found tuple variant `XE::XEmpty5`
--> $DIR/empty-struct-tuple-pat.rs:33:9
| ^^^^-------
| | |
| | help: a unit variant with a similar name exists: `XEmpty4`
- | did you mean `XE::XEmpty5 ( /* fields */ )`?
+ | did you mean `XE::XEmpty5( /* fields */ )`?
error: aborting due to 4 previous errors
error[E0423]: expected function, found struct `Foo`
--> $DIR/E0423.rs:4:13
|
+LL | struct Foo { a: bool };
+ | ---------------------- `Foo` defined here
+LL |
LL | let f = Foo();
| ^^^
| |
+++ /dev/null
-#![feature(decl_macro)]
-
-macro_rules! returns_isize(
- ($ident:ident) => (
- fn $ident() -> isize;
- )
-);
-
-macro takes_u32_returns_u32($ident:ident) {
- fn $ident (arg: u32) -> u32;
-}
-
-macro_rules! emits_nothing(
- () => ()
-);
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern {
- returns_isize!(rust_get_test_int);
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
- emits_nothing!();
- //~^ ERROR macro invocations in `extern {}` blocks are experimental
-}
-
-fn main() {}
+++ /dev/null
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/feature-gate-macros_in_extern.rs:19:5
- |
-LL | returns_isize!(rust_get_test_int);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/feature-gate-macros_in_extern.rs:21:5
- |
-LL | takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error[E0658]: macro invocations in `extern {}` blocks are experimental
- --> $DIR/feature-gate-macros_in_extern.rs:23:5
- |
-LL | emits_nothing!();
- | ^^^^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/49476
- = help: add `#![feature(macros_in_extern)]` to the crate attributes to enable
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
error[E0532]: expected tuple struct/variant, found struct variant `FooB`
--> $DIR/issue-19086.rs:10:9
|
+LL | FooB { x: i32, y: i32 }
+ | ----------------------- `FooB` defined here
+...
LL | FooB(a, b) => println!("{} {}", a, b),
| ^^^^ did you mean `FooB { /* fields */ }`?
error[E0532]: expected unit struct/variant or constant, found tuple variant `Foo::Bar`
--> $DIR/issue-32004.rs:10:9
|
+LL | Bar(i32),
+ | -------- `Foo::Bar` defined here
+...
LL | Foo::Bar => {}
| ^^^^^---
| | |
| | help: a unit variant with a similar name exists: `Baz`
- | did you mean `Foo::Bar ( /* fields */ )`?
+ | did you mean `Foo::Bar( /* fields */ )`?
error[E0532]: expected tuple struct/variant, found unit struct `S`
--> $DIR/issue-32004.rs:16:9
error[E0532]: expected unit struct/variant or constant, found tuple variant `MyEnum::Tuple`
--> $DIR/issue-63983.rs:8:9
|
+LL | Tuple(i32),
+ | ---------- `MyEnum::Tuple` defined here
+...
LL | MyEnum::Tuple => "",
- | ^^^^^^^^^^^^^ did you mean `MyEnum::Tuple ( /* fields */ )`?
+ | ^^^^^^^^^^^^^ did you mean `MyEnum::Tuple( /* fields */ )`?
error[E0532]: expected unit struct/variant or constant, found struct variant `MyEnum::Struct`
--> $DIR/issue-63983.rs:10:9
|
+LL | Struct{ s: i32 },
+ | ---------------- `MyEnum::Struct` defined here
+...
LL | MyEnum::Struct => "",
| ^^^^^^^^^^^^^^ did you mean `MyEnum::Struct { /* fields */ }`?
b: i32,
//~^ ERROR unused variable: `b`
) {}
+ fn issue_64682_associated_fn(
+ #[allow(unused_variables)] a: i32,
+ b: i32,
+ //~^ ERROR unused variable: `b`
+ ) {}
}
trait RefTrait {
fn bar(
b: i32,
//~^ ERROR unused variable: `b`
) {}
+ fn issue_64682_associated_fn(
+ #[allow(unused_variables)] a: i32,
+ b: i32,
+ //~^ ERROR unused variable: `b`
+ ) {}
}
impl RefTrait for RefStruct {
fn bar(
b: i32,
//~^ ERROR unused variable: `b`
) {}
+ fn issue_64682_associated_fn(
+ #[allow(unused_variables)] a: i32,
+ b: i32,
+ //~^ ERROR unused variable: `b`
+ ) {}
}
fn main() {
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `a`
- --> $DIR/lint-unused-variables.rs:53:9
+ --> $DIR/lint-unused-variables.rs:68:9
|
LL | a: i32,
| ^ help: consider prefixing with an underscore: `_a`
error: unused variable: `b`
- --> $DIR/lint-unused-variables.rs:59:9
+ --> $DIR/lint-unused-variables.rs:74:9
|
LL | b: i32,
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `b`
- --> $DIR/lint-unused-variables.rs:37:9
+ --> $DIR/lint-unused-variables.rs:42:9
+ |
+LL | b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `b`
+ --> $DIR/lint-unused-variables.rs:47:9
|
LL | b: i32,
| ^ help: consider prefixing with an underscore: `_b`
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `b`
- --> $DIR/lint-unused-variables.rs:45:9
+ --> $DIR/lint-unused-variables.rs:34:9
+ |
+LL | b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `b`
+ --> $DIR/lint-unused-variables.rs:55:9
+ |
+LL | b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `b`
+ --> $DIR/lint-unused-variables.rs:60:9
|
LL | b: i32,
| ^ help: consider prefixing with an underscore: `_b`
-error: aborting due to 8 previous errors
+error: aborting due to 11 previous errors
-#![feature(macros_in_extern)]
-
macro_rules! m {
() => {
let //~ ERROR expected
error: expected one of `crate`, `fn`, `pub`, `static`, or `type`, found `let`
- --> $DIR/issue-54441.rs:5:9
+ --> $DIR/issue-54441.rs:3:9
|
LL | let
| ^^^ unexpected token
test_path!(std::u8,);
test_path!(any, super, super::super::self::path, X<Y>::Z<'a, T=U>);
-macro_rules! test_meta_block {
- ($($m:meta)* $b:block) => {};
-}
-
-test_meta_block!(windows {});
-
macro_rules! test_lifetime {
(1. $($l:lifetime)* $($b:block)*) => {};
(2. $($b:block)* $($l:lifetime)*) => {};
--- /dev/null
+// check-pass
+
+macro_rules! check { ($meta:meta) => () }
+
+check!(meta(a b c d));
+check!(meta[a b c d]);
+check!(meta { a b c d });
+check!(meta);
+check!(meta = 0);
+
+fn main() {}
+++ /dev/null
-// run-pass
-// ignore-wasm32
-
-#![feature(decl_macro, macros_in_extern)]
-
-macro_rules! returns_isize(
- ($ident:ident) => (
- fn $ident() -> isize;
- )
-);
-
-macro takes_u32_returns_u32($ident:ident) {
- fn $ident (arg: u32) -> u32;
-}
-
-macro_rules! emits_nothing(
- () => ()
-);
-
-fn main() {
- assert_eq!(unsafe { rust_get_test_int() }, 1isize);
- assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEFu32);
-}
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern {
- returns_isize!(rust_get_test_int);
- takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
- emits_nothing!();
-}
--- /dev/null
+// run-pass
+// ignore-wasm32
+
+#![feature(decl_macro)]
+
+macro_rules! returns_isize(
+ ($ident:ident) => (
+ fn $ident() -> isize;
+ )
+);
+
+macro takes_u32_returns_u32($ident:ident) {
+ fn $ident (arg: u32) -> u32;
+}
+
+macro_rules! emits_nothing(
+ () => ()
+);
+
+macro_rules! emits_multiple(
+ () => {
+ fn f1() -> u32;
+ fn f2() -> u32;
+ }
+);
+
+mod defs {
+ #[no_mangle] extern fn f1() -> u32 { 1 }
+ #[no_mangle] extern fn f2() -> u32 { 2 }
+}
+
+fn main() {
+ assert_eq!(unsafe { rust_get_test_int() }, 1);
+ assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEFu32);
+ assert_eq!(unsafe { f1() }, 1);
+ assert_eq!(unsafe { f2() }, 2);
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern {
+ returns_isize!(rust_get_test_int);
+ takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
+ emits_nothing!();
+ emits_multiple!();
+}
<Foo>::trait_bar => {}
//~^ ERROR expected unit struct/variant or constant, found method `<Foo>::trait_bar`
}
+ if let Foo::bar = 0u32 {}
+ //~^ ERROR expected unit struct/variant or constant, found method `<Foo>::bar`
+ if let <Foo>::bar = 0u32 {}
+ //~^ ERROR expected unit struct/variant or constant, found method `<Foo>::bar`
+ if let Foo::trait_bar = 0u32 {}
+ //~^ ERROR expected unit struct/variant or constant, found method `<Foo>::trait_bar`
}
LL | <Foo>::trait_bar => {}
| ^^^^^^^^^^^^^^^^
-error: aborting due to 3 previous errors
+error[E0533]: expected unit struct/variant or constant, found method `<Foo>::bar`
+ --> $DIR/method-path-in-pattern.rs:26:12
+ |
+LL | if let Foo::bar = 0u32 {}
+ | ^^^^^^^^
+
+error[E0533]: expected unit struct/variant or constant, found method `<Foo>::bar`
+ --> $DIR/method-path-in-pattern.rs:28:12
+ |
+LL | if let <Foo>::bar = 0u32 {}
+ | ^^^^^^^^^^
+
+error[E0533]: expected unit struct/variant or constant, found method `<Foo>::trait_bar`
+ --> $DIR/method-path-in-pattern.rs:30:12
+ |
+LL | if let Foo::trait_bar = 0u32 {}
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+For more information about this error, try `rustc --explain E0533`.
error[E0423]: expected value, found struct variant `m7::V`
--> $DIR/namespace-mix.rs:100:11
|
+LL | V {},
+ | ---- `m7::V` defined here
+...
LL | check(m7::V);
| ^^^^^ did you mean `m7::V { /* fields */ }`?
help: a tuple variant with a similar name exists
--- /dev/null
+// In this regression test we check that a trailing `|` in an or-pattern just
+// before the `if` token of a `match` guard will receive parser recovery with
+// an appropriate error message.
+
+enum E { A, B }
+
+fn main() {
+ match E::A {
+ E::A |
+ E::B | //~ ERROR a trailing `|` is not allowed in an or-pattern
+ if true => {
+ let recovery_witness: bool = 0; //~ ERROR mismatched types
+ }
+ }
+}
--- /dev/null
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/issue-64879-trailing-before-guard.rs:10:14
+ |
+LL | E::A |
+ | ---- while parsing this or-pattern starting here
+LL | E::B |
+ | ^ help: remove the `|`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-64879-trailing-before-guard.rs:12:42
+ |
+LL | let recovery_witness: bool = 0;
+ | ^ expected bool, found integer
+ |
+ = note: expected type `bool`
+ found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--> $DIR/multiple-pattern-typo.rs:8:15
|
LL | 1 | 2 || 3 => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:13:16
|
LL | (1 | 2 || 3) => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:18:16
|
LL | (1 | 2 || 3,) => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:25:18
|
LL | TS(1 | 2 || 3) => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:32:23
|
LL | NS { f: 1 | 2 || 3 } => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:37:16
|
LL | [1 | 2 || 3] => (),
- | ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
error: unexpected token `||` after pattern
--> $DIR/multiple-pattern-typo.rs:42:9
|
LL | let ( || A | B) = E::A;
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/or-patterns-syntactic-fail.rs:48:11
|
LL | let [ || A | B ] = [E::A];
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/or-patterns-syntactic-fail.rs:49:13
|
LL | let TS( || A | B );
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/or-patterns-syntactic-fail.rs:50:17
|
LL | let NS { f: || A | B };
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: no rules expected the token `|`
--> $DIR/or-patterns-syntactic-fail.rs:14:15
-// Test the suggestion to remove a leading `|`.
+// Test the suggestion to remove a leading, or trailing `|`.
// run-rustfix
fn main() {}
#[cfg(FALSE)]
-fn leading_vert() {
+fn leading() {
fn fun1( A: E) {} //~ ERROR a leading `|` is not allowed in a parameter pattern
fn fun2( A: E) {} //~ ERROR a leading `|` is not allowed in a parameter pattern
let ( A): E; //~ ERROR a leading `|` is only allowed in a top-level pattern
let NS { f: A }: NS; //~ ERROR a leading `|` is only allowed in a top-level pattern
let NS { f: A }: NS; //~ ERROR a leading `|` is only allowed in a top-level pattern
}
+
+#[cfg(FALSE)]
+fn trailing() {
+ let ( A ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let (a ,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let ( A | B ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let [ A | B ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let S { f: B }; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let ( A | B ): E; //~ ERROR unexpected token `||` after pattern
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ match A {
+ A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern
+ A => {} //~ ERROR a trailing `|` is not allowed in an or-pattern
+ A | B => {} //~ ERROR unexpected token `||` after pattern
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ | A | B => {}
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ }
+
+ let a : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let a = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let a ; //~ ERROR a trailing `|` is not allowed in an or-pattern
+}
-// Test the suggestion to remove a leading `|`.
+// Test the suggestion to remove a leading, or trailing `|`.
// run-rustfix
fn main() {}
#[cfg(FALSE)]
-fn leading_vert() {
+fn leading() {
fn fun1( | A: E) {} //~ ERROR a leading `|` is not allowed in a parameter pattern
fn fun2( || A: E) {} //~ ERROR a leading `|` is not allowed in a parameter pattern
let ( | A): E; //~ ERROR a leading `|` is only allowed in a top-level pattern
let NS { f: | A }: NS; //~ ERROR a leading `|` is only allowed in a top-level pattern
let NS { f: || A }: NS; //~ ERROR a leading `|` is only allowed in a top-level pattern
}
+
+#[cfg(FALSE)]
+fn trailing() {
+ let ( A | ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let (a |,): (E,); //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let ( A | B | ): E; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let [ A | B | ]: [E; 1]; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let S { f: B | }; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let ( A || B | ): E; //~ ERROR unexpected token `||` after pattern
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ match A {
+ A | => {} //~ ERROR a trailing `|` is not allowed in an or-pattern
+ A || => {} //~ ERROR a trailing `|` is not allowed in an or-pattern
+ A || B | => {} //~ ERROR unexpected token `||` after pattern
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ | A | B | => {}
+ //~^ ERROR a trailing `|` is not allowed in an or-pattern
+ }
+
+ let a | : u8 = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let a | = 0; //~ ERROR a trailing `|` is not allowed in an or-pattern
+ let a | ; //~ ERROR a trailing `|` is not allowed in an or-pattern
+}
|
LL | fn fun2( || A: E) {}
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/remove-leading-vert.rs:14:11
|
LL | let ( || A): (E);
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/remove-leading-vert.rs:16:11
|
LL | let [ || A ]: [E; 1];
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/remove-leading-vert.rs:19:13
|
LL | let TS( || A ): TS;
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
error: a leading `|` is only allowed in a top-level pattern
--> $DIR/remove-leading-vert.rs:21:17
|
LL | let NS { f: || A }: NS;
| ^^ help: remove the `||`
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:27:13
+ |
+LL | let ( A | ): E;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:28:12
+ |
+LL | let (a |,): (E,);
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:29:17
+ |
+LL | let ( A | B | ): E;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:30:17
+ |
+LL | let [ A | B | ]: [E; 1];
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:31:18
+ |
+LL | let S { f: B | };
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: unexpected token `||` after pattern
+ --> $DIR/remove-leading-vert.rs:32:13
+ |
+LL | let ( A || B | ): E;
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:32:18
+ |
+LL | let ( A || B | ): E;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:35:11
+ |
+LL | A | => {}
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:36:11
+ |
+LL | A || => {}
+ | - ^^ help: remove the `||`
+ | |
+ | while parsing this or-pattern starting here
+ |
+ = note: alternatives in or-patterns are separated with `|`, not `||`
+
+error: unexpected token `||` after pattern
+ --> $DIR/remove-leading-vert.rs:37:11
+ |
+LL | A || B | => {}
+ | - ^^ help: use a single `|` to separate multiple alternative patterns: `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:37:16
+ |
+LL | A || B | => {}
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:39:17
+ |
+LL | | A | B | => {}
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:43:11
+ |
+LL | let a | : u8 = 0;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:44:11
+ |
+LL | let a | = 0;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
+
+error: a trailing `|` is not allowed in an or-pattern
+ --> $DIR/remove-leading-vert.rs:45:11
+ |
+LL | let a | ;
+ | - ^ help: remove the `|`
+ | |
+ | while parsing this or-pattern starting here
-error: aborting due to 11 previous errors
+error: aborting due to 26 previous errors
+++ /dev/null
-// error-pattern:is not compiled with this crate's panic strategy `abort`
-// compile-flags:-C panic=abort
-// ignore-wasm32-bare compiled with panic=abort by default
-
-#![feature(test)]
-
-extern crate test;
-
-fn main() {
-}
+++ /dev/null
-error: the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort`
-
-error: aborting due to previous error
-
error[E0532]: expected tuple struct/variant, found struct variant `Enum::Foo`
--> $DIR/recover-from-bad-variant.rs:10:9
|
+LL | Foo { a: usize, b: usize },
+ | -------------------------- `Enum::Foo` defined here
+...
LL | Enum::Foo(a, b) => {}
| ^^^^^^^^^ did you mean `Enum::Foo { /* fields */ }`?
+++ /dev/null
-// force-host
-// no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
-extern crate proc_macro;
-
-use proc_macro::TokenStream;
-
-#[proc_macro_attribute]
-pub fn nop_attr(_attr: TokenStream, input: TokenStream) -> TokenStream {
- assert!(_attr.to_string().is_empty());
- input
-}
-
-#[proc_macro_attribute]
-pub fn no_output(_attr: TokenStream, _input: TokenStream) -> TokenStream {
- assert!(_attr.to_string().is_empty());
- assert!(!_input.to_string().is_empty());
- "".parse().unwrap()
-}
-
-#[proc_macro]
-pub fn emit_input(input: TokenStream) -> TokenStream {
- input
-}
// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
-#![feature(proc_macro_hygiene)]
-
#[macro_use]
extern crate test_macros;
extern crate dollar_crate_external;
// aux-build:lifetimes.rs
-#![feature(proc_macro_hygiene)]
-
extern crate lifetimes;
use lifetimes::*;
error: expected type, found `'`
- --> $DIR/lifetimes.rs:9:10
+ --> $DIR/lifetimes.rs:7:10
|
LL | type A = single_quote_alone!();
| ^^^^^^^^^^^^^^^^^^^^^ this macro call doesn't expand to a type
--- /dev/null
+extern {
+ #[derive(Copy)] //~ ERROR `derive` may only be applied to structs, enums and unions
+ fn f();
+}
+
+fn main() {}
--- /dev/null
+error: `derive` may only be applied to structs, enums and unions
+ --> $DIR/macros-in-extern-derive.rs:2:5
+ |
+LL | #[derive(Copy)]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+++ /dev/null
-// run-pass
-// aux-build:test-macros-rpass.rs
-// ignore-wasm32
-
-#![feature(macros_in_extern)]
-
-extern crate test_macros_rpass as test_macros;
-
-use test_macros::{nop_attr, no_output, emit_input};
-
-fn main() {
- assert_eq!(unsafe { rust_get_test_int() }, 1isize);
- assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF);
-}
-
-#[link(name = "rust_test_helpers", kind = "static")]
-extern {
- #[no_output]
- fn some_definitely_unknown_symbol_which_should_be_removed();
-
- #[nop_attr]
- fn rust_get_test_int() -> isize;
-
- emit_input!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
-}
--- /dev/null
+// run-pass
+// aux-build:test-macros.rs
+// ignore-wasm32
+
+#[macro_use]
+extern crate test_macros;
+
+fn main() {
+ assert_eq!(unsafe { rust_get_test_int() }, 1);
+ assert_eq!(unsafe { rust_dbg_extern_identity_u32(0xDEADBEEF) }, 0xDEADBEEF);
+}
+
+#[link(name = "rust_test_helpers", kind = "static")]
+extern {
+ #[empty_attr]
+ fn some_definitely_unknown_symbol_which_should_be_removed();
+
+ #[identity_attr]
+ fn rust_get_test_int() -> isize;
+
+ identity!(fn rust_dbg_extern_identity_u32(arg: u32) -> u32;);
+}
--- /dev/null
+// check-pass
+// aux-build:test-macros.rs
+
+#[macro_use]
+extern crate test_macros;
+
+const C: identity!(u8) = 10;
+
+fn main() {
+ let c: u8 = C;
+}
}
fn main() {
- let _x: identity!(u32) = 3; //~ ERROR: procedural macros cannot be expanded to types
if let identity!(Some(_x)) = Some(3) {}
//~^ ERROR: procedural macros cannot be expanded to patterns
= note: for more information, see https://github.com/rust-lang/rust/issues/54727
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
-error[E0658]: procedural macros cannot be expanded to types
- --> $DIR/proc-macro-gates.rs:53:13
- |
-LL | let _x: identity!(u32) = 3;
- | ^^^^^^^^^^^^^^
- |
- = note: for more information, see https://github.com/rust-lang/rust/issues/54727
- = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
-
error[E0658]: procedural macros cannot be expanded to patterns
- --> $DIR/proc-macro-gates.rs:54:12
+ --> $DIR/proc-macro-gates.rs:53:12
|
LL | if let identity!(Some(_x)) = Some(3) {}
| ^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
error[E0658]: procedural macros cannot be expanded to statements
- --> $DIR/proc-macro-gates.rs:57:5
+ --> $DIR/proc-macro-gates.rs:56:5
|
LL | empty!(struct S;);
| ^^^^^^^^^^^^^^^^^^
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
error[E0658]: procedural macros cannot be expanded to statements
- --> $DIR/proc-macro-gates.rs:58:5
+ --> $DIR/proc-macro-gates.rs:57:5
|
LL | empty!(let _x = 3;);
| ^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
error[E0658]: procedural macros cannot be expanded to expressions
- --> $DIR/proc-macro-gates.rs:60:14
+ --> $DIR/proc-macro-gates.rs:59:14
|
LL | let _x = identity!(3);
| ^^^^^^^^^^^^
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
error[E0658]: procedural macros cannot be expanded to expressions
- --> $DIR/proc-macro-gates.rs:61:15
+ --> $DIR/proc-macro-gates.rs:60:15
|
LL | let _x = [empty!(3)];
| ^^^^^^^^^
= note: for more information, see https://github.com/rust-lang/rust/issues/54727
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
-error: aborting due to 17 previous errors
+error: aborting due to 16 previous errors
For more information about this error, try `rustc --explain E0658`.
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0029`.
+Some errors have detailed explanations: E0029, E0533.
+For more information about an error, try `rustc --explain E0029`.
error[E0423]: expected function, found struct variant `Foo::Variant`
--> $DIR/issue-18252.rs:6:13
|
+LL | Variant { x: usize }
+ | -------------------- `Foo::Variant` defined here
+...
LL | let f = Foo::Variant(42);
| ^^^^^^^^^^^^ did you mean `Foo::Variant { /* fields */ }`?
error[E0423]: expected value, found struct variant `Homura::Madoka`
--> $DIR/issue-19452.rs:10:18
|
+LL | Madoka { age: u32 }
+ | ------------------- `Homura::Madoka` defined here
+...
LL | let homura = Homura::Madoka;
| ^^^^^^^^^^^^^^ did you mean `Homura::Madoka { /* fields */ }`?
error[E0423]: expected value, found struct `Handle`
--> $DIR/issue-39226.rs:11:17
|
+LL | struct Handle {}
+ | ---------------- `Handle` defined here
+...
LL | handle: Handle
| ^^^^^^
| |
error[E0423]: expected function, found struct `Monster`
--> $DIR/issue-6702.rs:7:14
|
-LL | let _m = Monster();
- | ^^^^^^^ did you mean `Monster { /* fields */ }`?
+LL | / struct Monster {
+LL | | damage: isize
+LL | | }
+ | |_- `Monster` defined here
+...
+LL | let _m = Monster();
+ | ^^^^^^^ did you mean `Monster { /* fields */ }`?
error: aborting due to previous error
error[E0423]: expected value, found struct variant `Z::Struct`
--> $DIR/privacy-enum-ctor.rs:29:20
|
-LL | let _: Z = Z::Struct;
- | ^^^^^^^^^ did you mean `Z::Struct { /* fields */ }`?
+LL | / Struct {
+LL | | s: u8,
+LL | | },
+ | |_____________- `Z::Struct` defined here
+...
+LL | let _: Z = Z::Struct;
+ | ^^^^^^^^^ did you mean `Z::Struct { /* fields */ }`?
error[E0423]: expected value, found enum `m::E`
--> $DIR/privacy-enum-ctor.rs:41:16
error[E0423]: expected value, found struct variant `m::E::Struct`
--> $DIR/privacy-enum-ctor.rs:45:16
|
-LL | let _: E = m::E::Struct;
- | ^^^^^^^^^^^^ did you mean `m::E::Struct { /* fields */ }`?
+LL | / Struct {
+LL | | s: u8,
+LL | | },
+ | |_________- `m::E::Struct` defined here
+...
+LL | let _: E = m::E::Struct;
+ | ^^^^^^^^^^^^ did you mean `m::E::Struct { /* fields */ }`?
error[E0423]: expected value, found enum `E`
--> $DIR/privacy-enum-ctor.rs:49:16
error[E0423]: expected value, found struct variant `E::Struct`
--> $DIR/privacy-enum-ctor.rs:53:16
|
-LL | let _: E = E::Struct;
- | ^^^^^^^^^ did you mean `E::Struct { /* fields */ }`?
+LL | / Struct {
+LL | | s: u8,
+LL | | },
+ | |_________- `E::Struct` defined here
+...
+LL | let _: E = E::Struct;
+ | ^^^^^^^^^ did you mean `E::Struct { /* fields */ }`?
error[E0412]: cannot find type `Z` in this scope
--> $DIR/privacy-enum-ctor.rs:57:12
error[E0423]: expected value, found struct variant `m::n::Z::Struct`
--> $DIR/privacy-enum-ctor.rs:64:16
|
-LL | let _: Z = m::n::Z::Struct;
- | ^^^^^^^^^^^^^^^ did you mean `m::n::Z::Struct { /* fields */ }`?
+LL | / Struct {
+LL | | s: u8,
+LL | | },
+ | |_____________- `m::n::Z::Struct` defined here
+...
+LL | let _: Z = m::n::Z::Struct;
+ | ^^^^^^^^^^^^^^^ did you mean `m::n::Z::Struct { /* fields */ }`?
error[E0412]: cannot find type `Z` in this scope
--> $DIR/privacy-enum-ctor.rs:68:12
error[E0423]: expected value, found struct `S2`
--> $DIR/privacy-struct-ctor.rs:38:5
|
-LL | S2;
- | ^^ did you mean `S2 { /* fields */ }`?
+LL | / pub struct S2 {
+LL | | s: u8
+LL | | }
+ | |_____- `S2` defined here
+...
+LL | S2;
+ | ^^ did you mean `S2 { /* fields */ }`?
error[E0423]: expected value, found struct `xcrate::S`
--> $DIR/privacy-struct-ctor.rs:43:5
--- /dev/null
+#[cfg(FALSE)]
+impl S {
+ fn f(#[attr]) {} //~ ERROR expected parameter name, found `)`
+}
+
+#[cfg(FALSE)]
+impl T for S {
+ fn f(#[attr]) {} //~ ERROR expected parameter name, found `)`
+}
+
+#[cfg(FALSE)]
+trait T {
+ fn f(#[attr]); //~ ERROR expected argument name, found `)`
+}
+
+fn main() {}
--- /dev/null
+error: expected parameter name, found `)`
+ --> $DIR/attr-without-param.rs:3:17
+ |
+LL | fn f(#[attr]) {}
+ | ^ expected parameter name
+
+error: expected parameter name, found `)`
+ --> $DIR/attr-without-param.rs:8:17
+ |
+LL | fn f(#[attr]) {}
+ | ^ expected parameter name
+
+error: expected argument name, found `)`
+ --> $DIR/attr-without-param.rs:13:17
+ |
+LL | fn f(#[attr]);
+ | ^ expected argument name
+
+error: aborting due to 3 previous errors
+
($attr_name:ident, $expected:literal) => {
#[proc_macro_attribute]
pub fn $attr_name(attr: TokenStream, input: TokenStream) -> TokenStream {
- assert!(attr.to_string().is_empty());
assert_eq!(input.to_string(), $expected);
TokenStream::new()
}
checker!(attr_inherent_2, "fn inherent2(#[a1] &self, #[a2] arg1: u8) { }");
checker!(attr_inherent_3, "fn inherent3<'a>(#[a1] &'a mut self, #[a2] arg1: u8) { }");
checker!(attr_inherent_4, "fn inherent4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8) { }");
+checker!(attr_inherent_issue_64682, "fn inherent5(#[a1] #[a2] arg1: u8, #[a3] arg2: u8) { }");
checker!(attr_trait_1, "fn trait1(#[a1] self, #[a2] arg1: u8);");
checker!(attr_trait_2, "fn trait2(#[a1] &self, #[a2] arg1: u8);");
checker!(attr_trait_3, "fn trait3<'a>(#[a1] &'a mut self, #[a2] arg1: u8);");
checker!(attr_trait_4, "fn trait4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8, #[a3] Vec<u8>);");
+checker!(attr_trait_issue_64682, "fn trait5(#[a1] #[a2] arg1: u8, #[a3] arg2: u8);");
+checker!(rename_params, r#"impl Foo {
+ fn hello(#[angery(true)] a: i32, #[a2] b: i32, #[what = "how"] c: u32) { }
+ fn hello2(#[a1] #[a2] a: i32, #[what = "how"] b: i32,
+ #[angery(true)] c: u32) {
+ }
+ fn hello_self(#[a1] #[a2] &self, #[a1] #[a2] a: i32,
+ #[what = "how"] b: i32, #[angery(true)] c: u32) {
+ }
+}"#);
--- /dev/null
+// aux-build:param-attrs.rs
+
+// check-pass
+
+extern crate param_attrs;
+
+use param_attrs::rename_params;
+
+#[rename_params(send_help)]
+impl Foo {
+ fn hello(#[angery(true)] a: i32, #[a2] b: i32, #[what = "how"] c: u32) {}
+ fn hello2(#[a1] #[a2] a: i32, #[what = "how"] b: i32, #[angery(true)] c: u32) {}
+ fn hello_self(
+ #[a1] #[a2] &self,
+ #[a1] #[a2] a: i32,
+ #[what = "how"] b: i32,
+ #[angery(true)] c: u32
+ ) {}
+}
+
+fn main() {}
#[no_mangle] b: i32,
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
) {}
+
+ fn issue_64682_associated_fn(
+ /// Foo
+ //~^ ERROR documentation comments cannot be applied to function
+ #[test] a: i32,
+ //~^ ERROR expected an inert attribute, found an attribute macro
+ /// Baz
+ //~^ ERROR documentation comments cannot be applied to function
+ #[must_use]
+ //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+ /// Qux
+ //~^ ERROR documentation comments cannot be applied to function
+ #[no_mangle] b: i32,
+ //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+ ) {}
}
struct RefStruct {}
#[no_mangle] b: i32,
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
) {}
+
+ fn issue_64682_associated_fn(
+ /// Foo
+ //~^ ERROR documentation comments cannot be applied to function
+ #[test] a: i32,
+ //~^ ERROR expected an inert attribute, found an attribute macro
+ /// Baz
+ //~^ ERROR documentation comments cannot be applied to function
+ #[must_use]
+ //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+ /// Qux
+ //~^ ERROR documentation comments cannot be applied to function
+ #[no_mangle] b: i32,
+ //~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
+ ) {}
}
+
impl RefTrait for RefStruct {
fn foo(
/// Foo
| ^^^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/param-attrs-builtin-attrs.rs:77:9
+ --> $DIR/param-attrs-builtin-attrs.rs:71:9
|
LL | #[test] a: i32,
| ^^^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/param-attrs-builtin-attrs.rs:96:9
+ --> $DIR/param-attrs-builtin-attrs.rs:92:9
|
LL | #[test] a: i32,
| ^^^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/param-attrs-builtin-attrs.rs:115:9
+ --> $DIR/param-attrs-builtin-attrs.rs:111:9
|
LL | #[test] a: i32,
| ^^^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/param-attrs-builtin-attrs.rs:132:9
+ --> $DIR/param-attrs-builtin-attrs.rs:126:9
+ |
+LL | #[test] a: i32,
+ | ^^^^^^^
+
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/param-attrs-builtin-attrs.rs:146:9
+ |
+LL | #[test] a: i32,
+ | ^^^^^^^
+
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/param-attrs-builtin-attrs.rs:163:9
|
LL | #[test] a: u32,
| ^^^^^^^
| ^^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:72:9
+ --> $DIR/param-attrs-builtin-attrs.rs:69:9
|
LL | /// Foo
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:73:9
+ |
+LL | /// Baz
+ | ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:75:9
|
+LL | #[must_use]
+ | ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:77:9
+ |
+LL | /// Qux
+ | ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:79:9
+ |
+LL | #[no_mangle] b: i32,
+ | ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:87:9
+ |
+LL | /// Foo
+ | ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:90:9
+ |
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:79:9
+ --> $DIR/param-attrs-builtin-attrs.rs:94:9
|
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:81:9
+ --> $DIR/param-attrs-builtin-attrs.rs:96:9
|
LL | #[must_use]
| ^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:83:9
+ --> $DIR/param-attrs-builtin-attrs.rs:98:9
|
LL | /// Qux
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:85:9
+ --> $DIR/param-attrs-builtin-attrs.rs:100:9
|
LL | #[no_mangle] b: i32,
| ^^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:91:9
+ --> $DIR/param-attrs-builtin-attrs.rs:106:9
|
LL | /// Foo
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:94:9
+ --> $DIR/param-attrs-builtin-attrs.rs:109:9
|
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:98:9
+ --> $DIR/param-attrs-builtin-attrs.rs:113:9
|
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:100:9
+ --> $DIR/param-attrs-builtin-attrs.rs:115:9
|
LL | #[must_use]
| ^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:102:9
+ --> $DIR/param-attrs-builtin-attrs.rs:117:9
|
LL | /// Qux
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:104:9
+ --> $DIR/param-attrs-builtin-attrs.rs:119:9
|
LL | #[no_mangle] b: i32,
| ^^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:110:9
+ --> $DIR/param-attrs-builtin-attrs.rs:124:9
|
LL | /// Foo
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:113:9
+ --> $DIR/param-attrs-builtin-attrs.rs:128:9
+ |
+LL | /// Baz
+ | ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:130:9
+ |
+LL | #[must_use]
+ | ^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:132:9
+ |
+LL | /// Qux
+ | ^^^^^^^ doc comments are not allowed here
+
+error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:134:9
+ |
+LL | #[no_mangle] b: i32,
+ | ^^^^^^^^^^^^
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:141:9
+ |
+LL | /// Foo
+ | ^^^^^^^ doc comments are not allowed here
+
+error: documentation comments cannot be applied to function parameters
+ --> $DIR/param-attrs-builtin-attrs.rs:144:9
|
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:117:9
+ --> $DIR/param-attrs-builtin-attrs.rs:148:9
|
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:119:9
+ --> $DIR/param-attrs-builtin-attrs.rs:150:9
|
LL | #[must_use]
| ^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:121:9
+ --> $DIR/param-attrs-builtin-attrs.rs:152:9
|
LL | /// Qux
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:123:9
+ --> $DIR/param-attrs-builtin-attrs.rs:154:9
|
LL | #[no_mangle] b: i32,
| ^^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:130:9
+ --> $DIR/param-attrs-builtin-attrs.rs:161:9
|
LL | /// Foo
| ^^^^^^^ doc comments are not allowed here
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:134:9
+ --> $DIR/param-attrs-builtin-attrs.rs:165:9
|
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:136:9
+ --> $DIR/param-attrs-builtin-attrs.rs:167:9
|
LL | #[must_use]
| ^^^^^^^^^^^
error: documentation comments cannot be applied to function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:138:9
+ --> $DIR/param-attrs-builtin-attrs.rs:169:9
|
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here
error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
- --> $DIR/param-attrs-builtin-attrs.rs:140:9
+ --> $DIR/param-attrs-builtin-attrs.rs:171:9
|
LL | #[no_mangle] b: i32
| ^^^^^^^^^^^^
-error: aborting due to 52 previous errors
+error: aborting due to 64 previous errors
//~^ ERROR unused variable: `c`
#[cfg_attr(something, cfg(nothing))] d: i32,
) {}
+ fn issue_64682_associated_fn(
+ #[cfg(nothing)] a: i32,
+ #[cfg(something)] b: i32,
+ //~^ ERROR unused variable: `b`
+ #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ //~^ ERROR unused variable: `c`
+ #[cfg_attr(something, cfg(nothing))] d: i32,
+ ) {}
}
trait RefTrait {
fn bar(
//~^ ERROR unused variable: `c`
#[cfg_attr(something, cfg(nothing))] d: i32,
) {}
+ fn issue_64682_associated_fn(
+ #[cfg(nothing)] a: i32,
+ #[cfg(something)] b: i32,
+ //~^ ERROR unused variable: `b`
+ #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ //~^ ERROR unused variable: `c`
+ #[cfg_attr(something, cfg(nothing))] d: i32,
+ ) {}
}
impl RefTrait for RefStruct {
fn bar(
//~^ ERROR unused variable: `c`
#[cfg_attr(something, cfg(nothing))] d: i32,
) {}
+ fn issue_64682_associated_fn(
+ #[cfg(nothing)] a: i32,
+ #[cfg(something)] b: i32,
+ //~^ ERROR unused variable: `b`
+ #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ //~^ ERROR unused variable: `c`
+ #[cfg_attr(something, cfg(nothing))] d: i32,
+ ) {}
}
fn main() {
| ^ help: consider prefixing with an underscore: `_c`
error: unused variable: `a`
- --> $DIR/param-attrs-cfg.rs:83:27
+ --> $DIR/param-attrs-cfg.rs:107:27
|
LL | #[cfg(something)] a: i32,
| ^ help: consider prefixing with an underscore: `_a`
error: unused variable: `b`
- --> $DIR/param-attrs-cfg.rs:89:27
+ --> $DIR/param-attrs-cfg.rs:113:27
|
LL | #[cfg(something)] b: i32,
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `c`
- --> $DIR/param-attrs-cfg.rs:91:44
+ --> $DIR/param-attrs-cfg.rs:115:44
|
LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
| ^ help: consider prefixing with an underscore: `_c`
error: unused variable: `b`
- --> $DIR/param-attrs-cfg.rs:59:27
+ --> $DIR/param-attrs-cfg.rs:67:27
|
LL | #[cfg(something)] b: i32,
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `c`
- --> $DIR/param-attrs-cfg.rs:61:44
+ --> $DIR/param-attrs-cfg.rs:69:44
+ |
+LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ | ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+ --> $DIR/param-attrs-cfg.rs:75:27
+ |
+LL | #[cfg(something)] b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+ --> $DIR/param-attrs-cfg.rs:77:44
|
LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
| ^ help: consider prefixing with an underscore: `_c`
| ^ help: consider prefixing with an underscore: `_c`
error: unused variable: `b`
- --> $DIR/param-attrs-cfg.rs:70:27
+ --> $DIR/param-attrs-cfg.rs:56:27
+ |
+LL | #[cfg(something)] b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+ --> $DIR/param-attrs-cfg.rs:58:44
+ |
+LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ | ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+ --> $DIR/param-attrs-cfg.rs:86:27
+ |
+LL | #[cfg(something)] b: i32,
+ | ^ help: consider prefixing with an underscore: `_b`
+
+error: unused variable: `c`
+ --> $DIR/param-attrs-cfg.rs:88:44
+ |
+LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
+ | ^ help: consider prefixing with an underscore: `_c`
+
+error: unused variable: `b`
+ --> $DIR/param-attrs-cfg.rs:94:27
|
LL | #[cfg(something)] b: i32,
| ^ help: consider prefixing with an underscore: `_b`
error: unused variable: `c`
- --> $DIR/param-attrs-cfg.rs:72:44
+ --> $DIR/param-attrs-cfg.rs:96:44
|
LL | #[cfg_attr(nothing, cfg(nothing))] c: i32,
| ^ help: consider prefixing with an underscore: `_c`
-error: aborting due to 13 previous errors
+error: aborting due to 19 previous errors
#[attr_inherent_4]
fn inherent4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8) {}
+
+ #[attr_inherent_issue_64682]
+ fn inherent5(#[a1] #[a2] arg1: u8, #[a3] arg2: u8) {}
}
trait A {
#[attr_trait_4]
fn trait4<'a>(#[a1] self: Box<Self>, #[a2] arg1: u8, #[a3] Vec<u8>);
+
+ #[attr_trait_issue_64682]
+ fn trait5(#[a1] #[a2] arg1: u8, #[a3] arg2: u8);
}
fn main() {}
fn inherent4<'a>(#[id] self: Box<Self>, #[id] arg1: u8) {}
//~^ ERROR expected an inert attribute, found an attribute macro
//~| ERROR expected an inert attribute, found an attribute macro
+ fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {}
+ //~^ ERROR expected an inert attribute, found an attribute macro
+ //~| ERROR expected an inert attribute, found an attribute macro
}
trait A {
//~^ ERROR expected an inert attribute, found an attribute macro
//~| ERROR expected an inert attribute, found an attribute macro
//~| ERROR expected an inert attribute, found an attribute macro
+ fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8);
+ //~^ ERROR expected an inert attribute, found an attribute macro
+ //~| ERROR expected an inert attribute, found an attribute macro
}
fn main() {}
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:44:15
+ --> $DIR/proc-macro-cannot-be-used.rs:41:38
+ |
+LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {}
+ | ^^^^^
+
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/proc-macro-cannot-be-used.rs:41:54
+ |
+LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8) {}
+ | ^^^^^
+
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/proc-macro-cannot-be-used.rs:47:15
|
LL | fn trait1(#[id] self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:44:27
+ --> $DIR/proc-macro-cannot-be-used.rs:47:27
|
LL | fn trait1(#[id] self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:47:15
+ --> $DIR/proc-macro-cannot-be-used.rs:50:15
|
LL | fn trait2(#[id] &self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:47:28
+ --> $DIR/proc-macro-cannot-be-used.rs:50:28
|
LL | fn trait2(#[id] &self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:50:19
+ --> $DIR/proc-macro-cannot-be-used.rs:53:19
|
LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:50:39
+ --> $DIR/proc-macro-cannot-be-used.rs:53:39
|
LL | fn trait3<'a>(#[id] &'a mut self, #[id] arg1: u8);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:53:19
+ --> $DIR/proc-macro-cannot-be-used.rs:56:19
|
LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:53:42
+ --> $DIR/proc-macro-cannot-be-used.rs:56:42
|
LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>);
| ^^^^^
error: expected an inert attribute, found an attribute macro
- --> $DIR/proc-macro-cannot-be-used.rs:53:58
+ --> $DIR/proc-macro-cannot-be-used.rs:56:58
|
LL | fn trait4<'a>(#[id] self: Box<Self>, #[id] arg1: u8, #[id] Vec<u8>);
| ^^^^^
-error: aborting due to 25 previous errors
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/proc-macro-cannot-be-used.rs:60:38
+ |
+LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8);
+ | ^^^^^
+
+error: expected an inert attribute, found an attribute macro
+ --> $DIR/proc-macro-cannot-be-used.rs:60:54
+ |
+LL | fn issue_64682_associated_fn<'a>(#[id] arg1: u8, #[id] arg2: u8);
+ | ^^^^^
+
+error: aborting due to 29 previous errors
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0493`.
error: aborting due to 10 previous errors
-For more information about this error, try `rustc --explain E0716`.
+Some errors have detailed explanations: E0493, E0716.
+For more information about an error, try `rustc --explain E0493`.
--> $DIR/struct-literal-variant-in-if.rs:10:20
|
LL | if x == E::V { field } {}
- | ^^^^^ expected (), found bool
+ | ---------------^^^^^--- help: consider using a semicolon here
+ | | |
+ | | expected (), found bool
+ | expected this to be `()`
|
= note: expected type `()`
found type `bool`
error[E0423]: expected value, found struct variant `E::B`
--> $DIR/fn-or-tuple-struct-without-args.rs:36:16
|
+LL | B { a: usize },
+ | -------------- `E::B` defined here
+...
LL | let _: E = E::B;
| ^^^-
| | |
error[E0423]: expected value, found struct `X`
--> $DIR/issue-61226.rs:3:10
|
+LL | struct X {}
+ | ----------- `X` defined here
+LL | fn main() {
LL | vec![X]; //…
| ^ did you mean `X { /* fields */ }`?
--- /dev/null
+// check-only
+// run-rustfix
+
+fn main() {
+ match 3 {
+ 4 => 1,
+ 3 => {
+ 2 //~ ERROR mismatched types
+ }
+ _ => 2
+ };
+ match 3 { //~ ERROR mismatched types
+ 4 => 1,
+ 3 => 2,
+ _ => 2
+ };
+ let _ = ();
+}
--- /dev/null
+// check-only
+// run-rustfix
+
+fn main() {
+ match 3 {
+ 4 => 1,
+ 3 => {
+ 2 //~ ERROR mismatched types
+ }
+ _ => 2
+ }
+ match 3 { //~ ERROR mismatched types
+ 4 => 1,
+ 3 => 2,
+ _ => 2
+ }
+ let _ = ();
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/match-needing-semi.rs:8:13
+ |
+LL | / match 3 {
+LL | | 4 => 1,
+LL | | 3 => {
+LL | | 2
+ | | ^ expected (), found integer
+LL | | }
+LL | | _ => 2
+LL | | }
+ | | -- help: consider using a semicolon here
+ | |_____|
+ | expected this to be `()`
+ |
+ = note: expected type `()`
+ found type `{integer}`
+
+error[E0308]: mismatched types
+ --> $DIR/match-needing-semi.rs:12:5
+ |
+LL | / match 3 {
+LL | | 4 => 1,
+LL | | 3 => 2,
+LL | | _ => 2
+LL | | }
+ | | ^- help: consider using a semicolon here
+ | |_____|
+ | expected (), found integer
+ |
+ = note: expected type `()`
+ found type `{integer}`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// error-pattern:building tests with panic=abort is not yet supported
+// no-prefer-dynamic
+// compile-flags: --test -Cpanic=abort
+// run-flags: --test-threads=1
+
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+
+#![cfg(test)]
+
+#[test]
+fn it_works() {
+ assert_eq!(1 + 1, 2);
+}
+
+#[test]
+#[should_panic]
+fn it_panics() {
+ assert_eq!(1 + 1, 4);
+}
--- /dev/null
+error: building tests with panic=abort is not yet supported
+
+error: aborting due to previous error
+
--- /dev/null
+// no-prefer-dynamic
+// compile-flags: --test -Cpanic=abort -Zpanic_abort_tests
+// run-flags: --test-threads=1
+// run-fail
+// check-run-results
+
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+
+#![cfg(test)]
+
+use std::io::Write;
+
+#[test]
+fn it_works() {
+ assert_eq!(1 + 1, 2);
+}
+
+#[test]
+#[should_panic]
+fn it_panics() {
+ assert_eq!(1 + 1, 4);
+}
+
+#[test]
+fn it_fails() {
+ println!("hello, world");
+ writeln!(std::io::stdout(), "testing123").unwrap();
+ writeln!(std::io::stderr(), "testing321").unwrap();
+ assert_eq!(1 + 1, 5);
+}
+
+#[test]
+fn it_exits() {
+ std::process::exit(123);
+}
--- /dev/null
+
+running 4 tests
+test it_exits ... FAILED
+test it_fails ... FAILED
+test it_panics ... ok
+test it_works ... ok
+
+failures:
+
+---- it_exits stdout ----
+---- it_exits stderr ----
+note: got unexpected return code 123
+---- it_fails stdout ----
+hello, world
+testing123
+---- it_fails stderr ----
+testing321
+thread 'main' panicked at 'assertion failed: `(left == right)`
+ left: `2`,
+ right: `5`', $DIR/test-panic-abort.rs:30:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
+
+
+failures:
+ it_exits
+ it_fails
+
+test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
+
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0533`.
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0164, E0618.
+Some errors have detailed explanations: E0164, E0533, E0618.
For more information about an error, try `rustc --explain E0164`.
Check,
Build,
Run,
+ RunFail,
}
impl FromStr for PassMode {
PassMode::Check => "check",
PassMode::Build => "build",
PassMode::Run => "run",
+ PassMode::RunFail => "run-fail",
};
fmt::Display::fmt(s, f)
}
panic!("`run-pass` header is only supported in UI tests")
}
Some(PassMode::Run)
+ } else if config.parse_name_directive(ln, "run-fail") {
+ if config.mode != Mode::Ui {
+ panic!("`run-fail` header is only supported in UI tests")
+ }
+ Some(PassMode::RunFail)
} else {
None
};
self.props.pass_mode(self.config)
}
+ fn should_run(&self) -> bool {
+ let pass_mode = self.pass_mode();
+ match self.config.mode {
+ Ui => pass_mode == Some(PassMode::Run) || pass_mode == Some(PassMode::RunFail),
+ mode => panic!("unimplemented for mode {:?}", mode),
+ }
+ }
+
fn should_run_successfully(&self) -> bool {
let pass_mode = self.pass_mode();
match self.config.mode {
fn compile_test(&self) -> ProcRes {
// Only use `make_exe_name` when the test ends up being executed.
let will_execute = match self.config.mode {
- Ui => self.should_run_successfully(),
+ Ui => self.should_run(),
Incremental => self.revision.unwrap().starts_with("r"),
RunFail | RunPassValgrind | MirOpt |
DebugInfoCdb | DebugInfoGdbLldb | DebugInfoGdb | DebugInfoLldb => true,
let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
- if self.should_run_successfully() {
+ if self.should_run() {
let proc_res = self.exec_compiled_test();
let run_output_errors = if self.props.check_run_results {
self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
&proc_res,
);
}
- if !proc_res.status.success() {
- self.fatal_proc_rec("test run failed!", &proc_res);
+ if self.should_run_successfully() {
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ } else {
+ if proc_res.status.success() {
+ self.fatal_proc_rec("test run succeeded!", &proc_res);
+ }
}
}
-Subproject commit 130f9488d3b861e02c9282b686eec717e30912cf
+Subproject commit 07ac10277ea5ad42efbb914da5844e0ab08efbf4