Rollup merge of #68422 - Centril:diverges-simplify, r=eddyb
typeck: simplify the handling of `diverges`
Some drive-by cleanup while working on `hir::ExprKind::Let`.
Ostensibly, this has some perf benefits due to reduced allocation and whatnot as well.
r? @eddyb
diff --git a/Cargo.lock b/Cargo.lock
index 48bc269..0a76aab 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -307,7 +307,7 @@
"glob",
"hex 0.4.0",
"home",
- "humantime",
+ "humantime 2.0.0",
"ignore",
"im-rc",
"jobserver",
@@ -1013,7 +1013,7 @@
checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
dependencies = [
"atty",
- "humantime",
+ "humantime 1.3.0",
"log",
"regex",
"termcolor",
@@ -1026,7 +1026,7 @@
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
- "humantime",
+ "humantime 1.3.0",
"log",
"regex",
"termcolor",
@@ -1455,6 +1455,12 @@
]
[[package]]
+name = "humantime"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9b6c53306532d3c8e8087b44e6580e10db51a023cf9b433cea2ac38066b92da"
+
+[[package]]
name = "hyper"
version = "0.12.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2647,7 +2653,7 @@
"clap",
"derive_more",
"env_logger 0.6.2",
- "humantime",
+ "humantime 1.3.0",
"lazy_static 1.3.0",
"log",
"rls-span",
@@ -3763,6 +3769,7 @@
"rustc_hir",
"rustc_index",
"rustc_macros",
+ "rustc_session",
"rustc_span",
"rustc_target",
"serialize",
diff --git a/src/doc/book b/src/doc/book
index 5c5cfd2..87dd684 160000
--- a/src/doc/book
+++ b/src/doc/book
@@ -1 +1 @@
-Subproject commit 5c5cfd2e94cd42632798d9bd3d1116133e128ac9
+Subproject commit 87dd6843678575f8dda962f239d14ef4be14b352
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
index 9493b7d..4d78994 160000
--- a/src/doc/embedded-book
+++ b/src/doc/embedded-book
@@ -1 +1 @@
-Subproject commit 9493b7d4dc97eda439bd8780f05ad7b234cd1cd7
+Subproject commit 4d78994915af1bde9a95c04a8c27d8dca066232a
diff --git a/src/doc/reference b/src/doc/reference
index e115753..11e893f 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit e1157538e86d83df0cf95d5e33bd943f80d0248f
+Subproject commit 11e893fc1357bc688418ddf1087c2b7aa25d154d
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index 1d59403..1c2bd02 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit 1d59403cb5269c190cc52a95584ecc280345495a
+Subproject commit 1c2bd024d13f8011307e13386cf1fea2180352b5
diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs
index 302c2bc..e70f881 100644
--- a/src/liballoc/collections/btree/map.rs
+++ b/src/liballoc/collections/btree/map.rs
@@ -1968,7 +1968,7 @@
(i, false) => i,
},
(_, Unbounded) => 0,
- (true, Included(_)) => min_node.keys().len(),
+ (true, Included(_)) => min_node.len(),
(true, Excluded(_)) => 0,
};
@@ -1987,9 +1987,9 @@
}
(i, false) => i,
},
- (_, Unbounded) => max_node.keys().len(),
+ (_, Unbounded) => max_node.len(),
(true, Included(_)) => 0,
- (true, Excluded(_)) => max_node.keys().len(),
+ (true, Excluded(_)) => max_node.len(),
};
if !diverged {
diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs
index f40e0b0..d9cdebb 100644
--- a/src/liballoc/collections/btree/node.rs
+++ b/src/liballoc/collections/btree/node.rs
@@ -54,10 +54,8 @@
/// `NodeHeader` because we do not want unnecessary padding between `len` and the keys.
/// Crucially, `NodeHeader` can be safely transmuted to different K and V. (This is exploited
/// by `as_header`.)
-/// See `into_key_slice` for an explanation of K2. K2 cannot be safely transmuted around
-/// because the size of `NodeHeader` depends on its alignment!
#[repr(C)]
-struct NodeHeader<K, V, K2 = ()> {
+struct NodeHeader<K, V> {
/// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`.
/// This either points to an actual node or is null.
parent: *const InternalNode<K, V>,
@@ -72,9 +70,6 @@
/// This next to `parent_idx` to encourage the compiler to join `len` and
/// `parent_idx` into the same 32-bit word, reducing space overhead.
len: u16,
-
- /// See `into_key_slice`.
- keys_start: [K2; 0],
}
#[repr(C)]
struct LeafNode<K, V> {
@@ -128,7 +123,7 @@
// We use just a header in order to save space, since no operation on an empty tree will
// ever take a pointer past the first key.
static EMPTY_ROOT_NODE: NodeHeader<(), ()> =
- NodeHeader { parent: ptr::null(), parent_idx: MaybeUninit::uninit(), len: 0, keys_start: [] };
+ NodeHeader { parent: ptr::null(), parent_idx: MaybeUninit::uninit(), len: 0 };
/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden
/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an
@@ -390,14 +385,13 @@
}
/// Borrows a view into the keys stored in the node.
- /// Works on all possible nodes, including the shared root.
- pub fn keys(&self) -> &[K] {
+ /// The caller must ensure that the node is not the shared root.
+ pub unsafe fn keys(&self) -> &[K] {
self.reborrow().into_key_slice()
}
/// Borrows a view into the values stored in the node.
/// The caller must ensure that the node is not the shared root.
- /// This function is not public, so doesn't have to support shared roots like `keys` does.
fn vals(&self) -> &[V] {
self.reborrow().into_val_slice()
}
@@ -515,7 +509,6 @@
}
/// The caller must ensure that the node is not the shared root.
- /// This function is not public, so doesn't have to support shared roots like `keys` does.
fn keys_mut(&mut self) -> &mut [K] {
unsafe { self.reborrow_mut().into_key_slice_mut() }
}
@@ -527,48 +520,11 @@
}
impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
- fn into_key_slice(self) -> &'a [K] {
- // We have to be careful here because we might be pointing to the shared root.
- // In that case, we must not create an `&LeafNode`. We could just return
- // an empty slice whenever the length is 0 (this includes the shared root),
- // but we want to avoid that run-time check.
- // Instead, we create a slice pointing into the node whenever possible.
- // We can sometimes do this even for the shared root, as the slice will be
- // empty and `NodeHeader` contains an empty `keys_start` array.
- // We cannot *always* do this because:
- // - `keys_start` is not correctly typed because we want `NodeHeader`'s size to
- // not depend on the alignment of `K` (needed because `as_header` should be safe).
- // For this reason, `NodeHeader` has this `K2` parameter (that's usually `()`
- // and hence just adds a size-0-align-1 field, not affecting layout).
- // If the correctly typed header is more highly aligned than the allocated header,
- // we cannot transmute safely.
- // - Even if we can transmute, the offset of a correctly typed `keys_start` might
- // be different and outside the bounds of the allocated header!
- // So we do an alignment check and a size check first, that will be evaluated
- // at compile-time, and only do any run-time check in the rare case that
- // the compile-time checks signal danger.
- if (mem::align_of::<NodeHeader<K, V, K>>() > mem::align_of::<NodeHeader<K, V>>()
- || mem::size_of::<NodeHeader<K, V, K>>() != mem::size_of::<NodeHeader<K, V>>())
- && self.is_shared_root()
- {
- &[]
- } else {
- // If we are a `LeafNode<K, V>`, we can always transmute to
- // `NodeHeader<K, V, K>` and `keys_start` always has the same offset
- // as the actual `keys`.
- // Thanks to the checks above, we know that we can transmute to
- // `NodeHeader<K, V, K>` and that `keys_start` will be
- // in-bounds of some allocation even if this is the shared root!
- // (We might be one-past-the-end, but that is allowed by LLVM.)
- // Thus we can use `NodeHeader<K, V, K>`
- // to compute the pointer where the keys start.
- // This entire hack will become unnecessary once
- // <https://github.com/rust-lang/rfcs/pull/2582> lands, then we can just take a raw
- // pointer to the `keys` field of `*const InternalNode<K, V>`.
- let header = self.as_header() as *const _ as *const NodeHeader<K, V, K>;
- let keys = unsafe { &(*header).keys_start as *const _ as *const K };
- unsafe { slice::from_raw_parts(keys, self.len()) }
- }
+ /// The caller must ensure that the node is not the shared root.
+ unsafe fn into_key_slice(self) -> &'a [K] {
+ debug_assert!(!self.is_shared_root());
+ // We cannot be the shared root, so `as_leaf` is okay.
+ slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().keys), self.len())
}
/// The caller must ensure that the node is not the shared root.
@@ -578,9 +534,10 @@
unsafe { slice::from_raw_parts(MaybeUninit::first_ptr(&self.as_leaf().vals), self.len()) }
}
+ /// The caller must ensure that the node is not the shared root.
fn into_slices(self) -> (&'a [K], &'a [V]) {
let k = unsafe { ptr::read(&self) };
- (k.into_key_slice(), self.into_val_slice())
+ (unsafe { k.into_key_slice() }, self.into_val_slice())
}
}
diff --git a/src/liballoc/collections/btree/search.rs b/src/liballoc/collections/btree/search.rs
index 48cbf67..579624c 100644
--- a/src/liballoc/collections/btree/search.rs
+++ b/src/liballoc/collections/btree/search.rs
@@ -61,16 +61,18 @@
{
// This function is defined over all borrow types (immutable, mutable, owned),
// and may be called on the shared root in each case.
- // Crucially, we use `keys()` here, i.e., we work with immutable data.
- // `keys_mut()` does not support the shared root, so we cannot use it.
// Using `keys()` is fine here even if BorrowType is mutable, as all we return
// is an index -- not a reference.
- for (i, k) in node.keys().iter().enumerate() {
- match key.cmp(k.borrow()) {
- Ordering::Greater => {}
- Ordering::Equal => return (i, true),
- Ordering::Less => return (i, false),
+ let len = node.len();
+ if len > 0 {
+ let keys = unsafe { node.keys() }; // safe because a non-empty node cannot be the shared root
+ for (i, k) in keys.iter().enumerate() {
+ match key.cmp(k.borrow()) {
+ Ordering::Greater => {}
+ Ordering::Equal => return (i, true),
+ Ordering::Less => return (i, false),
+ }
}
}
- (node.keys().len(), false)
+ (len, false)
}
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index ccd2a96..3a7c650 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -215,6 +215,31 @@
}
}
+ /// Returns a partially initialized MIR body containing only a list of basic blocks.
+ ///
+ /// The returned MIR contains no `LocalDecl`s (even for the return place) or source scopes. It
+ /// is only useful for testing but cannot be `#[cfg(test)]` because it is used in a different
+ /// crate.
+ pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
+ Body {
+ phase: MirPhase::Build,
+ basic_blocks,
+ source_scopes: IndexVec::new(),
+ yield_ty: None,
+ generator_drop: None,
+ generator_layout: None,
+ local_decls: IndexVec::new(),
+ user_type_annotations: IndexVec::new(),
+ arg_count: 0,
+ spread_arg: None,
+ span: DUMMY_SP,
+ control_flow_destroyed: Vec::new(),
+ generator_kind: None,
+ var_debug_info: Vec::new(),
+ ignore_interior_mut_in_const_validation: false,
+ }
+ }
+
#[inline]
pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&self.basic_blocks
diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs
index 89b28ae..c97c5c2 100644
--- a/src/librustc/traits/auto_trait.rs
+++ b/src/librustc/traits/auto_trait.rs
@@ -337,7 +337,10 @@
&Err(SelectionError::Unimplemented) => {
if self.is_param_no_infer(pred.skip_binder().trait_ref.substs) {
already_visited.remove(&pred);
- self.add_user_pred(&mut user_computed_preds, ty::Predicate::Trait(pred));
+ self.add_user_pred(
+ &mut user_computed_preds,
+ ty::Predicate::Trait(pred, ast::Constness::NotConst),
+ );
predicates.push_back(pred);
} else {
debug!(
@@ -405,7 +408,7 @@
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
match (&new_pred, old_pred) {
- (&ty::Predicate::Trait(new_trait), ty::Predicate::Trait(old_trait)) => {
+ (&ty::Predicate::Trait(new_trait, _), ty::Predicate::Trait(old_trait, _)) => {
if new_trait.def_id() == old_trait.def_id() {
let new_substs = new_trait.skip_binder().trait_ref.substs;
let old_substs = old_trait.skip_binder().trait_ref.substs;
@@ -627,7 +630,7 @@
// We check this by calling is_of_param on the relevant types
// from the various possible predicates
match &predicate {
- &ty::Predicate::Trait(p) => {
+ &ty::Predicate::Trait(p, _) => {
if self.is_param_no_infer(p.skip_binder().trait_ref.substs)
&& !only_projections
&& is_new_pred
diff --git a/src/librustc/traits/engine.rs b/src/librustc/traits/engine.rs
index 5b80448..84bfc86 100644
--- a/src/librustc/traits/engine.rs
+++ b/src/librustc/traits/engine.rs
@@ -1,6 +1,6 @@
use crate::infer::InferCtxt;
use crate::traits::Obligation;
-use crate::ty::{self, ToPredicate, Ty, TyCtxt};
+use crate::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
use rustc_hir::def_id::DefId;
use super::{ChalkFulfillmentContext, FulfillmentContext, FulfillmentError};
@@ -33,7 +33,7 @@
cause,
recursion_depth: 0,
param_env,
- predicate: trait_ref.to_predicate(),
+ predicate: trait_ref.without_const().to_predicate(),
},
);
}
diff --git a/src/librustc/traits/error_reporting/mod.rs b/src/librustc/traits/error_reporting/mod.rs
index 646cb80..d1c369d 100644
--- a/src/librustc/traits/error_reporting/mod.rs
+++ b/src/librustc/traits/error_reporting/mod.rs
@@ -19,7 +19,9 @@
use crate::ty::fast_reject;
use crate::ty::fold::TypeFolder;
use crate::ty::SubtypePredicate;
-use crate::ty::{self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{
+ self, AdtKind, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
+};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
@@ -128,7 +130,7 @@
}
let (cond, error) = match (cond, error) {
- (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error)) => (cond, error),
+ (&ty::Predicate::Trait(..), &ty::Predicate::Trait(ref error, _)) => (cond, error),
_ => {
// FIXME: make this work in other cases too.
return false;
@@ -136,7 +138,7 @@
};
for implication in super::elaborate_predicates(self.tcx, vec![cond.clone()]) {
- if let ty::Predicate::Trait(implication) = implication {
+ if let ty::Predicate::Trait(implication, _) = implication {
let error = error.to_poly_trait_ref();
let implication = implication.to_poly_trait_ref();
// FIXME: I'm just not taking associated types at all here.
@@ -528,7 +530,7 @@
return;
}
match obligation.predicate {
- ty::Predicate::Trait(ref trait_predicate) => {
+ ty::Predicate::Trait(ref trait_predicate, _) => {
let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
if self.tcx.sess.has_errors() && trait_predicate.references_error() {
@@ -581,7 +583,7 @@
"{}",
message.unwrap_or_else(|| format!(
"the trait bound `{}` is not satisfied{}",
- trait_ref.to_predicate(),
+ trait_ref.without_const().to_predicate(),
post_message,
))
);
@@ -693,7 +695,10 @@
trait_pred
});
let unit_obligation = Obligation {
- predicate: ty::Predicate::Trait(predicate),
+ predicate: ty::Predicate::Trait(
+ predicate,
+ ast::Constness::NotConst,
+ ),
..obligation.clone()
};
if self.predicate_may_hold(&unit_obligation) {
@@ -986,7 +991,7 @@
) -> PredicateObligation<'tcx> {
let new_trait_ref =
ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) };
- Obligation::new(cause, param_env, new_trait_ref.to_predicate())
+ Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate())
}
}
@@ -1074,7 +1079,7 @@
}
let mut err = match predicate {
- ty::Predicate::Trait(ref data) => {
+ ty::Predicate::Trait(ref data, _) => {
let trait_ref = data.to_poly_trait_ref();
let self_ty = trait_ref.self_ty();
debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind, trait_ref);
@@ -1267,8 +1272,11 @@
)
.value;
- let obligation =
- Obligation::new(ObligationCause::dummy(), param_env, cleaned_pred.to_predicate());
+ let obligation = Obligation::new(
+ ObligationCause::dummy(),
+ param_env,
+ cleaned_pred.without_const().to_predicate(),
+ );
self.predicate_may_hold(&obligation)
})
diff --git a/src/librustc/traits/error_reporting/suggestions.rs b/src/librustc/traits/error_reporting/suggestions.rs
index c18e175..4559007 100644
--- a/src/librustc/traits/error_reporting/suggestions.rs
+++ b/src/librustc/traits/error_reporting/suggestions.rs
@@ -6,7 +6,7 @@
use crate::infer::InferCtxt;
use crate::traits::object_safety::object_safety_violations;
use crate::ty::TypeckTables;
-use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style,
@@ -48,7 +48,7 @@
} else {
" where"
},
- trait_ref.to_predicate(),
+ trait_ref.without_const().to_predicate(),
),
Applicability::MachineApplicable,
);
@@ -338,8 +338,11 @@
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
- let new_obligation =
- Obligation::new(ObligationCause::dummy(), param_env, new_trait_ref.to_predicate());
+ let new_obligation = Obligation::new(
+ ObligationCause::dummy(),
+ param_env,
+ new_trait_ref.without_const().to_predicate(),
+ );
if self.predicate_must_hold_modulo_regions(&new_obligation) {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
// We have a very specific type of error, where just borrowing this argument
@@ -1120,7 +1123,7 @@
// the type. The last generator has information about where the bound was introduced. At
// least one generator should be present for this diagnostic to be modified.
let (mut trait_ref, mut target_ty) = match obligation.predicate {
- ty::Predicate::Trait(p) => {
+ ty::Predicate::Trait(p, _) => {
(Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty()))
}
_ => (None, None),
@@ -1543,7 +1546,7 @@
err.note(&format!("required because it appears within the type `{}`", ty));
obligated_types.push(ty);
- let parent_predicate = parent_trait_ref.to_predicate();
+ let parent_predicate = parent_trait_ref.without_const().to_predicate();
if !self.is_recursive_obligation(obligated_types, &data.parent_code) {
self.note_obligation_cause_code(
err,
@@ -1560,7 +1563,7 @@
parent_trait_ref.print_only_trait_path(),
parent_trait_ref.skip_binder().self_ty()
));
- let parent_predicate = parent_trait_ref.to_predicate();
+ let parent_predicate = parent_trait_ref.without_const().to_predicate();
self.note_obligation_cause_code(
err,
&parent_predicate,
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 46ece6f..0aac6fb 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -311,7 +311,7 @@
}
match obligation.predicate {
- ty::Predicate::Trait(ref data) => {
+ ty::Predicate::Trait(ref data, _) => {
let trait_obligation = obligation.with(data.clone());
if data.is_global() {
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index e935291..daaba95 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -29,7 +29,7 @@
use crate::ty::error::{ExpectedFound, TypeError};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::subst::{InternalSubsts, SubstsRef};
-use crate::ty::{self, AdtKind, GenericParamDefKind, List, ToPredicate, Ty, TyCtxt};
+use crate::ty::{self, AdtKind, GenericParamDefKind, List, ToPredicate, Ty, TyCtxt, WithConstness};
use crate::util::common::ErrorReported;
use chalk_engine;
use rustc_hir as hir;
@@ -732,7 +732,7 @@
param_env,
cause: ObligationCause::misc(span, hir::DUMMY_HIR_ID),
recursion_depth: 0,
- predicate: trait_ref.to_predicate(),
+ predicate: trait_ref.without_const().to_predicate(),
};
let result = infcx.predicate_must_hold_modulo_regions(&obligation);
diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs
index ce57fb8..15f81bb 100644
--- a/src/librustc/traits/object_safety.rs
+++ b/src/librustc/traits/object_safety.rs
@@ -12,7 +12,7 @@
use crate::traits::{self, Obligation, ObligationCause};
use crate::ty::subst::{InternalSubsts, Subst};
-use crate::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
@@ -234,7 +234,7 @@
.map(|(predicate, _)| predicate.subst_supertrait(tcx, &trait_ref))
.any(|predicate| {
match predicate {
- ty::Predicate::Trait(ref data) => {
+ ty::Predicate::Trait(ref data, _) => {
// In the case of a trait predicate, we can skip the "self" type.
data.skip_binder().input_types().skip(1).any(has_self_ty)
}
@@ -285,7 +285,7 @@
let predicates = tcx.predicates_of(def_id);
let predicates = predicates.instantiate_identity(tcx).predicates;
elaborate_predicates(tcx, predicates).any(|predicate| match predicate {
- ty::Predicate::Trait(ref trait_pred) => {
+ ty::Predicate::Trait(ref trait_pred, _) => {
trait_pred.def_id() == sized_def_id && trait_pred.skip_binder().self_ty().is_param(0)
}
ty::Predicate::Projection(..)
@@ -585,6 +585,7 @@
def_id: unsize_did,
substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]),
}
+ .without_const()
.to_predicate();
// U: Trait<Arg1, ..., ArgN>
@@ -598,7 +599,7 @@
}
});
- ty::TraitRef { def_id: unsize_did, substs }.to_predicate()
+ ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate()
};
let caller_bounds: Vec<Predicate<'tcx>> = param_env
@@ -620,6 +621,7 @@
def_id: dispatch_from_dyn_did,
substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]),
}
+ .without_const()
.to_predicate();
Obligation::new(ObligationCause::dummy(), param_env, predicate)
diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs
index 5bc211a..62672a7 100644
--- a/src/librustc/traits/project.rs
+++ b/src/librustc/traits/project.rs
@@ -16,7 +16,7 @@
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use crate::ty::fold::{TypeFoldable, TypeFolder};
use crate::ty::subst::{InternalSubsts, Subst};
-use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt};
+use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
@@ -738,7 +738,12 @@
depth: usize,
) -> PredicateObligation<'tcx> {
let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref();
- Obligation { cause, recursion_depth: depth, param_env, predicate: trait_ref.to_predicate() }
+ Obligation {
+ cause,
+ recursion_depth: depth,
+ param_env,
+ predicate: trait_ref.without_const().to_predicate(),
+ }
}
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
@@ -772,7 +777,7 @@
cause,
recursion_depth: depth,
param_env,
- predicate: trait_ref.to_predicate(),
+ predicate: trait_ref.without_const().to_predicate(),
};
let tcx = selcx.infcx().tcx;
let def_id = projection_ty.item_def_id;
diff --git a/src/librustc/traits/query/type_op/prove_predicate.rs b/src/librustc/traits/query/type_op/prove_predicate.rs
index c0a0cbe..15870ec 100644
--- a/src/librustc/traits/query/type_op/prove_predicate.rs
+++ b/src/librustc/traits/query/type_op/prove_predicate.rs
@@ -24,7 +24,7 @@
// `&T`, accounts for about 60% percentage of the predicates
// we have to prove. No need to canonicalize and all that for
// such cases.
- if let Predicate::Trait(trait_ref) = key.value.predicate {
+ if let Predicate::Trait(trait_ref, _) = key.value.predicate {
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
if trait_ref.def_id() == sized_def_id {
if trait_ref.skip_binder().self_ty().is_trivially_sized(tcx) {
diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs
index 3bfe542b..ac1ca4d 100644
--- a/src/librustc/traits/select.rs
+++ b/src/librustc/traits/select.rs
@@ -37,7 +37,7 @@
use crate::ty::fast_reject;
use crate::ty::relate::TypeRelation;
use crate::ty::subst::{Subst, SubstsRef};
-use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_hir::def_id::DefId;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -51,7 +51,7 @@
use std::fmt::{self, Display};
use std::iter;
use std::rc::Rc;
-use syntax::attr;
+use syntax::{ast, attr};
pub struct SelectionContext<'cx, 'tcx> {
infcx: &'cx InferCtxt<'cx, 'tcx>,
@@ -718,7 +718,7 @@
}
match obligation.predicate {
- ty::Predicate::Trait(ref t) => {
+ ty::Predicate::Trait(ref t, _) => {
debug_assert!(!t.has_escaping_bound_vars());
let obligation = obligation.with(t.clone());
self.evaluate_trait_predicate_recursively(previous_stack, obligation)
@@ -945,7 +945,9 @@
// trait refs. This is important because it's only a cycle
// if the regions match exactly.
let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth);
- let cycle = cycle.map(|stack| ty::Predicate::Trait(stack.obligation.predicate));
+ let cycle = cycle.map(|stack| {
+ ty::Predicate::Trait(stack.obligation.predicate, ast::Constness::NotConst)
+ });
if self.coinductive_match(cycle) {
debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref);
Some(EvaluatedToOk)
@@ -1060,7 +1062,7 @@
fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
let result = match predicate {
- ty::Predicate::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()),
+ ty::Predicate::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()),
_ => false,
};
debug!("coinductive_predicate({:?}) = {:?}", predicate, result);
@@ -3366,7 +3368,7 @@
tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
tcx.mk_substs_trait(source, &[]),
);
- nested.push(predicate_to_obligation(tr.to_predicate()));
+ nested.push(predicate_to_obligation(tr.without_const().to_predicate()));
// If the type is `Foo + 'a`, ensure that the type
// being cast to `Foo + 'a` outlives `'a`:
diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs
index f058a4d..f3bd98b 100644
--- a/src/librustc/traits/util.rs
+++ b/src/librustc/traits/util.rs
@@ -4,7 +4,7 @@
use crate::ty::outlives::Component;
use crate::ty::subst::{GenericArg, Subst, SubstsRef};
-use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt};
+use crate::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@@ -13,8 +13,8 @@
fn anonymize_predicate<'tcx>(tcx: TyCtxt<'tcx>, pred: &ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
match *pred {
- ty::Predicate::Trait(ref data) => {
- ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data))
+ ty::Predicate::Trait(ref data, constness) => {
+ ty::Predicate::Trait(tcx.anonymize_late_bound_regions(data), constness)
}
ty::Predicate::RegionOutlives(ref data) => {
@@ -99,14 +99,14 @@
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
) -> Elaborator<'tcx> {
- elaborate_predicates(tcx, vec![trait_ref.to_predicate()])
+ elaborate_predicates(tcx, vec![trait_ref.without_const().to_predicate()])
}
pub fn elaborate_trait_refs<'tcx>(
tcx: TyCtxt<'tcx>,
trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
) -> Elaborator<'tcx> {
- let predicates = trait_refs.map(|trait_ref| trait_ref.to_predicate()).collect();
+ let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate()).collect();
elaborate_predicates(tcx, predicates)
}
@@ -127,7 +127,7 @@
fn elaborate(&mut self, predicate: &ty::Predicate<'tcx>) {
let tcx = self.visited.tcx;
match *predicate {
- ty::Predicate::Trait(ref data) => {
+ ty::Predicate::Trait(ref data, _) => {
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());
@@ -358,7 +358,7 @@
fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool {
let tcx = self.tcx;
let trait_ref = item.trait_ref();
- let pred = trait_ref.to_predicate();
+ let pred = trait_ref.without_const().to_predicate();
debug!("expand_trait_aliases: trait_ref={:?}", trait_ref);
@@ -370,13 +370,9 @@
// Don't recurse if this trait alias is already on the stack for the DFS search.
let anon_pred = anonymize_predicate(tcx, &pred);
- if item
- .path
- .iter()
- .rev()
- .skip(1)
- .any(|(tr, _)| anonymize_predicate(tcx, &tr.to_predicate()) == anon_pred)
- {
+ if item.path.iter().rev().skip(1).any(|(tr, _)| {
+ anonymize_predicate(tcx, &tr.without_const().to_predicate()) == anon_pred
+ }) {
return false;
}
@@ -471,7 +467,7 @@
fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
while let Some(pred) = self.base_iterator.next() {
- if let ty::Predicate::Trait(data) = pred {
+ if let ty::Predicate::Trait(data, _) = pred {
return Some(data.to_poly_trait_ref());
}
}
@@ -545,7 +541,12 @@
trait_ref: ty::TraitRef<'tcx>,
recursion_depth: usize,
) -> PredicateObligation<'tcx> {
- Obligation { cause, param_env, recursion_depth, predicate: trait_ref.to_predicate() }
+ Obligation {
+ cause,
+ param_env,
+ recursion_depth,
+ predicate: trait_ref.without_const().to_predicate(),
+ }
}
pub fn predicate_for_trait_def(
diff --git a/src/librustc/traits/wf.rs b/src/librustc/traits/wf.rs
index 869ba53..a0cb844 100644
--- a/src/librustc/traits/wf.rs
+++ b/src/librustc/traits/wf.rs
@@ -3,7 +3,7 @@
use crate::middle::lang_items;
use crate::traits::{self, AssocTypeBoundData};
use crate::ty::subst::SubstsRef;
-use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{kw, Ident};
@@ -62,7 +62,7 @@
// (*) ok to skip binders, because wf code is prepared for it
match *predicate {
- ty::Predicate::Trait(ref t) => {
+ ty::Predicate::Trait(ref t, _) => {
wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
}
ty::Predicate::RegionOutlives(..) => {}
@@ -245,7 +245,7 @@
}
}
}
- ty::Predicate::Trait(proj) => {
+ ty::Predicate::Trait(proj, _) => {
// An associated item obligation born out of the `trait` failed to be met.
// Point at the `impl` that failed the obligation, the associated item that
// needed to meet the obligation, and the definition of that associated item,
@@ -350,7 +350,7 @@
self.compute_trait_ref(&trait_ref, Elaborate::None);
if !data.has_escaping_bound_vars() {
- let predicate = trait_ref.to_predicate();
+ let predicate = trait_ref.without_const().to_predicate();
let cause = self.cause(traits::ProjectionWf(data));
self.out.push(traits::Obligation::new(cause, self.param_env, predicate));
}
@@ -378,7 +378,11 @@
def_id: self.infcx.tcx.require_lang_item(lang_items::SizedTraitLangItem, None),
substs: self.infcx.tcx.mk_substs_trait(subty, &[]),
};
- self.out.push(traits::Obligation::new(cause, self.param_env, trait_ref.to_predicate()));
+ self.out.push(traits::Obligation::new(
+ cause,
+ self.param_env,
+ trait_ref.without_const().to_predicate(),
+ ));
}
}
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index b16db6a..0dddca9 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -150,6 +150,15 @@
}
}
+impl TypeFoldable<'tcx> for syntax::ast::Constness {
+ fn super_fold_with<F: TypeFolder<'tcx>>(&self, _: &mut F) -> Self {
+ *self
+ }
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> bool {
+ false
+ }
+}
+
/// The `TypeFolder` trait defines the actual *folding*. There is a
/// method defined for every foldable type. Each of these has a
/// default implementation that does an "identity" fold. Within each
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 3040ecf..0470ab2 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -52,7 +52,7 @@
use std::ops::Range;
use std::slice;
use std::{mem, ptr};
-use syntax::ast::{self, Ident, Name, NodeId};
+use syntax::ast::{self, Constness, Ident, Name, NodeId};
use syntax::attr;
pub use self::sty::BoundRegion::*;
@@ -1068,7 +1068,11 @@
/// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
/// the `Self` type of the trait reference and `A`, `B`, and `C`
/// would be the type parameters.
- Trait(PolyTraitPredicate<'tcx>),
+ ///
+ /// A trait predicate will have `Constness::Const` if it originates
+ /// from a bound on a `const fn` without the `?const` opt-out (e.g.,
+ /// `const fn foobar<Foo: Bar>() {}`).
+ Trait(PolyTraitPredicate<'tcx>, Constness),
/// `where 'a: 'b`
RegionOutlives(PolyRegionOutlivesPredicate<'tcx>),
@@ -1191,8 +1195,8 @@
let substs = &trait_ref.skip_binder().substs;
match *self {
- Predicate::Trait(ref binder) => {
- Predicate::Trait(binder.map_bound(|data| data.subst(tcx, substs)))
+ Predicate::Trait(ref binder, constness) => {
+ Predicate::Trait(binder.map_bound(|data| data.subst(tcx, substs)), constness)
}
Predicate::Subtype(ref binder) => {
Predicate::Subtype(binder.map_bound(|data| data.subst(tcx, substs)))
@@ -1336,15 +1340,33 @@
fn to_predicate(&self) -> Predicate<'tcx>;
}
-impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<TraitRef<'tcx>> {
fn to_predicate(&self) -> Predicate<'tcx> {
- ty::Predicate::Trait(ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.clone() }))
+ ty::Predicate::Trait(
+ ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value.clone() }),
+ self.constness,
+ )
}
}
-impl<'tcx> ToPredicate<'tcx> for PolyTraitRef<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> {
fn to_predicate(&self) -> Predicate<'tcx> {
- ty::Predicate::Trait(self.to_poly_trait_predicate())
+ ty::Predicate::Trait(
+ ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value.clone() }),
+ self.constness,
+ )
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<PolyTraitRef<'tcx>> {
+ fn to_predicate(&self) -> Predicate<'tcx> {
+ ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness)
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> {
+ fn to_predicate(&self) -> Predicate<'tcx> {
+ ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness)
}
}
@@ -1413,7 +1435,7 @@
/// with depth 0 are bound by the predicate.
pub fn walk_tys(&'a self) -> impl Iterator<Item = Ty<'tcx>> + 'a {
match *self {
- ty::Predicate::Trait(ref data) => {
+ ty::Predicate::Trait(ref data, _) => {
WalkTysIter::InputTypes(data.skip_binder().input_types())
}
ty::Predicate::Subtype(binder) => {
@@ -1439,7 +1461,7 @@
pub fn to_opt_poly_trait_ref(&self) -> Option<PolyTraitRef<'tcx>> {
match *self {
- Predicate::Trait(ref t) => Some(t.to_poly_trait_ref()),
+ Predicate::Trait(ref t, _) => Some(t.to_poly_trait_ref()),
Predicate::Projection(..)
| Predicate::Subtype(..)
| Predicate::RegionOutlives(..)
@@ -1700,6 +1722,33 @@
}
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ConstnessAnd<T> {
+ pub constness: Constness,
+ pub value: T,
+}
+
+// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate()` to ensure that
+// the constness of trait bounds is being propagated correctly.
+pub trait WithConstness: Sized {
+ #[inline]
+ fn with_constness(self, constness: Constness) -> ConstnessAnd<Self> {
+ ConstnessAnd { constness, value: self }
+ }
+
+ #[inline]
+ fn with_const(self) -> ConstnessAnd<Self> {
+ self.with_constness(Constness::Const)
+ }
+
+ #[inline]
+ fn without_const(self) -> ConstnessAnd<Self> {
+ self.with_constness(Constness::NotConst)
+ }
+}
+
+impl<T> WithConstness for T {}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable)]
pub struct ParamEnvAnd<'tcx, T> {
pub param_env: ParamEnv<'tcx>,
diff --git a/src/librustc/ty/print/pretty.rs b/src/librustc/ty/print/pretty.rs
index 8b1b2bb..9091de5 100644
--- a/src/librustc/ty/print/pretty.rs
+++ b/src/librustc/ty/print/pretty.rs
@@ -1791,7 +1791,12 @@
ty::Predicate<'tcx> {
match *self {
- ty::Predicate::Trait(ref data) => p!(print(data)),
+ ty::Predicate::Trait(ref data, constness) => {
+ if let ast::Constness::Const = constness {
+ p!(write("const "));
+ }
+ p!(print(data))
+ }
ty::Predicate::Subtype(ref predicate) => p!(print(predicate)),
ty::Predicate::RegionOutlives(ref predicate) => p!(print(predicate)),
ty::Predicate::TypeOutlives(ref predicate) => p!(print(predicate)),
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 62e895a..25f9dc5 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -15,6 +15,7 @@
use std::fmt;
use std::rc::Rc;
use std::sync::Arc;
+use syntax::ast;
impl fmt::Debug for ty::GenericParamDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -234,7 +235,12 @@
impl fmt::Debug for ty::Predicate<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
- ty::Predicate::Trait(ref a) => a.fmt(f),
+ ty::Predicate::Trait(ref a, constness) => {
+ if let ast::Constness::Const = constness {
+ write!(f, "const ")?;
+ }
+ a.fmt(f)
+ }
ty::Predicate::Subtype(ref pair) => pair.fmt(f),
ty::Predicate::RegionOutlives(ref pair) => pair.fmt(f),
ty::Predicate::TypeOutlives(ref pair) => pair.fmt(f),
@@ -474,7 +480,9 @@
type Lifted = ty::Predicate<'tcx>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
match *self {
- ty::Predicate::Trait(ref binder) => tcx.lift(binder).map(ty::Predicate::Trait),
+ ty::Predicate::Trait(ref binder, constness) => {
+ tcx.lift(binder).map(|binder| ty::Predicate::Trait(binder, constness))
+ }
ty::Predicate::Subtype(ref binder) => tcx.lift(binder).map(ty::Predicate::Subtype),
ty::Predicate::RegionOutlives(ref binder) => {
tcx.lift(binder).map(ty::Predicate::RegionOutlives)
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 8423612..13f623a 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -12,7 +12,9 @@
use crate::mir::Promoted;
use crate::ty::layout::VariantIdx;
use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
-use crate::ty::{self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable};
+use crate::ty::{
+ self, AdtDef, DefIdTree, Discr, Ty, TyCtxt, TypeFlags, TypeFoldable, WithConstness,
+};
use crate::ty::{List, ParamEnv, ParamEnvAnd, TyS};
use polonius_engine::Atom;
use rustc_data_structures::captures::Captures;
@@ -665,14 +667,16 @@
pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Predicate<'tcx> {
use crate::ty::ToPredicate;
match *self.skip_binder() {
- ExistentialPredicate::Trait(tr) => Binder(tr).with_self_ty(tcx, self_ty).to_predicate(),
+ ExistentialPredicate::Trait(tr) => {
+ Binder(tr).with_self_ty(tcx, self_ty).without_const().to_predicate()
+ }
ExistentialPredicate::Projection(p) => {
ty::Predicate::Projection(Binder(p.with_self_ty(tcx, self_ty)))
}
ExistentialPredicate::AutoTrait(did) => {
let trait_ref =
Binder(ty::TraitRef { def_id: did, substs: tcx.mk_substs_trait(self_ty, &[]) });
- trait_ref.to_predicate()
+ trait_ref.without_const().to_predicate()
}
}
}
diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs
index a24bb52..5dc855e 100644
--- a/src/librustc_ast_lowering/expr.rs
+++ b/src/librustc_ast_lowering/expr.rs
@@ -848,10 +848,7 @@
}
}
- fn with_catch_scope<T, F>(&mut self, catch_id: NodeId, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_catch_scope<T>(&mut self, catch_id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
let len = self.catch_scopes.len();
self.catch_scopes.push(catch_id);
@@ -867,10 +864,7 @@
result
}
- fn with_loop_scope<T, F>(&mut self, loop_id: NodeId, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_loop_scope<T>(&mut self, loop_id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
// We're no longer in the base loop's condition; we're in another loop.
let was_in_loop_condition = self.is_in_loop_condition;
self.is_in_loop_condition = false;
@@ -892,10 +886,7 @@
result
}
- fn with_loop_condition_scope<T, F>(&mut self, f: F) -> T
- where
- F: FnOnce(&mut Self) -> T,
- {
+ fn with_loop_condition_scope<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let was_in_loop_condition = self.is_in_loop_condition;
self.is_in_loop_condition = true;
diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs
index bcc40b0..2025d0c 100644
--- a/src/librustc_ast_lowering/item.rs
+++ b/src/librustc_ast_lowering/item.rs
@@ -66,9 +66,8 @@
if let Some(hir_id) = item_hir_id {
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
let this = &mut ItemLowerer { lctx: this };
- if let ItemKind::Impl { ref of_trait, .. } = item.kind {
- if of_trait.as_ref().map(|tr| tr.constness.is_some()).unwrap_or(false) {
- this.with_trait_impl_ref(of_trait, |this| visit::walk_item(this, item));
+ if let ItemKind::Impl { constness, ref of_trait, .. } = item.kind {
+ if constness == Constness::Const {
this.lctx
.diagnostic()
.span_err(item.span, "const trait impls are not yet implemented");
@@ -365,6 +364,7 @@
unsafety,
polarity,
defaultness,
+ constness,
generics: ref ast_generics,
of_trait: ref trait_ref,
self_ty: ref ty,
@@ -421,6 +421,7 @@
unsafety,
polarity,
defaultness: self.lower_defaultness(defaultness, true /* [1] */),
+ constness,
generics,
of_trait: trait_ref,
self_ty: lowered_ty,
diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs
index 2cd4a6e..284ede3 100644
--- a/src/librustc_ast_lowering/lib.rs
+++ b/src/librustc_ast_lowering/lib.rs
@@ -1249,10 +1249,16 @@
let bounds =
this.arena.alloc_from_iter(bounds.iter().filter_map(
|bound| match *bound {
- GenericBound::Trait(ref ty, TraitBoundModifier::None) => {
+ GenericBound::Trait(ref ty, TraitBoundModifier::None)
+ | GenericBound::Trait(ref ty, TraitBoundModifier::MaybeConst) => {
Some(this.lower_poly_trait_ref(ty, itctx.reborrow()))
}
- GenericBound::Trait(_, TraitBoundModifier::Maybe) => None,
+ // `?const ?Bound` will cause an error during AST validation
+ // anyways, so treat it like `?Bound` as compilation proceeds.
+ GenericBound::Trait(_, TraitBoundModifier::Maybe)
+ | GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
+ None
+ }
GenericBound::Outlives(ref lifetime) => {
if lifetime_bound.is_none() {
lifetime_bound = Some(this.lower_lifetime(lifetime));
@@ -2157,10 +2163,6 @@
p: &PolyTraitRef,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PolyTraitRef<'hir> {
- if p.trait_ref.constness.is_some() {
- self.diagnostic().span_err(p.span, "`?const` on trait bounds is not yet implemented");
- }
-
let bound_generic_params = self.lower_generic_params(
&p.bound_generic_params,
&NodeMap::default(),
@@ -2299,7 +2301,13 @@
fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBoundModifier {
match f {
TraitBoundModifier::None => hir::TraitBoundModifier::None,
- TraitBoundModifier::Maybe => hir::TraitBoundModifier::Maybe,
+ TraitBoundModifier::MaybeConst => hir::TraitBoundModifier::MaybeConst,
+
+ // `MaybeConstMaybe` will cause an error during AST validation, but we need to pick a
+ // placeholder for compilation to proceed.
+ TraitBoundModifier::MaybeConstMaybe | TraitBoundModifier::Maybe => {
+ hir::TraitBoundModifier::Maybe
+ }
}
}
diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs
index bc02406..152086b 100644
--- a/src/librustc_ast_passes/ast_validation.rs
+++ b/src/librustc_ast_passes/ast_validation.rs
@@ -614,6 +614,7 @@
unsafety,
polarity,
defaultness: _,
+ constness: _,
generics: _,
of_trait: Some(_),
ref self_ty,
@@ -647,6 +648,7 @@
unsafety,
polarity,
defaultness,
+ constness,
generics: _,
of_trait: None,
self_ty: _,
@@ -674,6 +676,12 @@
.note("only trait implementations may be annotated with default")
.emit();
}
+ if constness == Constness::Const {
+ self.err_handler()
+ .struct_span_err(item.span, "inherent impls cannot be `const`")
+ .note("only trait implementations may be annotated with `const`")
+ .emit();
+ }
}
ItemKind::Fn(ref sig, ref generics, _) => {
self.visit_fn_header(&sig.header);
@@ -907,23 +915,20 @@
}
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
- if let GenericBound::Trait(poly, maybe_bound) = bound {
- match poly.trait_ref.constness {
- Some(Constness::NotConst) => {
- if *maybe_bound == TraitBoundModifier::Maybe {
- self.err_handler()
- .span_err(bound.span(), "`?const` and `?` are mutually exclusive");
- }
-
- if let Some(ctx) = self.bound_context {
- let msg = format!("`?const` is not permitted in {}", ctx.description());
- self.err_handler().span_err(bound.span(), &msg);
- }
+ match bound {
+ GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
+ if let Some(ctx) = self.bound_context {
+ let msg = format!("`?const` is not permitted in {}", ctx.description());
+ self.err_handler().span_err(bound.span(), &msg);
}
-
- Some(Constness::Const) => panic!("Parser should reject bare `const` on bounds"),
- None => {}
}
+
+ GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
+ self.err_handler()
+ .span_err(bound.span(), "`?const` and `?` are mutually exclusive");
+ }
+
+ _ => {}
}
visit::walk_param_bound(self, bound)
diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs
index d346dbc8..f891801 100644
--- a/src/librustc_builtin_macros/deriving/generic/mod.rs
+++ b/src/librustc_builtin_macros/deriving/generic/mod.rs
@@ -709,6 +709,7 @@
unsafety,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
+ constness: ast::Constness::NotConst,
generics: trait_generics,
of_trait: opt_trait_ref,
self_ty: self_type,
diff --git a/src/librustc_builtin_macros/deriving/mod.rs b/src/librustc_builtin_macros/deriving/mod.rs
index 9aa7623..914dcdf 100644
--- a/src/librustc_builtin_macros/deriving/mod.rs
+++ b/src/librustc_builtin_macros/deriving/mod.rs
@@ -160,6 +160,7 @@
unsafety: ast::Unsafety::Normal,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
+ constness: ast::Constness::NotConst,
generics,
of_trait: Some(trait_ref),
self_ty: self_type,
diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs
index 52613fe..4823fe1 100644
--- a/src/librustc_codegen_llvm/llvm_util.rs
+++ b/src/librustc_codegen_llvm/llvm_util.rs
@@ -58,9 +58,10 @@
let cg_opts = sess.opts.cg.llvm_args.iter();
let tg_opts = sess.target.target.options.llvm_args.iter();
+ let sess_args = cg_opts.chain(tg_opts);
let user_specified_args: FxHashSet<_> =
- cg_opts.chain(tg_opts).map(|s| llvm_arg_to_arg_name(s)).filter(|s| s.len() > 0).collect();
+ sess_args.clone().map(|s| llvm_arg_to_arg_name(s)).filter(|s| s.len() > 0).collect();
{
// This adds the given argument to LLVM. Unless `force` is true
@@ -107,7 +108,7 @@
// during inlining. Unfortunately these may block other optimizations.
add("-preserve-alignment-assumptions-during-inlining=false", false);
- for arg in &sess.opts.cg.llvm_args {
+ for arg in sess_args {
add(&(*arg), true);
}
}
diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs
index bd3d6b5..11f94ab 100644
--- a/src/librustc_expand/build.rs
+++ b/src/librustc_expand/build.rs
@@ -110,7 +110,7 @@
}
pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef {
- ast::TraitRef { path, constness: None, ref_id: ast::DUMMY_NODE_ID }
+ ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
}
pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs
index 1fef871b..b62a7e4 100644
--- a/src/librustc_hir/hir.rs
+++ b/src/librustc_hir/hir.rs
@@ -364,6 +364,7 @@
pub enum TraitBoundModifier {
None,
Maybe,
+ MaybeConst,
}
/// The AST represents all type param bounds as types.
@@ -2440,6 +2441,7 @@
unsafety: Unsafety,
polarity: ImplPolarity,
defaultness: Defaultness,
+ constness: Constness,
generics: Generics<'hir>,
/// The trait being implemented, if any.
diff --git a/src/librustc_hir/intravisit.rs b/src/librustc_hir/intravisit.rs
index 1a73e41..539a0ee 100644
--- a/src/librustc_hir/intravisit.rs
+++ b/src/librustc_hir/intravisit.rs
@@ -570,6 +570,7 @@
unsafety: _,
defaultness: _,
polarity: _,
+ constness: _,
ref generics,
ref of_trait,
ref self_ty,
diff --git a/src/librustc_hir/print.rs b/src/librustc_hir/print.rs
index 6c7d419..b9598c9 100644
--- a/src/librustc_hir/print.rs
+++ b/src/librustc_hir/print.rs
@@ -631,6 +631,7 @@
unsafety,
polarity,
defaultness,
+ constness,
ref generics,
ref of_trait,
ref self_ty,
@@ -647,6 +648,10 @@
self.s.space();
}
+ if constness == ast::Constness::Const {
+ self.word_nbsp("const");
+ }
+
if let hir::ImplPolarity::Negative = polarity {
self.s.word("!");
}
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 26cbda3..15158c0 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -144,7 +144,7 @@
ty::Opaque(def, _) => {
let mut has_emitted = false;
for (predicate, _) in cx.tcx.predicates_of(def).predicates {
- if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
+ if let ty::Predicate::Trait(ref poly_trait_predicate, _) = predicate {
let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
let def_id = trait_ref.def_id;
let descr_pre =
diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs
index a92abc9..c9a1c46 100644
--- a/src/librustc_mir/borrow_check/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/type_check/mod.rs
@@ -32,6 +32,7 @@
use rustc_hir::def_id::DefId;
use rustc_index::vec::{Idx, IndexVec};
use rustc_span::{Span, DUMMY_SP};
+use syntax::ast;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::FlowAtLocation;
@@ -1930,12 +1931,15 @@
traits::ObligationCauseCode::RepeatVec(should_suggest),
),
self.param_env,
- ty::Predicate::Trait(ty::Binder::bind(ty::TraitPredicate {
- trait_ref: ty::TraitRef::new(
- self.tcx().lang_items().copy_trait().unwrap(),
- tcx.mk_substs_trait(ty, &[]),
- ),
- })),
+ ty::Predicate::Trait(
+ ty::Binder::bind(ty::TraitPredicate {
+ trait_ref: ty::TraitRef::new(
+ self.tcx().lang_items().copy_trait().unwrap(),
+ tcx.mk_substs_trait(ty, &[]),
+ ),
+ }),
+ ast::Constness::NotConst,
+ ),
),
&traits::SelectionError::Unimplemented,
false,
@@ -2573,7 +2577,10 @@
category: ConstraintCategory,
) {
self.prove_predicates(
- Some(ty::Predicate::Trait(trait_ref.to_poly_trait_ref().to_poly_trait_predicate())),
+ Some(ty::Predicate::Trait(
+ trait_ref.to_poly_trait_ref().to_poly_trait_predicate(),
+ ast::Constness::NotConst,
+ )),
locations,
category,
);
diff --git a/src/librustc_mir/dataflow/generic.rs b/src/librustc_mir/dataflow/generic.rs
deleted file mode 100644
index d2ca4f1..0000000
--- a/src/librustc_mir/dataflow/generic.rs
+++ /dev/null
@@ -1,595 +0,0 @@
-//! Dataflow analysis with arbitrary transfer functions.
-//!
-//! This module is a work in progress. You should instead use `BitDenotation` in
-//! `librustc_mir/dataflow/mod.rs` and encode your transfer function as a [gen/kill set][gk]. In
-//! doing so, your analysis will run faster and you will be able to generate graphviz diagrams for
-//! debugging with no extra effort. The interface in this module is intended only for dataflow
-//! problems that cannot be expressed using gen/kill sets.
-//!
-//! FIXME(ecstaticmorse): In the long term, the plan is to preserve the existing `BitDenotation`
-//! interface, but make `Engine` and `ResultsCursor` the canonical way to perform and inspect a
-//! dataflow analysis. This requires porting the graphviz debugging logic to this module, deciding
-//! on a way to handle the `before` methods in `BitDenotation` and creating an adapter so that
-//! gen-kill problems can still be evaluated efficiently. See the discussion in [#64566] for more
-//! information.
-//!
-//! [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::ffi::OsString;
-use std::path::{Path, PathBuf};
-use std::{fs, io, ops};
-
-use rustc::mir::{self, traversal, BasicBlock, Location};
-use rustc::ty::{self, TyCtxt};
-use rustc_data_structures::work_queue::WorkQueue;
-use rustc_hir::def_id::DefId;
-use rustc_index::bit_set::BitSet;
-use rustc_index::vec::{Idx, IndexVec};
-use rustc_span::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
-/// `initialize_start_block` and define a transfer function for each statement or terminator via
-/// the various `effect` methods. The entry set for all other basic blocks is initialized to
-/// `Self::BOTTOM_VALUE`. The dataflow `Engine` then iteratively updates the various entry sets for
-/// each block with the cumulative effects of the transfer functions of all preceding blocks.
-///
-/// You should use an `Engine` to actually run an analysis, and a `ResultsCursor` to inspect the
-/// results of that analysis like so:
-///
-/// ```ignore(cross-crate-imports)
-/// fn do_my_analysis(body: &mir::Body<'tcx>, dead_unwinds: &BitSet<BasicBlock>) {
-/// // `MyAnalysis` implements `Analysis`.
-/// let analysis = MyAnalysis::new();
-///
-/// let results = Engine::new(body, dead_unwinds, analysis).iterate_to_fixpoint();
-/// let mut cursor = ResultsCursor::new(body, results);
-///
-/// for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
-/// cursor.seek_after(Location { block: START_BLOCK, statement_index });
-/// let state = cursor.get();
-/// println!("{:?}", state);
-/// }
-/// }
-/// ```
-pub trait Analysis<'tcx>: BottomValue {
- /// The index type used to access the dataflow state.
- type Idx: Idx;
-
- /// A name, used for debugging, that describes this dataflow analysis.
- ///
- /// The name should be suitable as part of a filename, so avoid whitespace, slashes or periods
- /// 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;
-
- /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
- /// analysis.
- fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
-
- /// Updates the current dataflow state with the effect of evaluating a statement.
- fn apply_statement_effect(
- &self,
- state: &mut BitSet<Self::Idx>,
- statement: &mir::Statement<'tcx>,
- location: Location,
- );
-
- /// 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,
- /// in the `InitializedPlaces` analyses, the return place is not marked as initialized here.
- fn apply_terminator_effect(
- &self,
- state: &mut BitSet<Self::Idx>,
- terminator: &mir::Terminator<'tcx>,
- location: Location,
- );
-
- /// Updates the current dataflow state with the effect of a successful return from a `Call`
- /// terminator.
- ///
- /// This is separated from `apply_terminator_effect` to properly track state across
- /// unwind edges for `Call`s.
- fn apply_call_return_effect(
- &self,
- state: &mut BitSet<Self::Idx>,
- block: BasicBlock,
- func: &mir::Operand<'tcx>,
- args: &[mir::Operand<'tcx>],
- return_place: &mir::Place<'tcx>,
- );
-
- /// Applies the cumulative effect of an entire basic block to the dataflow state (except for
- /// `call_return_effect`, which is handled in the `Engine`).
- ///
- /// The default implementation calls `statement_effect` for every statement in the block before
- /// finally calling `terminator_effect`. However, some dataflow analyses are able to coalesce
- /// transfer functions for an entire block and apply them at once. Such analyses should
- /// override `block_effect`.
- fn apply_whole_block_effect(
- &self,
- state: &mut BitSet<Self::Idx>,
- block: BasicBlock,
- block_data: &mir::BasicBlockData<'tcx>,
- ) {
- for (statement_index, stmt) in block_data.statements.iter().enumerate() {
- let location = Location { block, statement_index };
- self.apply_statement_effect(state, stmt, location);
- }
-
- let location = Location { block, statement_index: block_data.statements.len() };
- self.apply_terminator_effect(state, block_data.terminator(), location);
- }
-
- /// Applies the cumulative effect of a sequence of statements (and possibly a terminator)
- /// within a single basic block.
- ///
- /// When called with `0..block_data.statements.len() + 1` as the statement range, this function
- /// is equivalent to `apply_whole_block_effect`.
- fn apply_partial_block_effect(
- &self,
- state: &mut BitSet<Self::Idx>,
- block: BasicBlock,
- block_data: &mir::BasicBlockData<'tcx>,
- mut range: ops::Range<usize>,
- ) {
- if range.is_empty() {
- return;
- }
-
- // The final location might be a terminator, so iterate through all statements until the
- // final one, then check to see whether the final one is a statement or terminator.
- //
- // This can't cause the range to wrap-around since we check that the range contains at
- // least one element above.
- range.end -= 1;
- let final_location = Location { block, statement_index: range.end };
-
- for statement_index in range {
- let location = Location { block, statement_index };
- let stmt = &block_data.statements[statement_index];
- self.apply_statement_effect(state, stmt, location);
- }
-
- if final_location.statement_index == block_data.statements.len() {
- let terminator = block_data.terminator();
- self.apply_terminator_effect(state, terminator, final_location);
- } else {
- let stmt = &block_data.statements[final_location.statement_index];
- self.apply_statement_effect(state, stmt, final_location);
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-enum CursorPosition {
- AtBlockStart(BasicBlock),
- After(Location),
-}
-
-impl CursorPosition {
- fn block(&self) -> BasicBlock {
- match *self {
- Self::AtBlockStart(block) => block,
- Self::After(Location { block, .. }) => block,
- }
- }
-}
-
-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, R = Results<'tcx, A>>
-where
- A: Analysis<'tcx>,
-{
- body: &'mir mir::Body<'tcx>,
- results: R,
- state: BitSet<A::Idx>,
-
- pos: CursorPosition,
-
- /// Whether the effects of `apply_call_return_effect` are currently stored in `state`.
- ///
- /// This flag ensures that multiple calls to `seek_after_assume_call_returns` with the same
- /// target only result in one invocation of `apply_call_return_effect`.
- is_call_return_effect_applied: bool,
-}
-
-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: R) -> Self {
- ResultsCursor {
- body,
- pos: CursorPosition::AtBlockStart(mir::START_BLOCK),
- is_call_return_effect_applied: false,
- 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.borrow().entry_sets[block]);
- self.pos = CursorPosition::AtBlockStart(block);
- self.is_call_return_effect_applied = false;
- }
-
- /// Updates the cursor to hold the dataflow state immediately before `target`.
- pub fn seek_before(&mut self, target: Location) {
- assert!(target <= self.body.terminator_loc(target.block));
-
- if target.statement_index == 0 {
- self.seek_to_block_start(target.block);
- } else {
- self._seek_after(Location {
- block: target.block,
- statement_index: target.statement_index - 1,
- });
- }
- }
-
- /// Updates the cursor to hold the dataflow state at `target`.
- ///
- /// If `target` is a `Call` terminator, `apply_call_return_effect` will not be called. See
- /// `seek_after_assume_call_returns` if you wish to observe the dataflow state upon a
- /// successful return.
- pub fn seek_after(&mut self, target: Location) {
- assert!(target <= self.body.terminator_loc(target.block));
-
- // This check ensures the correctness of a call to `seek_after_assume_call_returns`
- // followed by one to `seek_after` with the same target.
- if self.is_call_return_effect_applied {
- self.seek_to_block_start(target.block);
- }
-
- self._seek_after(target);
- }
-
- /// Equivalent to `seek_after`, but also calls `apply_call_return_effect` if `target` is a
- /// `Call` terminator whose callee is convergent.
- pub fn seek_after_assume_call_returns(&mut self, target: Location) {
- assert!(target <= self.body.terminator_loc(target.block));
-
- self._seek_after(target);
-
- if target != self.body.terminator_loc(target.block) {
- return;
- }
-
- let term = self.body.basic_blocks()[target.block].terminator();
- if let mir::TerminatorKind::Call {
- destination: Some((return_place, _)), func, args, ..
- } = &term.kind
- {
- if !self.is_call_return_effect_applied {
- self.is_call_return_effect_applied = true;
- self.results.borrow().analysis.apply_call_return_effect(
- &mut self.state,
- target.block,
- func,
- args,
- return_place,
- );
- }
- }
- }
-
- fn _seek_after(&mut self, target: Location) {
- let Location { block: target_block, statement_index: target_index } = target;
-
- if self.pos.block() != target_block {
- self.seek_to_block_start(target_block);
- }
-
- // If we're in the same block but after the target statement, we need to reset to the start
- // of the block.
- if let CursorPosition::After(Location { statement_index: curr_index, .. }) = self.pos {
- match curr_index.cmp(&target_index) {
- Ordering::Equal => return,
- Ordering::Less => {}
- Ordering::Greater => self.seek_to_block_start(target_block),
- }
- }
-
- // The cursor is now in the same block as the target location pointing at an earlier
- // statement.
- debug_assert_eq!(self.pos.block(), target_block);
- if let CursorPosition::After(Location { statement_index, .. }) = self.pos {
- debug_assert!(statement_index < target_index);
- }
-
- let first_unapplied_statement = match self.pos {
- CursorPosition::AtBlockStart(_) => 0,
- CursorPosition::After(Location { statement_index, .. }) => statement_index + 1,
- };
-
- let block_data = &self.body.basic_blocks()[target_block];
- self.results.borrow().analysis.apply_partial_block_effect(
- &mut self.state,
- target_block,
- block_data,
- first_unapplied_statement..target_index + 1,
- );
-
- self.pos = CursorPosition::After(target);
- self.is_call_return_effect_applied = false;
- }
-
- /// Gets the dataflow state at the current location.
- pub fn get(&self) -> &BitSet<A::Idx> {
- &self.state
- }
-}
-
-/// A completed dataflow analysis.
-pub struct Results<'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- analysis: A,
- entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
-}
-
-/// All information required to iterate a dataflow analysis to fixpoint.
-pub struct Engine<'a, 'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- 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>>,
-}
-
-impl<A> Engine<'a, 'tcx, A>
-where
- 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 {
- let bits_per_block = analysis.bits_per_block(body);
-
- let bottom_value_set = if A::BOTTOM_VALUE == true {
- BitSet::new_filled(bits_per_block)
- } else {
- BitSet::new_empty(bits_per_block)
- };
-
- let mut entry_sets = IndexVec::from_elem(bottom_value_set, body.basic_blocks());
- analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
-
- Engine { analysis, bits_per_block, tcx, body, def_id, dead_unwinds, entry_sets }
- }
-
- pub fn iterate_to_fixpoint(mut self) -> Results<'tcx, A> {
- let mut temp_state = BitSet::new_empty(self.bits_per_block);
-
- let mut dirty_queue: WorkQueue<BasicBlock> =
- WorkQueue::with_none(self.body.basic_blocks().len());
-
- for (bb, _) in traversal::reverse_postorder(self.body) {
- dirty_queue.insert(bb);
- }
-
- // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
- // be processed after the ones added above.
- for bb in self.body.basic_blocks().indices() {
- dirty_queue.insert(bb);
- }
-
- while let Some(bb) = dirty_queue.pop() {
- let bb_data = &self.body[bb];
- let on_entry = &self.entry_sets[bb];
-
- temp_state.overwrite(on_entry);
- self.analysis.apply_whole_block_effect(&mut temp_state, bb, bb_data);
-
- self.propagate_bits_into_graph_successors_of(
- &mut temp_state,
- (bb, bb_data),
- &mut dirty_queue,
- );
- }
-
- 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(
- &mut self,
- in_out: &mut BitSet<A::Idx>,
- (bb, bb_data): (BasicBlock, &'a mir::BasicBlockData<'tcx>),
- dirty_list: &mut WorkQueue<BasicBlock>,
- ) {
- match bb_data.terminator().kind {
- mir::TerminatorKind::Return
- | mir::TerminatorKind::Resume
- | mir::TerminatorKind::Abort
- | mir::TerminatorKind::GeneratorDrop
- | mir::TerminatorKind::Unreachable => {}
-
- mir::TerminatorKind::Goto { target }
- | mir::TerminatorKind::Assert { target, cleanup: None, .. }
- | mir::TerminatorKind::Yield { resume: target, drop: None, .. }
- | mir::TerminatorKind::Drop { target, location: _, unwind: None }
- | mir::TerminatorKind::DropAndReplace { target, value: _, location: _, unwind: None } =>
- {
- self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
- }
-
- mir::TerminatorKind::Yield { resume: target, drop: Some(drop), .. } => {
- self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
- self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
- }
-
- mir::TerminatorKind::Assert { target, cleanup: Some(unwind), .. }
- | mir::TerminatorKind::Drop { target, location: _, unwind: Some(unwind) }
- | mir::TerminatorKind::DropAndReplace {
- target,
- value: _,
- location: _,
- unwind: Some(unwind),
- } => {
- self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
- if !self.dead_unwinds.contains(bb) {
- self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
- }
- }
-
- mir::TerminatorKind::SwitchInt { ref targets, .. } => {
- for target in targets {
- self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list);
- }
- }
-
- mir::TerminatorKind::Call { cleanup, ref destination, ref func, ref args, .. } => {
- if let Some(unwind) = cleanup {
- if !self.dead_unwinds.contains(bb) {
- self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
- }
- }
-
- if let Some((ref dest_place, dest_bb)) = *destination {
- // N.B.: This must be done *last*, after all other
- // propagation, as documented in comment above.
- self.analysis.apply_call_return_effect(in_out, bb, func, args, dest_place);
- self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
- }
- }
-
- mir::TerminatorKind::FalseEdges { real_target, imaginary_target } => {
- self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
- self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
- }
-
- mir::TerminatorKind::FalseUnwind { real_target, unwind } => {
- self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
- if let Some(unwind) = unwind {
- if !self.dead_unwinds.contains(bb) {
- self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
- }
- }
- }
- }
- }
-
- fn propagate_bits_into_entry_set_for(
- &mut self,
- in_out: &BitSet<A::Idx>,
- bb: BasicBlock,
- dirty_queue: &mut WorkQueue<BasicBlock>,
- ) {
- let entry_set = &mut self.entry_sets[bb];
- let set_changed = self.analysis.join(entry_set, &in_out);
- if set_changed {
- dirty_queue.insert(bb);
- }
- }
-}
-
-/// 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)
-}
diff --git a/src/librustc_mir/dataflow/generic/cursor.rs b/src/librustc_mir/dataflow/generic/cursor.rs
new file mode 100644
index 0000000..d2eff49
--- /dev/null
+++ b/src/librustc_mir/dataflow/generic/cursor.rs
@@ -0,0 +1,265 @@
+//! Random access inspection of the results of a dataflow analysis.
+
+use std::borrow::Borrow;
+
+use rustc::mir::{self, BasicBlock, Location};
+use rustc_index::bit_set::BitSet;
+
+use super::{Analysis, Results};
+
+/// A `ResultsCursor` that borrows the underlying `Results`.
+pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
+
+/// Allows random access inspection of the results of a dataflow analysis.
+///
+/// This cursor only has linear performance within a basic block when its statements are visited in
+/// order. In the worst case—when statements are visited in *reverse* order—performance will be
+/// quadratic in the number of statements in the block. The order in which basic blocks are
+/// inspected has no impact on performance.
+///
+/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
+/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
+pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
+where
+ A: Analysis<'tcx>,
+{
+ body: &'mir mir::Body<'tcx>,
+ results: R,
+ state: BitSet<A::Idx>,
+
+ pos: CursorPosition,
+
+ /// When this flag is set, the cursor is pointing at a `Call` terminator whose call return
+ /// effect has been applied to `state`.
+ ///
+ /// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the
+ /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
+ /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
+ /// terminator will always require a cursor reset.
+ call_return_effect_applied: bool,
+}
+
+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: R) -> Self {
+ ResultsCursor {
+ body,
+ pos: CursorPosition::BlockStart(mir::START_BLOCK),
+ state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
+ call_return_effect_applied: false,
+ results,
+ }
+ }
+
+ /// Returns the `Analysis` used to generate the underlying results.
+ pub fn analysis(&self) -> &A {
+ &self.results.borrow().analysis
+ }
+
+ /// Returns the dataflow state at the current location.
+ pub fn get(&self) -> &BitSet<A::Idx> {
+ &self.state
+ }
+
+ /// Resets the cursor to the start of the given basic block.
+ pub fn seek_to_block_start(&mut self, block: BasicBlock) {
+ self.state.overwrite(&self.results.borrow().entry_sets[block]);
+ self.pos = CursorPosition::BlockStart(block);
+ self.call_return_effect_applied = false;
+ }
+
+ /// Advances the cursor to hold all effects up to and including to the "before" effect of the
+ /// statement (or terminator) at the given location.
+ ///
+ /// If you wish to observe the full effect of a statement or terminator, not just the "before"
+ /// effect, use `seek_after` or `seek_after_assume_call_returns`.
+ pub fn seek_before(&mut self, target: Location) {
+ assert!(target <= self.body.terminator_loc(target.block));
+ self.seek_(target, false);
+ }
+
+ /// Advances the cursor to hold the full effect of all statements (and possibly closing
+ /// terminators) up to and including the `target`.
+ ///
+ /// If the `target` is a `Call` terminator, any call return effect for that terminator will
+ /// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call
+ /// return effect.
+ pub fn seek_after(&mut self, target: Location) {
+ assert!(target <= self.body.terminator_loc(target.block));
+
+ // If we have already applied the call return effect, we are currently pointing at a `Call`
+ // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
+ // the call return effect.
+ if self.call_return_effect_applied {
+ self.seek_to_block_start(target.block);
+ }
+
+ self.seek_(target, true);
+ }
+
+ /// Advances the cursor to hold all effects up to and including of the statement (or
+ /// terminator) at the given location.
+ ///
+ /// If the `target` is a `Call` terminator, any call return effect for that terminator will
+ /// be observed. Use `seek_after` if you do **not** wish to observe the call return effect.
+ pub fn seek_after_assume_call_returns(&mut self, target: Location) {
+ let terminator_loc = self.body.terminator_loc(target.block);
+ assert!(target.statement_index <= terminator_loc.statement_index);
+
+ self.seek_(target, true);
+
+ if target != terminator_loc {
+ return;
+ }
+
+ let terminator = self.body.basic_blocks()[target.block].terminator();
+ if let mir::TerminatorKind::Call {
+ destination: Some((return_place, _)), func, args, ..
+ } = &terminator.kind
+ {
+ if !self.call_return_effect_applied {
+ self.call_return_effect_applied = true;
+ self.results.borrow().analysis.apply_call_return_effect(
+ &mut self.state,
+ target.block,
+ func,
+ args,
+ return_place,
+ );
+ }
+ }
+ }
+
+ fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) {
+ use CursorPosition::*;
+
+ match self.pos {
+ // Return early if we are already at the target location.
+ Before(curr) if curr == target && !apply_after_effect_at_target => return,
+ After(curr) if curr == target && apply_after_effect_at_target => return,
+
+ // Otherwise, we must reset to the start of the target block if...
+
+ // we are in a different block entirely.
+ BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
+ if block != target.block =>
+ {
+ self.seek_to_block_start(target.block)
+ }
+
+ // we are in the same block but have advanced past the target statement.
+ Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
+ self.seek_to_block_start(target.block)
+ }
+
+ // we have already applied the entire effect of a statement but only wish to observe
+ // its "before" effect.
+ After(curr)
+ if curr.statement_index == target.statement_index
+ && !apply_after_effect_at_target =>
+ {
+ self.seek_to_block_start(target.block)
+ }
+
+ // N.B., `call_return_effect_applied` is checked in `seek_after`, not here.
+ _ => (),
+ }
+
+ let analysis = &self.results.borrow().analysis;
+ let block_data = &self.body.basic_blocks()[target.block];
+
+ // At this point, the cursor is in the same block as the target location at an earlier
+ // statement.
+ debug_assert_eq!(target.block, self.pos.block());
+
+ // Find the first statement whose transfer function has not yet been applied.
+ let first_unapplied_statement = match self.pos {
+ BlockStart(_) => 0,
+ After(Location { statement_index, .. }) => statement_index + 1,
+
+ // If we have only applied the "before" effect for the current statement, apply the
+ // remainder before continuing.
+ Before(curr) => {
+ if curr.statement_index == block_data.statements.len() {
+ let terminator = block_data.terminator();
+ analysis.apply_terminator_effect(&mut self.state, terminator, curr);
+ } else {
+ let statement = &block_data.statements[curr.statement_index];
+ analysis.apply_statement_effect(&mut self.state, statement, curr);
+ }
+
+ // If all we needed to do was go from `Before` to `After` in the same statement,
+ // we are now done.
+ if curr.statement_index == target.statement_index {
+ debug_assert!(apply_after_effect_at_target);
+ self.pos = After(target);
+ return;
+ }
+
+ curr.statement_index + 1
+ }
+ };
+
+ // We have now applied all effects prior to `first_unapplied_statement`.
+
+ // Apply the effects of all statements before `target`.
+ let mut location = Location { block: target.block, statement_index: 0 };
+ for statement_index in first_unapplied_statement..target.statement_index {
+ location.statement_index = statement_index;
+ let statement = &block_data.statements[statement_index];
+ analysis.apply_before_statement_effect(&mut self.state, statement, location);
+ analysis.apply_statement_effect(&mut self.state, statement, location);
+ }
+
+ // Apply the effect of the statement (or terminator) at `target`.
+ location.statement_index = target.statement_index;
+ if target.statement_index == block_data.statements.len() {
+ let terminator = &block_data.terminator();
+ analysis.apply_before_terminator_effect(&mut self.state, terminator, location);
+
+ if apply_after_effect_at_target {
+ analysis.apply_terminator_effect(&mut self.state, terminator, location);
+ self.pos = After(target);
+ } else {
+ self.pos = Before(target);
+ }
+ } else {
+ let statement = &block_data.statements[target.statement_index];
+ analysis.apply_before_statement_effect(&mut self.state, statement, location);
+
+ if apply_after_effect_at_target {
+ analysis.apply_statement_effect(&mut self.state, statement, location);
+ self.pos = After(target)
+ } else {
+ self.pos = Before(target);
+ }
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum CursorPosition {
+ /// No effects within this block have been applied.
+ BlockStart(BasicBlock),
+
+ /// Only the "before" effect of the statement (or terminator) at this location has been
+ /// applied (along with the effects of all previous statements).
+ Before(Location),
+
+ /// The effects of all statements up to and including the one at this location have been
+ /// applied.
+ After(Location),
+}
+
+impl CursorPosition {
+ fn block(&self) -> BasicBlock {
+ match *self {
+ Self::BlockStart(block) => block,
+ Self::Before(loc) | Self::After(loc) => loc.block,
+ }
+ }
+}
diff --git a/src/librustc_mir/dataflow/generic/engine.rs b/src/librustc_mir/dataflow/generic/engine.rs
new file mode 100644
index 0000000..c0152b0
--- /dev/null
+++ b/src/librustc_mir/dataflow/generic/engine.rs
@@ -0,0 +1,427 @@
+//! A solver for dataflow problems.
+
+use std::ffi::OsString;
+use std::fs;
+use std::path::PathBuf;
+
+use rustc::mir::{self, traversal, BasicBlock, Location};
+use rustc::ty::TyCtxt;
+use rustc_data_structures::work_queue::WorkQueue;
+use rustc_hir::def_id::DefId;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_span::symbol::{sym, Symbol};
+use syntax::ast;
+
+use super::graphviz;
+use super::{Analysis, GenKillAnalysis, GenKillSet, Results};
+
+/// A solver for dataflow problems.
+pub struct Engine<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ bits_per_block: usize,
+ tcx: TyCtxt<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ def_id: DefId,
+ dead_unwinds: Option<&'a BitSet<BasicBlock>>,
+ entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+ analysis: A,
+
+ /// Cached, cumulative transfer functions for each block.
+ trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+}
+
+impl<A> Engine<'a, 'tcx, A>
+where
+ A: GenKillAnalysis<'tcx>,
+{
+ /// Creates a new `Engine` to solve a gen-kill dataflow problem.
+ pub fn new_gen_kill(
+ tcx: TyCtxt<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ def_id: DefId,
+ analysis: A,
+ ) -> Self {
+ let bits_per_block = analysis.bits_per_block(body);
+ let mut trans_for_block =
+ IndexVec::from_elem(GenKillSet::identity(bits_per_block), body.basic_blocks());
+
+ // Compute cumulative block transfer functions.
+ //
+ // FIXME: we may want to skip this if the MIR is acyclic, since we will never access a
+ // block transfer function more than once.
+
+ for (block, block_data) in body.basic_blocks().iter_enumerated() {
+ let trans = &mut trans_for_block[block];
+
+ for (i, statement) in block_data.statements.iter().enumerate() {
+ let loc = Location { block, statement_index: i };
+ analysis.before_statement_effect(trans, statement, loc);
+ analysis.statement_effect(trans, statement, loc);
+ }
+
+ if let Some(terminator) = &block_data.terminator {
+ let loc = Location { block, statement_index: block_data.statements.len() };
+ analysis.before_terminator_effect(trans, terminator, loc);
+ analysis.terminator_effect(trans, terminator, loc);
+ }
+ }
+
+ Self::new(tcx, body, def_id, analysis, Some(trans_for_block))
+ }
+}
+
+impl<A> Engine<'a, 'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ /// Creates a new `Engine` to solve a dataflow problem with an arbitrary transfer
+ /// function.
+ ///
+ /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
+ /// better performance.
+ pub fn new_generic(
+ tcx: TyCtxt<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ def_id: DefId,
+ analysis: A,
+ ) -> Self {
+ Self::new(tcx, body, def_id, analysis, None)
+ }
+
+ fn new(
+ tcx: TyCtxt<'tcx>,
+ body: &'a mir::Body<'tcx>,
+ def_id: DefId,
+ analysis: A,
+ trans_for_block: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+ ) -> Self {
+ let bits_per_block = analysis.bits_per_block(body);
+
+ let bottom_value_set = if A::BOTTOM_VALUE == true {
+ BitSet::new_filled(bits_per_block)
+ } else {
+ BitSet::new_empty(bits_per_block)
+ };
+
+ let mut entry_sets = IndexVec::from_elem(bottom_value_set, body.basic_blocks());
+ analysis.initialize_start_block(body, &mut entry_sets[mir::START_BLOCK]);
+
+ Engine {
+ analysis,
+ bits_per_block,
+ tcx,
+ body,
+ def_id,
+ dead_unwinds: None,
+ entry_sets,
+ trans_for_block,
+ }
+ }
+
+ /// Signals that we do not want dataflow state to propagate across unwind edges for these
+ /// `BasicBlock`s.
+ ///
+ /// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
+ /// unwind during execution. Otherwise, your dataflow results will not be correct.
+ pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
+ self.dead_unwinds = Some(dead_unwinds);
+ self
+ }
+
+ /// Computes the fixpoint for this dataflow problem and returns it.
+ pub fn iterate_to_fixpoint(mut self) -> Results<'tcx, A> {
+ let mut temp_state = BitSet::new_empty(self.bits_per_block);
+
+ let mut dirty_queue: WorkQueue<BasicBlock> =
+ WorkQueue::with_none(self.body.basic_blocks().len());
+
+ for (bb, _) in traversal::reverse_postorder(self.body) {
+ dirty_queue.insert(bb);
+ }
+
+ // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
+ // be processed after the ones added above.
+ for bb in self.body.basic_blocks().indices() {
+ dirty_queue.insert(bb);
+ }
+
+ while let Some(bb) = dirty_queue.pop() {
+ let bb_data = &self.body[bb];
+ let on_entry = &self.entry_sets[bb];
+
+ temp_state.overwrite(on_entry);
+ self.apply_whole_block_effect(&mut temp_state, bb, bb_data);
+
+ self.propagate_bits_into_graph_successors_of(
+ &mut temp_state,
+ (bb, bb_data),
+ &mut dirty_queue,
+ );
+ }
+
+ let Engine { tcx, body, def_id, trans_for_block, entry_sets, analysis, .. } = self;
+ let results = Results { analysis, entry_sets };
+
+ let res = write_graphviz_results(tcx, def_id, body, &results, trans_for_block);
+ if let Err(e) = res {
+ warn!("Failed to write graphviz dataflow results: {}", e);
+ }
+
+ results
+ }
+
+ /// Applies the cumulative effect of an entire block, excluding the call return effect if one
+ /// exists.
+ fn apply_whole_block_effect(
+ &self,
+ state: &mut BitSet<A::Idx>,
+ block: BasicBlock,
+ block_data: &mir::BasicBlockData<'tcx>,
+ ) {
+ // Use the cached block transfer function if available.
+ if let Some(trans_for_block) = &self.trans_for_block {
+ trans_for_block[block].apply(state);
+ return;
+ }
+
+ // Otherwise apply effects one-by-one.
+
+ for (statement_index, statement) in block_data.statements.iter().enumerate() {
+ let location = Location { block, statement_index };
+ self.analysis.apply_before_statement_effect(state, statement, location);
+ self.analysis.apply_statement_effect(state, statement, location);
+ }
+
+ let terminator = block_data.terminator();
+ let location = Location { block, statement_index: block_data.statements.len() };
+ self.analysis.apply_before_terminator_effect(state, terminator, location);
+ self.analysis.apply_terminator_effect(state, terminator, location);
+ }
+
+ fn propagate_bits_into_graph_successors_of(
+ &mut self,
+ in_out: &mut BitSet<A::Idx>,
+ (bb, bb_data): (BasicBlock, &'a mir::BasicBlockData<'tcx>),
+ dirty_list: &mut WorkQueue<BasicBlock>,
+ ) {
+ use mir::TerminatorKind::*;
+
+ match bb_data.terminator().kind {
+ Return | Resume | Abort | GeneratorDrop | Unreachable => {}
+
+ Goto { target }
+ | Assert { target, cleanup: None, .. }
+ | Yield { resume: target, drop: None, .. }
+ | Drop { target, location: _, unwind: None }
+ | DropAndReplace { target, value: _, location: _, unwind: None } => {
+ self.propagate_bits_into_entry_set_for(in_out, target, dirty_list)
+ }
+
+ Yield { resume: target, drop: Some(drop), .. } => {
+ self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
+ self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
+ }
+
+ Assert { target, cleanup: Some(unwind), .. }
+ | Drop { target, location: _, unwind: Some(unwind) }
+ | DropAndReplace { target, value: _, location: _, unwind: Some(unwind) } => {
+ self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
+ if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+ self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+ }
+ }
+
+ SwitchInt { ref targets, .. } => {
+ for target in targets {
+ self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list);
+ }
+ }
+
+ Call { cleanup, ref destination, ref func, ref args, .. } => {
+ if let Some(unwind) = cleanup {
+ if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+ self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+ }
+ }
+
+ if let Some((ref dest_place, dest_bb)) = *destination {
+ // N.B.: This must be done *last*, otherwise the unwind path will see the call
+ // return effect.
+ self.analysis.apply_call_return_effect(in_out, bb, func, args, dest_place);
+ self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
+ }
+ }
+
+ FalseEdges { real_target, imaginary_target } => {
+ self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
+ self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
+ }
+
+ FalseUnwind { real_target, unwind } => {
+ self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
+ if let Some(unwind) = unwind {
+ if self.dead_unwinds.map_or(true, |bbs| !bbs.contains(bb)) {
+ self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
+ }
+ }
+ }
+ }
+ }
+
+ fn propagate_bits_into_entry_set_for(
+ &mut self,
+ in_out: &BitSet<A::Idx>,
+ bb: BasicBlock,
+ dirty_queue: &mut WorkQueue<BasicBlock>,
+ ) {
+ let entry_set = &mut self.entry_sets[bb];
+ let set_changed = self.analysis.join(entry_set, &in_out);
+ if set_changed {
+ dirty_queue.insert(bb);
+ }
+ }
+}
+
+// Graphviz
+
+/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
+/// `rustc_mir` attributes.
+fn write_graphviz_results<A>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ body: &mir::Body<'tcx>,
+ results: &Results<'tcx, A>,
+ block_transfer_functions: Option<IndexVec<BasicBlock, GenKillSet<A::Idx>>>,
+) -> std::io::Result<()>
+where
+ A: Analysis<'tcx>,
+{
+ let attrs = match RustcMirAttrs::parse(tcx, def_id) {
+ Ok(attrs) => attrs,
+
+ // Invalid `rustc_mir` attrs will be reported using `span_err`.
+ Err(()) => return Ok(()),
+ };
+
+ let path = match attrs.output_path(A::NAME) {
+ Some(path) => path,
+ None => return Ok(()),
+ };
+
+ let bits_per_block = results.analysis.bits_per_block(body);
+
+ let mut formatter: Box<dyn graphviz::StateFormatter<'tcx, _>> = match attrs.formatter {
+ Some(sym::two_phase) => Box::new(graphviz::TwoPhaseDiff::new(bits_per_block)),
+ Some(sym::gen_kill) => {
+ if let Some(trans_for_block) = block_transfer_functions {
+ Box::new(graphviz::BlockTransferFunc::new(body, trans_for_block))
+ } else {
+ Box::new(graphviz::SimpleDiff::new(bits_per_block))
+ }
+ }
+
+ // Default to the `SimpleDiff` output style.
+ _ => Box::new(graphviz::SimpleDiff::new(bits_per_block)),
+ };
+
+ debug!("printing dataflow results for {:?} to {}", def_id, path.display());
+ let mut buf = Vec::new();
+
+ let graphviz = graphviz::Formatter::new(body, def_id, results, &mut *formatter);
+ dot::render(&graphviz, &mut buf)?;
+ fs::write(&path, buf)?;
+ Ok(())
+}
+
+#[derive(Default)]
+struct RustcMirAttrs {
+ basename_and_suffix: Option<PathBuf>,
+ formatter: Option<Symbol>,
+}
+
+impl RustcMirAttrs {
+ fn parse(tcx: TyCtxt<'tcx>, def_id: DefId) -> Result<Self, ()> {
+ let attrs = tcx.get_attrs(def_id);
+
+ let mut result = Ok(());
+ let mut ret = RustcMirAttrs::default();
+
+ let 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()));
+
+ for attr in rustc_mir_attrs {
+ let attr_result = if attr.check_name(sym::borrowck_graphviz_postflow) {
+ Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
+ let path = PathBuf::from(s.to_string());
+ match path.file_name() {
+ Some(_) => Ok(path),
+ None => {
+ tcx.sess.span_err(attr.span(), "path must end in a filename");
+ Err(())
+ }
+ }
+ })
+ } else if attr.check_name(sym::borrowck_graphviz_format) {
+ Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
+ sym::gen_kill | sym::two_phase => Ok(s),
+ _ => {
+ tcx.sess.span_err(attr.span(), "unknown formatter");
+ Err(())
+ }
+ })
+ } else {
+ Ok(())
+ };
+
+ result = result.and(attr_result);
+ }
+
+ result.map(|()| ret)
+ }
+
+ fn set_field<T>(
+ field: &mut Option<T>,
+ tcx: TyCtxt<'tcx>,
+ attr: &ast::NestedMetaItem,
+ mapper: impl FnOnce(Symbol) -> Result<T, ()>,
+ ) -> Result<(), ()> {
+ if field.is_some() {
+ tcx.sess
+ .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
+
+ return Err(());
+ }
+
+ if let Some(s) = attr.value_str() {
+ *field = Some(mapper(s)?);
+ Ok(())
+ } else {
+ tcx.sess
+ .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
+ Err(())
+ }
+ }
+
+ /// Returns the path where dataflow results should be written, or `None`
+ /// `borrowck_graphviz_postflow` was not specified.
+ ///
+ /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
+ ///
+ /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
+ fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
+ let mut ret = self.basename_and_suffix.as_ref().cloned()?;
+ let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
+
+ let mut file_name: OsString = analysis_name.into();
+ file_name.push("_");
+ file_name.push(suffix);
+ ret.set_file_name(file_name);
+
+ Some(ret)
+ }
+}
diff --git a/src/librustc_mir/dataflow/generic/graphviz.rs b/src/librustc_mir/dataflow/generic/graphviz.rs
index e843956..fdf86e7 100644
--- a/src/librustc_mir/dataflow/generic/graphviz.rs
+++ b/src/librustc_mir/dataflow/generic/graphviz.rs
@@ -1,13 +1,14 @@
+//! A helpful diagram for debugging dataflow problems.
+
use std::cell::RefCell;
-use std::io::{self, Write};
-use std::{ops, str};
+use std::{io, ops, str};
use rustc::mir::{self, BasicBlock, Body, Location};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::{BitSet, HybridBitSet};
-use rustc_index::vec::Idx;
+use rustc_index::vec::{Idx, IndexVec};
-use super::{Analysis, Results, ResultsRefCursor};
+use super::{Analysis, GenKillSet, Results, ResultsRefCursor};
use crate::util::graphviz_safe_def_name;
pub struct Formatter<'a, 'tcx, A>
@@ -25,11 +26,16 @@
where
A: Analysis<'tcx>,
{
- pub fn new(body: &'a Body<'tcx>, def_id: DefId, results: &'a Results<'tcx, A>) -> Self {
+ pub fn new(
+ body: &'a Body<'tcx>,
+ def_id: DefId,
+ results: &'a Results<'tcx, A>,
+ state_formatter: &'a mut dyn StateFormatter<'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),
+ state_formatter,
};
Formatter { body, def_id, block_formatter: RefCell::new(block_formatter) }
@@ -117,15 +123,21 @@
where
A: Analysis<'tcx>,
{
- prev_state: BitSet<A::Idx>,
results: ResultsRefCursor<'a, 'a, 'tcx, A>,
bg: Background,
+ state_formatter: &'a mut dyn StateFormatter<'tcx, A>,
}
impl<A> BlockFormatter<'a, 'tcx, A>
where
A: Analysis<'tcx>,
{
+ const HEADER_COLOR: &'static str = "#a0a0a0";
+
+ fn num_state_columns(&self) -> usize {
+ std::cmp::max(1, self.state_formatter.column_names().len())
+ }
+
fn toggle_background(&mut self) -> Background {
let bg = self.bg;
self.bg = !bg;
@@ -164,196 +176,470 @@
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""##,
- )?;
+ // A + B: Block header
+ if self.state_formatter.column_names().is_empty() {
+ self.write_block_header_simple(w, block)?;
+ } else {
+ self.write_block_header_with_state_columns(w, block)?;
+ }
// C: Entry state
self.bg = Background::Light;
self.results.seek_to_block_start(block);
- self.write_row_with_curr_state(w, "", "(on entry)")?;
- self.prev_state.overwrite(self.results.get());
+ self.write_row_with_full_state(w, "", "(on_entry)")?;
// 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());
+ let statement_str = format!("{:?}", statement);
+ self.write_row_for_location(w, &i.to_string(), &statement_str, location)?;
}
// E: Terminator transfer function
let terminator = body[block].terminator();
- let location = body.terminator_loc(block);
+ let terminator_loc = body.terminator_loc(block);
+ let mut terminator_str = String::new();
+ terminator.kind.fmt_head(&mut terminator_str).unwrap();
- 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());
+ self.write_row_for_location(w, "T", &terminator_str, terminator_loc)?;
// F: Exit state
+ self.results.seek_after(terminator_loc);
if let mir::TerminatorKind::Call { destination: Some(_), .. } = &terminator.kind {
- self.write_row_with_curr_state(w, "", "(on unwind)")?;
+ self.write_row_with_full_state(w, "", "(on unwind)")?;
- self.results.seek_after_assume_call_returns(location);
- self.write_row_with_curr_diff(w, "", "(on successful return)")?;
+ let num_state_columns = self.num_state_columns();
+ self.write_row(w, "", "(on successful return)", |this, w, fmt| {
+ write!(
+ w,
+ r#"<td colspan="{colspan}" {fmt} align="left">"#,
+ colspan = num_state_columns,
+ fmt = fmt,
+ )?;
+
+ let state_on_unwind = this.results.get().clone();
+ this.results.seek_after_assume_call_returns(terminator_loc);
+ write_diff(w, this.results.analysis(), &state_on_unwind, this.results.get())?;
+
+ write!(w, "</td>")
+ })?;
} else {
- self.write_row_with_curr_state(w, "", "(on exit)")?;
+ self.write_row_with_full_state(w, "", "(on exit)")?;
}
write!(w, "</table>")
}
- fn write_row_with_curr_state(
+ fn write_block_header_simple(
&mut self,
w: &mut impl io::Write,
- i: &str,
- mir: &str,
+ block: BasicBlock,
) -> io::Result<()> {
- let bg = self.toggle_background();
+ // +-------------------------------------------------+
+ // A | bb4 |
+ // +-----------------------------------+-------------+
+ // B | MIR | STATE |
+ // +-+---------------------------------+-------------+
+ // | | ... | |
- let mut out = Vec::new();
- write!(&mut out, "{{")?;
- pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
- write!(&mut out, "}}")?;
-
+ // A
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()),
+ concat!("<tr>", r#"<td colspan="3" sides="tl">bb{block_id}</td>"#, "</tr>",),
+ block_id = block.index(),
+ )?;
+
+ // B
+ write!(
+ w,
+ concat!(
+ "<tr>",
+ r#"<td colspan="2" {fmt}>MIR</td>"#,
+ r#"<td {fmt}>STATE</td>"#,
+ "</tr>",
+ ),
+ fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR),
)
}
- fn write_row_with_curr_diff(
+ fn write_block_header_with_state_columns(
+ &mut self,
+ w: &mut impl io::Write,
+ block: BasicBlock,
+ ) -> io::Result<()> {
+ // +------------------------------------+-------------+
+ // A | bb4 | STATE |
+ // +------------------------------------+------+------+
+ // B | MIR | GEN | KILL |
+ // +-+----------------------------------+------+------+
+ // | | ... | | |
+
+ let state_column_names = self.state_formatter.column_names();
+
+ // A
+ write!(
+ w,
+ concat!(
+ "<tr>",
+ r#"<td {fmt} colspan="2">bb{block_id}</td>"#,
+ r#"<td {fmt} colspan="{num_state_cols}">STATE</td>"#,
+ "</tr>",
+ ),
+ fmt = "sides=\"tl\"",
+ num_state_cols = state_column_names.len(),
+ block_id = block.index(),
+ )?;
+
+ // B
+ let fmt = format!("bgcolor=\"{}\" sides=\"tl\"", Self::HEADER_COLOR);
+ write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?;
+
+ for name in state_column_names {
+ write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?;
+ }
+
+ write!(w, "</tr>")
+ }
+
+ /// Write a row with the given index and MIR, using the function argument to fill in the
+ /// "STATE" column(s).
+ fn write_row<W: io::Write>(
+ &mut self,
+ w: &mut W,
+ i: &str,
+ mir: &str,
+ f: impl FnOnce(&mut Self, &mut W, &str) -> io::Result<()>,
+ ) -> io::Result<()> {
+ let bg = self.toggle_background();
+ let fmt = format!("sides=\"tl\" {}", bg.attr());
+
+ write!(
+ w,
+ concat!(
+ "<tr>",
+ r#"<td {fmt} align="right">{i}</td>"#,
+ r#"<td {fmt} align="left">{mir}</td>"#,
+ ),
+ i = i,
+ fmt = fmt,
+ mir = dot::escape_html(mir),
+ )?;
+
+ f(self, w, &fmt)?;
+ write!(w, "</tr>")
+ }
+
+ fn write_row_with_full_state(
&mut self,
w: &mut impl io::Write,
i: &str,
mir: &str,
) -> io::Result<()> {
- let bg = self.toggle_background();
- let analysis = self.results.analysis();
+ self.write_row(w, i, mir, |this, w, fmt| {
+ let state = this.results.get();
+ let analysis = this.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()),
+ r#"<td colspan="{colspan}" {fmt} align="left">{{"#,
+ colspan = this.num_state_columns(),
+ fmt = fmt,
)?;
- }
+ pretty_print_state_elems(w, analysis, state.iter(), ",", LIMIT_40_ALIGN_1)?;
+ write!(w, "}}</td>")
+ })
+ }
- 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>")
+ fn write_row_for_location(
+ &mut self,
+ w: &mut impl io::Write,
+ i: &str,
+ mir: &str,
+ location: Location,
+ ) -> io::Result<()> {
+ self.write_row(w, i, mir, |this, w, fmt| {
+ this.state_formatter.write_state_for_location(w, fmt, &mut this.results, location)
+ })
}
}
-/// The operations required to transform one `BitSet` into another.
-struct BitSetDiff<T: Idx> {
- set: HybridBitSet<T>,
- clear: HybridBitSet<T>,
+/// Controls what gets printed under the `STATE` header.
+pub trait StateFormatter<'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ /// The columns that will get printed under `STATE`.
+ fn column_names(&self) -> &[&str];
+
+ fn write_state_for_location(
+ &mut self,
+ w: &mut dyn io::Write,
+ fmt: &str,
+ results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+ location: Location,
+ ) -> io::Result<()>;
}
-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();
+/// Prints a single column containing the state vector immediately *after* each statement.
+pub struct SimpleDiff<T: Idx> {
+ prev_state: BitSet<T>,
+ prev_loc: Location,
+}
- 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 }
+impl<T: Idx> SimpleDiff<T> {
+ #![allow(unused)]
+ pub fn new(bits_per_block: usize) -> Self {
+ SimpleDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
}
}
-/// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
-/// list.
+impl<A> StateFormatter<'tcx, A> for SimpleDiff<A::Idx>
+where
+ A: Analysis<'tcx>,
+{
+ fn column_names(&self) -> &[&str] {
+ &[]
+ }
+
+ fn write_state_for_location(
+ &mut self,
+ mut w: &mut dyn io::Write,
+ fmt: &str,
+ results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+ location: Location,
+ ) -> io::Result<()> {
+ if location.statement_index == 0 {
+ results.seek_to_block_start(location.block);
+ self.prev_state.overwrite(results.get());
+ } else {
+ // Ensure that we are visiting statements in order, so `prev_state` is correct.
+ assert_eq!(self.prev_loc.successor_within_block(), location);
+ }
+
+ self.prev_loc = location;
+ write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
+ results.seek_before(location);
+ let curr_state = results.get();
+ write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+ self.prev_state.overwrite(curr_state);
+ write!(w, "</td>")
+ }
+}
+
+/// Prints two state columns, one containing only the "before" effect of each statement and one
+/// containing the full effect.
+pub struct TwoPhaseDiff<T: Idx> {
+ prev_state: BitSet<T>,
+ prev_loc: Location,
+}
+
+impl<T: Idx> TwoPhaseDiff<T> {
+ #![allow(unused)]
+ pub fn new(bits_per_block: usize) -> Self {
+ TwoPhaseDiff { prev_state: BitSet::new_empty(bits_per_block), prev_loc: Location::START }
+ }
+}
+
+impl<A> StateFormatter<'tcx, A> for TwoPhaseDiff<A::Idx>
+where
+ A: Analysis<'tcx>,
+{
+ fn column_names(&self) -> &[&str] {
+ &["ENTRY", " EXIT"]
+ }
+
+ fn write_state_for_location(
+ &mut self,
+ mut w: &mut dyn io::Write,
+ fmt: &str,
+ results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+ location: Location,
+ ) -> io::Result<()> {
+ if location.statement_index == 0 {
+ results.seek_to_block_start(location.block);
+ self.prev_state.overwrite(results.get());
+ } else {
+ // Ensure that we are visiting statements in order, so `prev_state` is correct.
+ assert_eq!(self.prev_loc.successor_within_block(), location);
+ }
+
+ self.prev_loc = location;
+
+ // Entry
+
+ write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
+ results.seek_before(location);
+ let curr_state = results.get();
+ write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+ self.prev_state.overwrite(curr_state);
+ write!(w, "</td>")?;
+
+ // Exit
+
+ write!(w, r#"<td {fmt} align="left">"#, fmt = fmt)?;
+ results.seek_after(location);
+ let curr_state = results.get();
+ write_diff(&mut w, results.analysis(), &self.prev_state, curr_state)?;
+ self.prev_state.overwrite(curr_state);
+ write!(w, "</td>")
+ }
+}
+
+/// Prints the gen/kill set for the entire block.
+pub struct BlockTransferFunc<'a, 'tcx, T: Idx> {
+ body: &'a mir::Body<'tcx>,
+ trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
+}
+
+impl<T: Idx> BlockTransferFunc<'mir, 'tcx, T> {
+ #![allow(unused)]
+ pub fn new(
+ body: &'mir mir::Body<'tcx>,
+ trans_for_block: IndexVec<BasicBlock, GenKillSet<T>>,
+ ) -> Self {
+ BlockTransferFunc { body, trans_for_block }
+ }
+}
+
+impl<A> StateFormatter<'tcx, A> for BlockTransferFunc<'mir, 'tcx, A::Idx>
+where
+ A: Analysis<'tcx>,
+{
+ fn column_names(&self) -> &[&str] {
+ &["GEN", "KILL"]
+ }
+
+ fn write_state_for_location(
+ &mut self,
+ mut w: &mut dyn io::Write,
+ fmt: &str,
+ results: &mut ResultsRefCursor<'_, '_, 'tcx, A>,
+ location: Location,
+ ) -> io::Result<()> {
+ // Only print a single row.
+ if location.statement_index != 0 {
+ return Ok(());
+ }
+
+ let block_trans = &self.trans_for_block[location.block];
+ let rowspan = self.body.basic_blocks()[location.block].statements.len();
+
+ for set in &[&block_trans.gen, &block_trans.kill] {
+ write!(
+ w,
+ r#"<td {fmt} rowspan="{rowspan}" align="center">"#,
+ fmt = fmt,
+ rowspan = rowspan
+ )?;
+
+ pretty_print_state_elems(&mut w, results.analysis(), set.iter(), "\n", None)?;
+ write!(w, "</td>")?;
+ }
+
+ Ok(())
+ }
+}
+
+/// Writes two lines, one containing the added bits and one the removed bits.
+fn write_diff<A: Analysis<'tcx>>(
+ w: &mut impl io::Write,
+ analysis: &A,
+ from: &BitSet<A::Idx>,
+ to: &BitSet<A::Idx>,
+) -> io::Result<()> {
+ 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: Implement a lazy iterator over the symmetric difference of two bitsets.
+ for i in (0..len).map(|i| A::Idx::new(i)) {
+ match (from.contains(i), to.contains(i)) {
+ (false, true) => set.insert(i),
+ (true, false) => clear.insert(i),
+ _ => continue,
+ };
+ }
+
+ if !set.is_empty() {
+ write!(w, r#"<font color="darkgreen">+"#)?;
+ pretty_print_state_elems(w, analysis, set.iter(), ",", LIMIT_40_ALIGN_1)?;
+ write!(w, r#"</font>"#)?;
+ }
+
+ if !set.is_empty() && !clear.is_empty() {
+ write!(w, "<br/>")?;
+ }
+
+ if !clear.is_empty() {
+ write!(w, r#"<font color="red">-"#)?;
+ pretty_print_state_elems(w, analysis, clear.iter(), ",", LIMIT_40_ALIGN_1)?;
+ write!(w, r#"</font>"#)?;
+ }
+
+ Ok(())
+}
+
+/// Line break policy that breaks at 40 characters and starts the next line with a single space.
+const LIMIT_40_ALIGN_1: Option<LineBreak> = Some(LineBreak { sequence: "<br/> ", limit: 40 });
+
+struct LineBreak {
+ sequence: &'static str,
+ limit: usize,
+}
+
+/// Formats each `elem` using the pretty printer provided by `analysis` into a list with the given
+/// separator (`sep`).
+///
+/// Optionally, it will break lines using the given character sequence (usually `<br/>`) and
+/// character limit.
fn pretty_print_state_elems<A>(
w: &mut impl io::Write,
analysis: &A,
elems: impl Iterator<Item = A::Idx>,
-) -> io::Result<()>
+ sep: &str,
+ line_break: Option<LineBreak>,
+) -> io::Result<bool>
where
A: Analysis<'tcx>,
{
+ let sep_width = sep.chars().count();
+
+ let mut buf = Vec::new();
+
let mut first = true;
+ let mut curr_line_width = 0;
+ let mut line_break_inserted = false;
+
for idx in elems {
if first {
first = false;
} else {
- write!(w, ",")?;
+ write!(w, "{}", sep)?;
+ curr_line_width += sep_width;
}
- analysis.pretty_print_idx(w, idx)?;
+ buf.clear();
+ analysis.pretty_print_idx(&mut buf, idx)?;
+ let idx_str =
+ str::from_utf8(&buf).expect("Output of `pretty_print_idx` must be valid UTF-8");
+ let escaped = dot::escape_html(idx_str);
+ let escaped_width = escaped.chars().count();
+
+ if let Some(line_break) = &line_break {
+ if curr_line_width + sep_width + escaped_width > line_break.limit {
+ write!(w, "{}", line_break.sequence)?;
+ line_break_inserted = true;
+ curr_line_width = 0;
+ }
+ }
+
+ write!(w, "{}", escaped)?;
+ curr_line_width += escaped_width;
}
- Ok(())
+ Ok(line_break_inserted)
}
/// The background color used for zebra-striping the table.
diff --git a/src/librustc_mir/dataflow/generic/mod.rs b/src/librustc_mir/dataflow/generic/mod.rs
new file mode 100644
index 0000000..c9b4f87
--- /dev/null
+++ b/src/librustc_mir/dataflow/generic/mod.rs
@@ -0,0 +1,358 @@
+//! A framework that can express both [gen-kill] and generic dataflow problems.
+//!
+//! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The
+//! interface in this module will eventually [replace that one][design-meeting].
+//!
+//! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis`
+//! trait. If your transfer function can be expressed with only gen/kill operations, prefer
+//! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using
+//! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
+//! inspect the fixpoint solution to your dataflow problem.
+//!
+//! ```ignore(cross-crate-imports)
+//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
+//! let analysis = MyAnalysis::new();
+//!
+//! // If `MyAnalysis` implements `GenKillAnalysis`.
+//! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
+//!
+//! // If `MyAnalysis` implements `Analysis`.
+//! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
+//!
+//! let mut cursor = ResultsCursor::new(body, results);
+//!
+//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
+//! cursor.seek_after(Location { block: START_BLOCK, statement_index });
+//! let state = cursor.get();
+//! println!("{:?}", state);
+//! }
+//! }
+//! ```
+//!
+//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
+//! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202
+
+use std::io;
+
+use rustc::mir::{self, BasicBlock, Location};
+use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::vec::{Idx, IndexVec};
+
+use crate::dataflow::BottomValue;
+
+mod cursor;
+mod engine;
+mod graphviz;
+
+pub use self::cursor::{ResultsCursor, ResultsRefCursor};
+pub use self::engine::Engine;
+
+/// A dataflow analysis that has converged to fixpoint.
+pub struct Results<'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ pub analysis: A,
+ entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
+}
+
+impl<A> Results<'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ /// Creates a `ResultsCursor` that can inspect these `Results`.
+ pub fn into_results_cursor(self, body: &'mir mir::Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
+ ResultsCursor::new(body, self)
+ }
+
+ /// Gets the entry set for the given block.
+ pub fn entry_set_for_block(&self, block: BasicBlock) -> &BitSet<A::Idx> {
+ &self.entry_sets[block]
+ }
+}
+
+/// Define the domain of a dataflow problem.
+///
+/// This trait specifies the lattice on which this analysis operates. For now, this must be a
+/// powerset of values of type `Idx`. The elements of this lattice are represented with a `BitSet`
+/// and referred to as the state vector.
+///
+/// This trait also defines the initial value for the dataflow state upon entry to the
+/// `START_BLOCK`, as well as some names used to refer to this analysis when debugging.
+pub trait AnalysisDomain<'tcx>: BottomValue {
+ /// The type of the elements in the state vector.
+ type Idx: Idx;
+
+ /// A descriptive name for this analysis. Used only for debugging.
+ ///
+ /// This name should be brief and contain no spaces, periods or other characters that are not
+ /// suitable as part of a filename.
+ const NAME: &'static str;
+
+ /// The size of the state vector.
+ fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;
+
+ /// Mutates the entry set of the `START_BLOCK` to contain the initial state for dataflow
+ /// analysis.
+ fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>);
+
+ /// Prints an element in the state vector for debugging.
+ fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
+ write!(w, "{:?}", idx)
+ }
+}
+
+/// A dataflow problem with an arbitrarily complex transfer function.
+pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
+ /// Updates the current dataflow state with the effect of evaluating a statement.
+ fn apply_statement_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ statement: &mir::Statement<'tcx>,
+ location: Location,
+ );
+
+ /// Updates the current dataflow state with an effect that occurs immediately *before* the
+ /// given statement.
+ ///
+ /// This method is useful if the consumer of the results of this analysis needs only to observe
+ /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
+ /// analyses should not implement this without implementing `apply_statement_effect`.
+ fn apply_before_statement_effect(
+ &self,
+ _state: &mut BitSet<Self::Idx>,
+ _statement: &mir::Statement<'tcx>,
+ _location: Location,
+ ) {
+ }
+
+ /// Updates the current dataflow state with the effect of evaluating a terminator.
+ ///
+ /// The effect of a successful return from a `Call` terminator should **not** be accounted for
+ /// in this function. That should go in `apply_call_return_effect`. For example, in the
+ /// `InitializedPlaces` analyses, the return place for a function call is not marked as
+ /// initialized here.
+ fn apply_terminator_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ );
+
+ /// Updates the current dataflow state with an effect that occurs immediately *before* the
+ /// given terminator.
+ ///
+ /// This method is useful if the consumer of the results of this analysis needs only to observe
+ /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
+ /// analyses should not implement this without implementing `apply_terminator_effect`.
+ fn apply_before_terminator_effect(
+ &self,
+ _state: &mut BitSet<Self::Idx>,
+ _terminator: &mir::Terminator<'tcx>,
+ _location: Location,
+ ) {
+ }
+
+ /// Updates the current dataflow state with the effect of a successful return from a `Call`
+ /// terminator.
+ ///
+ /// This is separate from `apply_terminator_effect` to properly track state across unwind
+ /// edges.
+ fn apply_call_return_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ block: BasicBlock,
+ func: &mir::Operand<'tcx>,
+ args: &[mir::Operand<'tcx>],
+ return_place: &mir::Place<'tcx>,
+ );
+}
+
+/// A gen/kill dataflow problem.
+///
+/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
+/// allow modification of the dataflow state via "gen" and "kill" operations. By defining transfer
+/// functions for each statement in this way, the transfer function for an entire basic block can
+/// be computed efficiently.
+///
+/// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis`.
+pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
+ /// See `Analysis::apply_statement_effect`.
+ fn statement_effect(
+ &self,
+ trans: &mut impl GenKill<Self::Idx>,
+ statement: &mir::Statement<'tcx>,
+ location: Location,
+ );
+
+ /// See `Analysis::apply_before_statement_effect`.
+ fn before_statement_effect(
+ &self,
+ _trans: &mut impl GenKill<Self::Idx>,
+ _statement: &mir::Statement<'tcx>,
+ _location: Location,
+ ) {
+ }
+
+ /// See `Analysis::apply_terminator_effect`.
+ fn terminator_effect(
+ &self,
+ trans: &mut impl GenKill<Self::Idx>,
+ terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ );
+
+ /// See `Analysis::apply_before_terminator_effect`.
+ fn before_terminator_effect(
+ &self,
+ _trans: &mut impl GenKill<Self::Idx>,
+ _terminator: &mir::Terminator<'tcx>,
+ _location: Location,
+ ) {
+ }
+
+ /// See `Analysis::apply_call_return_effect`.
+ fn call_return_effect(
+ &self,
+ trans: &mut impl GenKill<Self::Idx>,
+ block: BasicBlock,
+ func: &mir::Operand<'tcx>,
+ args: &[mir::Operand<'tcx>],
+ return_place: &mir::Place<'tcx>,
+ );
+}
+
+impl<A> Analysis<'tcx> for A
+where
+ A: GenKillAnalysis<'tcx>,
+{
+ fn apply_statement_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ self.statement_effect(state, statement, location);
+ }
+
+ fn apply_before_statement_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ self.before_statement_effect(state, statement, location);
+ }
+
+ fn apply_terminator_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ ) {
+ self.terminator_effect(state, terminator, location);
+ }
+
+ fn apply_before_terminator_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ ) {
+ self.before_terminator_effect(state, terminator, location);
+ }
+
+ fn apply_call_return_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ block: BasicBlock,
+ func: &mir::Operand<'tcx>,
+ args: &[mir::Operand<'tcx>],
+ return_place: &mir::Place<'tcx>,
+ ) {
+ self.call_return_effect(state, block, func, args, return_place);
+ }
+}
+
+/// The legal operations for a transfer function in a gen/kill problem.
+///
+/// This abstraction exists because there are two different contexts in which we call the methods in
+/// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
+/// applied multiple times, such as when computing the cumulative transfer function for each block.
+/// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
+/// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
+/// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
+/// building up a `GenKillSet` and then throwing it away.
+pub trait GenKill<T> {
+ /// Inserts `elem` into the state vector.
+ fn gen(&mut self, elem: T);
+
+ /// Removes `elem` from the state vector.
+ fn kill(&mut self, elem: T);
+
+ /// Calls `gen` for each element in `elems`.
+ fn gen_all(&mut self, elems: impl IntoIterator<Item = T>) {
+ for elem in elems {
+ self.gen(elem);
+ }
+ }
+
+ /// Calls `kill` for each element in `elems`.
+ fn kill_all(&mut self, elems: impl IntoIterator<Item = T>) {
+ for elem in elems {
+ self.kill(elem);
+ }
+ }
+}
+
+/// Stores a transfer function for a gen/kill problem.
+///
+/// Calling `gen`/`kill` on a `GenKillSet` will "build up" a transfer function so that it can be
+/// applied multiple times efficiently. When there are multiple calls to `gen` and/or `kill` for
+/// the same element, the most recent one takes precedence.
+#[derive(Clone)]
+pub struct GenKillSet<T: Idx> {
+ gen: HybridBitSet<T>,
+ kill: HybridBitSet<T>,
+}
+
+impl<T: Idx> GenKillSet<T> {
+ /// Creates a new transfer function that will leave the dataflow state unchanged.
+ pub fn identity(universe: usize) -> Self {
+ GenKillSet {
+ gen: HybridBitSet::new_empty(universe),
+ kill: HybridBitSet::new_empty(universe),
+ }
+ }
+
+ /// Applies this transfer function to the given state vector.
+ pub fn apply(&self, state: &mut BitSet<T>) {
+ state.union(&self.gen);
+ state.subtract(&self.kill);
+ }
+}
+
+impl<T: Idx> GenKill<T> for GenKillSet<T> {
+ fn gen(&mut self, elem: T) {
+ self.gen.insert(elem);
+ self.kill.remove(elem);
+ }
+
+ fn kill(&mut self, elem: T) {
+ self.kill.insert(elem);
+ self.gen.remove(elem);
+ }
+}
+
+impl<T: Idx> GenKill<T> for BitSet<T> {
+ fn gen(&mut self, elem: T) {
+ self.insert(elem);
+ }
+
+ fn kill(&mut self, elem: T) {
+ self.remove(elem);
+ }
+}
+
+#[cfg(test)]
+mod tests;
diff --git a/src/librustc_mir/dataflow/generic/tests.rs b/src/librustc_mir/dataflow/generic/tests.rs
new file mode 100644
index 0000000..50d4bdb
--- /dev/null
+++ b/src/librustc_mir/dataflow/generic/tests.rs
@@ -0,0 +1,332 @@
+//! A test for the logic that updates the state in a `ResultsCursor` during seek.
+
+use rustc::mir::{self, BasicBlock, Location};
+use rustc::ty;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_span::DUMMY_SP;
+
+use super::*;
+use crate::dataflow::BottomValue;
+
+/// Returns `true` if the given location points to a `Call` terminator that can return
+/// successfully.
+fn is_call_terminator_non_diverging(body: &mir::Body<'_>, loc: Location) -> bool {
+ loc == body.terminator_loc(loc.block)
+ && matches!(
+ body[loc.block].terminator().kind,
+ mir::TerminatorKind::Call { destination: Some(_), .. }
+ )
+}
+
+/// Creates a `mir::Body` with a few disconnected basic blocks.
+///
+/// This is the `Body` that will be used by the `MockAnalysis` below. The shape of its CFG is not
+/// important.
+fn mock_body() -> mir::Body<'static> {
+ let source_info = mir::SourceInfo { scope: mir::OUTERMOST_SOURCE_SCOPE, span: DUMMY_SP };
+
+ let mut blocks = IndexVec::new();
+ let mut block = |n, kind| {
+ let nop = mir::Statement { source_info, kind: mir::StatementKind::Nop };
+
+ blocks.push(mir::BasicBlockData {
+ statements: std::iter::repeat(&nop).cloned().take(n).collect(),
+ terminator: Some(mir::Terminator { source_info, kind }),
+ is_cleanup: false,
+ })
+ };
+
+ let dummy_place = mir::Place { local: mir::RETURN_PLACE, projection: ty::List::empty() };
+
+ block(4, mir::TerminatorKind::Return);
+ block(1, mir::TerminatorKind::Return);
+ block(
+ 2,
+ mir::TerminatorKind::Call {
+ func: mir::Operand::Copy(dummy_place.clone()),
+ args: vec![],
+ destination: Some((dummy_place.clone(), mir::START_BLOCK)),
+ cleanup: None,
+ from_hir_call: false,
+ },
+ );
+ block(3, mir::TerminatorKind::Return);
+ block(0, mir::TerminatorKind::Return);
+ block(
+ 4,
+ mir::TerminatorKind::Call {
+ func: mir::Operand::Copy(dummy_place.clone()),
+ args: vec![],
+ destination: Some((dummy_place.clone(), mir::START_BLOCK)),
+ cleanup: None,
+ from_hir_call: false,
+ },
+ );
+
+ mir::Body::new_cfg_only(blocks)
+}
+
+/// A dataflow analysis whose state is unique at every possible `SeekTarget`.
+///
+/// Uniqueness is achieved by having a *locally* unique effect before and after each statement and
+/// terminator (see `effect_at_target`) while ensuring that the entry set for each block is
+/// *globally* unique (see `mock_entry_set`).
+///
+/// For example, a `BasicBlock` with ID `2` and a `Call` terminator has the following state at each
+/// location ("+x" indicates that "x" is added to the state).
+///
+/// | Location | Before | After |
+/// |------------------------|-------------------|--------|
+/// | (on_entry) | {102} ||
+/// | Statement 0 | +0 | +1 |
+/// | statement 1 | +2 | +3 |
+/// | `Call` terminator | +4 | +5 |
+/// | (on unwind) | {102,0,1,2,3,4,5} ||
+/// | (on successful return) | +6 ||
+///
+/// The `102` in the block's entry set is derived from the basic block index and ensures that the
+/// expected state is unique across all basic blocks. Remember, it is generated by
+/// `mock_entry_sets`, not from actually running `MockAnalysis` to fixpoint.
+struct MockAnalysis<'tcx> {
+ body: &'tcx mir::Body<'tcx>,
+}
+
+impl MockAnalysis<'tcx> {
+ const BASIC_BLOCK_OFFSET: usize = 100;
+
+ /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to
+ /// avoid colliding with the statement/terminator effects.
+ fn mock_entry_set(&self, bb: BasicBlock) -> BitSet<usize> {
+ let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+ ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index());
+ ret
+ }
+
+ fn mock_entry_sets(&self) -> IndexVec<BasicBlock, BitSet<usize>> {
+ let empty = BitSet::new_empty(self.bits_per_block(self.body));
+ let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks());
+
+ for (bb, _) in self.body.basic_blocks().iter_enumerated() {
+ ret[bb] = self.mock_entry_set(bb);
+ }
+
+ ret
+ }
+
+ /// Returns the index that should be added to the dataflow state at the given target.
+ ///
+ /// This index is only unique within a given basic block. `SeekAfter` and
+ /// `SeekAfterAssumeCallReturns` have the same effect unless `target` is a `Call` terminator.
+ fn effect_at_target(&self, target: SeekTarget) -> Option<usize> {
+ use SeekTarget::*;
+
+ let idx = match target {
+ BlockStart(_) => return None,
+
+ AfterAssumeCallReturns(loc) if is_call_terminator_non_diverging(self.body, loc) => {
+ loc.statement_index * 2 + 2
+ }
+
+ Before(loc) => loc.statement_index * 2,
+ After(loc) | AfterAssumeCallReturns(loc) => loc.statement_index * 2 + 1,
+ };
+
+ assert!(idx < Self::BASIC_BLOCK_OFFSET, "Too many statements in basic block");
+ Some(idx)
+ }
+
+ /// Returns the expected state at the given `SeekTarget`.
+ ///
+ /// This is the union of index of the target basic block, the index assigned to the
+ /// target statement or terminator, and the indices of all preceding statements in the target
+ /// basic block.
+ ///
+ /// For example, the expected state when calling
+ /// `seek_before(Location { block: 2, statement_index: 2 })` would be `[102, 0, 1, 2, 3, 4]`.
+ fn expected_state_at_target(&self, target: SeekTarget) -> BitSet<usize> {
+ let mut ret = BitSet::new_empty(self.bits_per_block(self.body));
+ ret.insert(Self::BASIC_BLOCK_OFFSET + target.block().index());
+
+ if let Some(target_effect) = self.effect_at_target(target) {
+ for i in 0..=target_effect {
+ ret.insert(i);
+ }
+ }
+
+ ret
+ }
+}
+
+impl BottomValue for MockAnalysis<'tcx> {
+ const BOTTOM_VALUE: bool = false;
+}
+
+impl AnalysisDomain<'tcx> for MockAnalysis<'tcx> {
+ type Idx = usize;
+
+ const NAME: &'static str = "mock";
+
+ fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
+ Self::BASIC_BLOCK_OFFSET + body.basic_blocks().len()
+ }
+
+ fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
+ unimplemented!("This is never called since `MockAnalysis` is never iterated to fixpoint");
+ }
+}
+
+impl Analysis<'tcx> for MockAnalysis<'tcx> {
+ fn apply_statement_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ _statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
+ assert!(state.insert(idx));
+ }
+
+ fn apply_before_statement_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ _statement: &mir::Statement<'tcx>,
+ location: Location,
+ ) {
+ let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
+ assert!(state.insert(idx));
+ }
+
+ fn apply_terminator_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ _terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ ) {
+ let idx = self.effect_at_target(SeekTarget::After(location)).unwrap();
+ assert!(state.insert(idx));
+ }
+
+ fn apply_before_terminator_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ _terminator: &mir::Terminator<'tcx>,
+ location: Location,
+ ) {
+ let idx = self.effect_at_target(SeekTarget::Before(location)).unwrap();
+ assert!(state.insert(idx));
+ }
+
+ fn apply_call_return_effect(
+ &self,
+ state: &mut BitSet<Self::Idx>,
+ block: BasicBlock,
+ _func: &mir::Operand<'tcx>,
+ _args: &[mir::Operand<'tcx>],
+ _return_place: &mir::Place<'tcx>,
+ ) {
+ let location = self.body.terminator_loc(block);
+ let idx = self.effect_at_target(SeekTarget::AfterAssumeCallReturns(location)).unwrap();
+ assert!(state.insert(idx));
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum SeekTarget {
+ BlockStart(BasicBlock),
+ Before(Location),
+ After(Location),
+ AfterAssumeCallReturns(Location),
+}
+
+impl SeekTarget {
+ fn block(&self) -> BasicBlock {
+ use SeekTarget::*;
+
+ match *self {
+ BlockStart(block) => block,
+ Before(loc) | After(loc) | AfterAssumeCallReturns(loc) => loc.block,
+ }
+ }
+
+ /// An iterator over all possible `SeekTarget`s in a given block in order, starting with
+ /// `BlockStart`.
+ ///
+ /// This includes both `After` and `AfterAssumeCallReturns` for every `Location`.
+ fn iter_in_block(body: &mir::Body<'_>, block: BasicBlock) -> impl Iterator<Item = Self> {
+ let statements_and_terminator = (0..=body[block].statements.len())
+ .flat_map(|i| (0..3).map(move |j| (i, j)))
+ .map(move |(i, kind)| {
+ let loc = Location { block, statement_index: i };
+ match kind {
+ 0 => SeekTarget::Before(loc),
+ 1 => SeekTarget::After(loc),
+ 2 => SeekTarget::AfterAssumeCallReturns(loc),
+ _ => unreachable!(),
+ }
+ });
+
+ std::iter::once(SeekTarget::BlockStart(block)).chain(statements_and_terminator)
+ }
+}
+
+#[test]
+fn cursor_seek() {
+ let body = mock_body();
+ let body = &body;
+ let analysis = MockAnalysis { body };
+
+ let mut cursor =
+ Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
+
+ // Sanity check: the mock call return effect is unique and actually being applied.
+ let call_terminator_loc = Location { block: BasicBlock::from_usize(2), statement_index: 2 };
+ assert!(is_call_terminator_non_diverging(body, call_terminator_loc));
+
+ let call_return_effect = cursor
+ .analysis()
+ .effect_at_target(SeekTarget::AfterAssumeCallReturns(call_terminator_loc))
+ .unwrap();
+ assert_ne!(
+ call_return_effect,
+ cursor.analysis().effect_at_target(SeekTarget::After(call_terminator_loc)).unwrap()
+ );
+
+ cursor.seek_after(call_terminator_loc);
+ assert!(!cursor.get().contains(call_return_effect));
+ cursor.seek_after_assume_call_returns(call_terminator_loc);
+ assert!(cursor.get().contains(call_return_effect));
+
+ let every_target = || {
+ body.basic_blocks()
+ .iter_enumerated()
+ .flat_map(|(bb, _)| SeekTarget::iter_in_block(body, bb))
+ };
+
+ let mut seek_to_target = |targ| {
+ use SeekTarget::*;
+
+ match targ {
+ BlockStart(block) => cursor.seek_to_block_start(block),
+ Before(loc) => cursor.seek_before(loc),
+ After(loc) => cursor.seek_after(loc),
+ AfterAssumeCallReturns(loc) => cursor.seek_after_assume_call_returns(loc),
+ }
+
+ assert_eq!(cursor.get(), &cursor.analysis().expected_state_at_target(targ));
+ };
+
+ // Seek *to* every possible `SeekTarget` *from* every possible `SeekTarget`.
+ //
+ // By resetting the cursor to `from` each time it changes, we end up checking some edges twice.
+ // What we really want is an Eulerian cycle for the complete digraph over all possible
+ // `SeekTarget`s, but it's not worth spending the time to compute it.
+ for from in every_target() {
+ seek_to_target(from);
+
+ for to in every_target() {
+ seek_to_target(to);
+ seek_to_target(from);
+ }
+ }
+}
diff --git a/src/librustc_mir/transform/check_consts/resolver.rs b/src/librustc_mir/transform/check_consts/resolver.rs
index c445568..2cd2495 100644
--- a/src/librustc_mir/transform/check_consts/resolver.rs
+++ b/src/librustc_mir/transform/check_consts/resolver.rs
@@ -158,7 +158,7 @@
const BOTTOM_VALUE: bool = false;
}
-impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
+impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
where
Q: Qualif,
{
@@ -173,7 +173,12 @@
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut BitSet<Self::Idx>) {
self.transfer_function(state).initialize_state();
}
+}
+impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
+where
+ Q: Qualif,
+{
fn apply_statement_effect(
&self,
state: &mut BitSet<Self::Idx>,
diff --git a/src/librustc_mir/transform/check_consts/validation.rs b/src/librustc_mir/transform/check_consts/validation.rs
index 1d5fb33..44b2a90 100644
--- a/src/librustc_mir/transform/check_consts/validation.rs
+++ b/src/librustc_mir/transform/check_consts/validation.rs
@@ -32,11 +32,10 @@
}
impl<Q: Qualif> QualifCursor<'a, 'mir, 'tcx, Q> {
- pub fn new(q: Q, item: &'a Item<'mir, 'tcx>, dead_unwinds: &BitSet<BasicBlock>) -> Self {
+ pub fn new(q: Q, item: &'a Item<'mir, 'tcx>) -> Self {
let analysis = FlowSensitiveAnalysis::new(q, item);
- let results =
- dataflow::Engine::new(item.tcx, &item.body, item.def_id, dead_unwinds, analysis)
- .iterate_to_fixpoint();
+ let results = dataflow::Engine::new_generic(item.tcx, &item.body, item.def_id, analysis)
+ .iterate_to_fixpoint();
let cursor = dataflow::ResultsCursor::new(*item.body, results);
let mut in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
@@ -145,12 +144,10 @@
impl Validator<'a, 'mir, 'tcx> {
pub fn new(item: &'a Item<'mir, 'tcx>) -> Self {
+ let needs_drop = QualifCursor::new(NeedsDrop, item);
+ let has_mut_interior = QualifCursor::new(HasMutInterior, item);
+
let dead_unwinds = BitSet::new_empty(item.body.basic_blocks().len());
-
- let needs_drop = QualifCursor::new(NeedsDrop, item, &dead_unwinds);
-
- let has_mut_interior = QualifCursor::new(HasMutInterior, item, &dead_unwinds);
-
let indirectly_mutable = old_dataflow::do_dataflow(
item.tcx,
&*item.body,
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index d927553..b047e53 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -5,7 +5,7 @@
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
-use syntax::attr;
+use syntax::{ast, attr};
type McfResult = Result<(), (Span, Cow<'static, str>)>;
@@ -27,12 +27,19 @@
bug!("closure kind predicate on function: {:#?}", predicate)
}
Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
- Predicate::Trait(pred) => {
+ Predicate::Trait(pred, constness) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue;
}
match pred.skip_binder().self_ty().kind {
ty::Param(ref p) => {
+ // Allow `T: ?const Trait`
+ if *constness == ast::Constness::NotConst
+ && feature_allowed(tcx, def_id, sym::const_trait_bound_opt_out)
+ {
+ continue;
+ }
+
let generics = tcx.generics_of(current);
let def = generics.type_param(p, tcx);
let span = tcx.def_span(def.def_id);
diff --git a/src/librustc_mir_build/Cargo.toml b/src/librustc_mir_build/Cargo.toml
index f0d1d4c..a22c4d1 100644
--- a/src/librustc_mir_build/Cargo.toml
+++ b/src/librustc_mir_build/Cargo.toml
@@ -21,6 +21,7 @@
rustc_hir = { path = "../librustc_hir" }
rustc_macros = { path = "../librustc_macros" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
+rustc_session = { path = "../librustc_session" }
rustc_span = { path = "../librustc_span" }
rustc_target = { path = "../librustc_target" }
syntax = { path = "../libsyntax" }
diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs
index 8fcaa1e..20183fd 100644
--- a/src/librustc_mir_build/hair/pattern/_match.rs
+++ b/src/librustc_mir_build/hair/pattern/_match.rs
@@ -582,15 +582,12 @@
}
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
- crate fn create_and_enter<F, R>(
+ crate fn create_and_enter<R>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
module: DefId,
- f: F,
- ) -> R
- where
- F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
- {
+ f: impl for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R,
+ ) -> R {
let pattern_arena = TypedArena::default();
f(MatchCheckCtxt { tcx, param_env, module, pattern_arena: &pattern_arena })
diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs
index eac52da..49b7c2d 100644
--- a/src/librustc_mir_build/hair/pattern/check_match.rs
+++ b/src/librustc_mir_build/hair/pattern/check_match.rs
@@ -5,9 +5,6 @@
use super::{PatCtxt, PatKind, PatternError};
use rustc::hir::map::Map;
-use rustc::lint;
-use rustc::session::parse::feature_err;
-use rustc::session::Session;
use rustc::ty::{self, Ty, TyCtxt};
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@@ -15,6 +12,10 @@
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{HirId, Pat};
+use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
+use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
+use rustc_session::parse::feature_err;
+use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::{MultiSpan, Span};
use syntax::ast::Mutability;
@@ -67,18 +68,13 @@
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
};
self.check_irrefutable(&loc.pat, msg, sp);
-
- // Check legality of move bindings and `@` patterns.
self.check_patterns(false, &loc.pat);
}
- fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
- intravisit::walk_body(self, body);
-
- for param in body.params {
- self.check_irrefutable(¶m.pat, "function argument", None);
- self.check_patterns(false, ¶m.pat);
- }
+ fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+ intravisit::walk_param(self, param);
+ self.check_irrefutable(¶m.pat, "function argument", None);
+ self.check_patterns(false, ¶m.pat);
}
}
@@ -123,6 +119,25 @@
if !self.tcx.features().bindings_after_at {
check_legality_of_bindings_in_at_patterns(self, pat);
}
+ check_for_bindings_named_same_as_variants(self, pat);
+ }
+
+ fn lower_pattern<'p>(
+ &self,
+ cx: &mut MatchCheckCtxt<'p, 'tcx>,
+ pat: &'tcx hir::Pat<'tcx>,
+ have_errors: &mut bool,
+ ) -> (&'p super::Pat<'tcx>, Ty<'tcx>) {
+ let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
+ patcx.include_lint_checks();
+ let pattern = patcx.lower_pattern(pat);
+ let pattern_ty = pattern.ty;
+ let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+ if !patcx.errors.is_empty() {
+ *have_errors = true;
+ patcx.report_inlining_errors(pat.span);
+ }
+ (pattern, pattern_ty)
}
fn check_match(
@@ -132,11 +147,8 @@
source: hir::MatchSource,
) {
for arm in arms {
- // First, check legality of move bindings.
+ // Check the arm for some things unrelated to exhaustiveness.
self.check_patterns(arm.guard.is_some(), &arm.pat);
-
- // Second, perform some lints.
- check_for_bindings_named_same_as_variants(self, &arm.pat);
}
let module = self.tcx.hir().get_module_parent(scrut.hir_id);
@@ -145,16 +157,8 @@
let inlined_arms: Vec<_> = arms
.iter()
- .map(|arm| {
- let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
- patcx.include_lint_checks();
- let pattern = patcx.lower_pattern(&arm.pat);
- let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
- if !patcx.errors.is_empty() {
- patcx.report_inlining_errors(arm.pat.span);
- have_errors = true;
- }
- (pattern, &*arm.pat, arm.guard.is_some())
+ .map(|hir::Arm { pat, guard, .. }| {
+ (self.lower_pattern(cx, pat, &mut have_errors).0, pat.hir_id, guard.is_some())
})
.collect();
@@ -178,11 +182,7 @@
fn check_irrefutable(&self, pat: &'tcx Pat<'tcx>, origin: &str, sp: Option<Span>) {
let module = self.tcx.hir().get_module_parent(pat.hir_id);
MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
- let mut patcx = PatCtxt::new(self.tcx, self.param_env, self.tables);
- patcx.include_lint_checks();
- let pattern = patcx.lower_pattern(pat);
- let pattern_ty = pattern.ty;
- let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
+ let (pattern, pattern_ty) = self.lower_pattern(cx, pat, &mut false);
let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect();
let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) {
@@ -285,7 +285,7 @@
let ty_path = cx.tcx.def_path_str(edef.did);
cx.tcx
.struct_span_lint_hir(
- lint::builtin::BINDINGS_WITH_VARIANT_NAME,
+ BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
&format!(
@@ -310,79 +310,63 @@
}
/// Checks for common cases of "catchall" patterns that may not be intended as such.
-fn pat_is_catchall(pat: &Pat<'_>) -> bool {
- match pat.kind {
- hir::PatKind::Binding(.., None) => true,
- hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s),
- hir::PatKind::Ref(ref s, _) => pat_is_catchall(s),
- hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)),
+fn pat_is_catchall(pat: &super::Pat<'_>) -> bool {
+ use super::PatKind::*;
+ match &*pat.kind {
+ Binding { subpattern: None, .. } => true,
+ Binding { subpattern: Some(s), .. } | Deref { subpattern: s } => pat_is_catchall(s),
+ Leaf { subpatterns: s } => s.iter().all(|p| pat_is_catchall(&p.pattern)),
_ => false,
}
}
+fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
+ let mut err = tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern");
+ if let Some(catchall) = catchall {
+ // We had a catchall pattern, hint at that.
+ err.span_label(span, "unreachable pattern");
+ err.span_label(catchall, "matches any value");
+ }
+ err.emit();
+}
+
+fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
+ let msg = match source {
+ hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
+ hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
+ _ => bug!(),
+ };
+ tcx.lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, msg);
+}
+
/// Check for unreachable patterns.
fn check_arms<'p, 'tcx>(
cx: &mut MatchCheckCtxt<'p, 'tcx>,
- arms: &[(&'p super::Pat<'tcx>, &hir::Pat<'_>, bool)],
+ arms: &[(&'p super::Pat<'tcx>, HirId, bool)],
source: hir::MatchSource,
) -> Matrix<'p, 'tcx> {
let mut seen = Matrix::empty();
let mut catchall = None;
- for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
+ for (arm_index, (pat, id, has_guard)) in arms.iter().copied().enumerate() {
let v = PatStack::from_pattern(pat);
-
- match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
+ match is_useful(cx, &seen, &v, LeaveOutWitness, id, true) {
NotUseful => {
match source {
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
- // check which arm we're on.
+ // Check which arm we're on.
match arm_index {
// The arm with the user-specified pattern.
- 0 => {
- cx.tcx.lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
- }
+ 0 => unreachable_pattern(cx.tcx, pat.span, id, None),
// The arm with the wildcard pattern.
- 1 => {
- let msg = match source {
- hir::MatchSource::IfLetDesugar { .. } => {
- "irrefutable if-let pattern"
- }
- hir::MatchSource::WhileLetDesugar => {
- "irrefutable while-let pattern"
- }
- _ => bug!(),
- };
- cx.tcx.lint_hir(
- lint::builtin::IRREFUTABLE_LET_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- msg,
- );
- }
+ 1 => irrefutable_let_pattern(cx.tcx, pat.span, id, source),
_ => bug!(),
}
}
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
- let mut err = cx.tcx.struct_span_lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
- // if we had a catchall pattern, hint at that
- if let Some(catchall) = catchall {
- err.span_label(pat.span, "unreachable pattern");
- err.span_label(catchall, "matches any value");
- }
- err.emit();
+ unreachable_pattern(cx.tcx, pat.span, id, catchall);
}
// Unreachable patterns in try and await expressions occur when one of
@@ -392,19 +376,14 @@
}
Useful(unreachable_subpatterns) => {
for pat in unreachable_subpatterns {
- cx.tcx.lint_hir(
- lint::builtin::UNREACHABLE_PATTERNS,
- hir_pat.hir_id,
- pat.span,
- "unreachable pattern",
- );
+ unreachable_pattern(cx.tcx, pat.span, id, None);
}
}
UsefulWithWitness(_) => bug!(),
}
if !has_guard {
seen.push(v);
- if catchall.is_none() && pat_is_catchall(hir_pat) {
+ if catchall.is_none() && pat_is_catchall(pat) {
catchall = Some(pat.span);
}
}
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 5076aaf..31db7fc 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -4,7 +4,7 @@
use crate::maybe_whole;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult, StashKey};
-use rustc_span::source_map::{self, respan, Span, Spanned};
+use rustc_span::source_map::{self, respan, Span};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::BytePos;
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
@@ -565,9 +565,9 @@
let constness = if self.eat_keyword(kw::Const) {
let span = self.prev_span;
self.sess.gated_spans.gate(sym::const_trait_impl, span);
- Some(respan(span, Constness::Const))
+ Constness::Const
} else {
- None
+ Constness::NotConst
};
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
@@ -630,13 +630,13 @@
err_path(ty_first.span)
}
};
- let constness = constness.map(|c| c.node);
- let trait_ref = TraitRef { path, constness, ref_id: ty_first.id };
+ let trait_ref = TraitRef { path, ref_id: ty_first.id };
ItemKind::Impl {
unsafety,
polarity,
defaultness,
+ constness,
generics,
of_trait: Some(trait_ref),
self_ty: ty_second,
@@ -644,18 +644,12 @@
}
}
None => {
- // Reject `impl const Type {}` here
- if let Some(Spanned { node: Constness::Const, span }) = constness {
- self.struct_span_err(span, "`const` cannot modify an inherent impl")
- .help("only a trait impl can be `const`")
- .emit();
- }
-
// impl Type
ItemKind::Impl {
unsafety,
polarity,
defaultness,
+ constness,
generics,
of_trait: None,
self_ty: ty_first,
diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs
index d1875a6..a4cc9fa 100644
--- a/src/librustc_parse/parser/ty.rs
+++ b/src/librustc_parse/parser/ty.rs
@@ -26,10 +26,12 @@
}
impl BoundModifiers {
- fn trait_bound_modifier(&self) -> TraitBoundModifier {
- match self.maybe {
- Some(_) => TraitBoundModifier::Maybe,
- None => TraitBoundModifier::None,
+ fn to_trait_bound_modifier(&self) -> TraitBoundModifier {
+ match (self.maybe, self.maybe_const) {
+ (None, None) => TraitBoundModifier::None,
+ (Some(_), None) => TraitBoundModifier::Maybe,
+ (None, Some(_)) => TraitBoundModifier::MaybeConst,
+ (Some(_), Some(_)) => TraitBoundModifier::MaybeConstMaybe,
}
}
}
@@ -214,7 +216,7 @@
) -> PResult<'a, TyKind> {
assert_ne!(self.token, token::Question);
- let poly_trait_ref = PolyTraitRef::new(generic_params, path, None, lo.to(self.prev_span));
+ let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
if parse_plus {
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
@@ -556,9 +558,9 @@
self.expect(&token::CloseDelim(token::Paren))?;
}
- let constness = modifiers.maybe_const.map(|_| ast::Constness::NotConst);
- let poly_trait = PolyTraitRef::new(lifetime_defs, path, constness, lo.to(self.prev_span));
- Ok(GenericBound::Trait(poly_trait, modifiers.trait_bound_modifier()))
+ let modifier = modifiers.to_trait_bound_modifier();
+ let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
+ Ok(GenericBound::Trait(poly_trait, modifier))
}
/// Optionally parses `for<$generic_params>`.
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 6349054..60bf271 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -91,7 +91,7 @@
let ty::GenericPredicates { parent: _, predicates } = predicates;
for (predicate, _span) in predicates {
match predicate {
- ty::Predicate::Trait(poly_predicate) => {
+ ty::Predicate::Trait(poly_predicate, _) => {
let ty::TraitPredicate { trait_ref } = *poly_predicate.skip_binder();
if self.visit_trait(trait_ref) {
return true;
@@ -1235,7 +1235,7 @@
// The traits' privacy in bodies is already checked as a part of trait object types.
let bounds = rustc_typeck::hir_trait_to_predicates(self.tcx, trait_ref);
- for (trait_predicate, _) in bounds.trait_bounds {
+ for (trait_predicate, _, _) in bounds.trait_bounds {
if self.visit_trait(*trait_predicate.skip_binder()) {
return;
}
diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs
index 779d3f5..a9d2bfa 100644
--- a/src/librustc_save_analysis/sig.rs
+++ b/src/librustc_save_analysis/sig.rs
@@ -486,6 +486,7 @@
unsafety,
polarity,
defaultness,
+ constness,
ref generics,
ref of_trait,
ref self_ty,
@@ -499,6 +500,9 @@
text.push_str("unsafe ");
}
text.push_str("impl");
+ if constness == ast::Constness::Const {
+ text.push_str(" const");
+ }
let generics_sig = generics.make(offset + text.len(), id, scx)?;
text.push_str(&generics_sig.text);
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 889f609..e4f8b5a 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -167,6 +167,7 @@
bindings_after_at,
block,
bool,
+ borrowck_graphviz_format,
borrowck_graphviz_postflow,
borrowck_graphviz_preflow,
box_patterns,
@@ -337,6 +338,7 @@
FxHashSet,
FxHashMap,
gen_future,
+ gen_kill,
generators,
generic_associated_types,
generic_param_attrs,
@@ -735,6 +737,7 @@
try_trait,
tt,
tuple_indexing,
+ two_phase,
Ty,
ty,
type_alias_impl_trait,
diff --git a/src/librustc_traits/lowering/mod.rs b/src/librustc_traits/lowering/mod.rs
index 4b4fa4b..b77c603 100644
--- a/src/librustc_traits/lowering/mod.rs
+++ b/src/librustc_traits/lowering/mod.rs
@@ -94,7 +94,7 @@
use rustc::ty::Predicate;
match self {
- Predicate::Trait(predicate) => predicate.lower(),
+ Predicate::Trait(predicate, _) => predicate.lower(),
Predicate::RegionOutlives(predicate) => predicate.lower(),
Predicate::TypeOutlives(predicate) => predicate.lower(),
Predicate::Projection(predicate) => predicate.lower(),
diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs
index d47e543..8b62403 100644
--- a/src/librustc_ty/ty.rs
+++ b/src/librustc_ty/ty.rs
@@ -2,7 +2,7 @@
use rustc::session::CrateDisambiguator;
use rustc::traits::{self};
use rustc::ty::subst::Subst;
-use rustc::ty::{self, ToPredicate, Ty, TyCtxt};
+use rustc::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
use rustc_data_structures::svh::Svh;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
@@ -58,6 +58,7 @@
def_id: sized_trait,
substs: tcx.mk_substs_trait(ty, &[]),
})
+ .without_const()
.to_predicate();
let predicates = tcx.predicates_of(adtdef.did).predicates;
if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] }
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 047c14c..89eeed8 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -3,6 +3,8 @@
//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an
//! instance of `AstConv`.
+// ignore-tidy-filelength
+
use crate::collect::PlaceholderHirTyCollector;
use crate::lint;
use crate::middle::lang_items::SizedTraitLangItem;
@@ -17,7 +19,7 @@
use rustc::traits::error_reporting::report_object_safety_error;
use rustc::traits::wf::object_region_bounds;
use rustc::ty::subst::{self, InternalSubsts, Subst, SubstsRef};
-use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc::ty::{GenericParamDef, GenericParamDefKind};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId};
@@ -31,7 +33,7 @@
use rustc_span::{MultiSpan, Span, DUMMY_SP};
use rustc_target::spec::abi;
use smallvec::SmallVec;
-use syntax::ast;
+use syntax::ast::{self, Constness};
use syntax::util::lev_distance::find_best_match_for_name;
use std::collections::BTreeSet;
@@ -48,6 +50,8 @@
fn item_def_id(&self) -> Option<DefId>;
+ fn default_constness_for_trait_bounds(&self) -> Constness;
+
/// Returns predicates in scope of the form `X: Foo`, where `X` is
/// a type parameter `X` with the given id `def_id`. This is a
/// subset of the full set of predicates.
@@ -918,6 +922,7 @@
&self,
trait_ref: &hir::TraitRef<'_>,
span: Span,
+ constness: Constness,
self_ty: Ty<'tcx>,
bounds: &mut Bounds<'tcx>,
speculative: bool,
@@ -946,7 +951,7 @@
);
let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs));
- bounds.trait_bounds.push((poly_trait_ref, span));
+ bounds.trait_bounds.push((poly_trait_ref, span, constness));
let mut dup_bindings = FxHashMap::default();
for binding in &assoc_bindings {
@@ -992,12 +997,14 @@
pub fn instantiate_poly_trait_ref(
&self,
poly_trait_ref: &hir::PolyTraitRef<'_>,
+ constness: Constness,
self_ty: Ty<'tcx>,
bounds: &mut Bounds<'tcx>,
) -> Option<Vec<Span>> {
self.instantiate_poly_trait_ref_inner(
&poly_trait_ref.trait_ref,
poly_trait_ref.span,
+ constness,
self_ty,
bounds,
false,
@@ -1180,18 +1187,22 @@
let mut trait_bounds = Vec::new();
let mut region_bounds = Vec::new();
+ let constness = self.default_constness_for_trait_bounds();
for ast_bound in ast_bounds {
match *ast_bound {
hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::None) => {
- trait_bounds.push(b)
+ trait_bounds.push((b, constness))
+ }
+ hir::GenericBound::Trait(ref b, hir::TraitBoundModifier::MaybeConst) => {
+ trait_bounds.push((b, Constness::NotConst))
}
hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => {}
hir::GenericBound::Outlives(ref l) => region_bounds.push(l),
}
}
- for bound in trait_bounds {
- let _ = self.instantiate_poly_trait_ref(bound, param_ty, bounds);
+ for (bound, constness) in trait_bounds {
+ let _ = self.instantiate_poly_trait_ref(bound, constness, param_ty, bounds);
}
bounds.region_bounds.extend(
@@ -1225,7 +1236,7 @@
let mut bounds = Bounds::default();
self.add_bounds(param_ty, ast_bounds, &mut bounds);
- bounds.trait_bounds.sort_by_key(|(t, _)| t.def_id());
+ bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id());
bounds.implicitly_sized = if let SizedByDefault::Yes = sized_by_default {
if !self.is_unsized(ast_bounds, span) { Some(span) } else { None }
@@ -1416,15 +1427,21 @@
let mut potential_assoc_types = Vec::new();
let dummy_self = self.tcx().types.trait_object_dummy_self;
for trait_bound in trait_bounds.iter().rev() {
- let cur_potential_assoc_types =
- self.instantiate_poly_trait_ref(trait_bound, dummy_self, &mut bounds);
+ let cur_potential_assoc_types = self.instantiate_poly_trait_ref(
+ trait_bound,
+ Constness::NotConst,
+ dummy_self,
+ &mut bounds,
+ );
potential_assoc_types.extend(cur_potential_assoc_types.into_iter().flatten());
}
// Expand trait aliases recursively and check that only one regular (non-auto) trait
// is used and no 'maybe' bounds are used.
- let expanded_traits =
- traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().cloned());
+ let expanded_traits = traits::expand_trait_aliases(
+ tcx,
+ bounds.trait_bounds.iter().map(|&(a, b, _)| (a.clone(), b)),
+ );
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) =
expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
if regular_traits.len() > 1 {
@@ -1480,16 +1497,18 @@
let regular_traits_refs_spans = bounds
.trait_bounds
.into_iter()
- .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
+ .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
- for (base_trait_ref, span) in regular_traits_refs_spans {
+ for (base_trait_ref, span, constness) in regular_traits_refs_spans {
+ assert_eq!(constness, ast::Constness::NotConst);
+
for trait_ref in traits::elaborate_trait_ref(tcx, base_trait_ref) {
debug!(
"conv_object_ty_poly_trait_ref: observing object predicate `{:?}`",
trait_ref
);
match trait_ref {
- ty::Predicate::Trait(pred) => {
+ ty::Predicate::Trait(pred, _) => {
associated_types.entry(span).or_default().extend(
tcx.associated_items(pred.def_id())
.filter(|item| item.kind == ty::AssocKind::Type)
@@ -2948,7 +2967,7 @@
/// A list of trait bounds. So if you had `T: Debug` this would be
/// `T: Debug`. Note that the self-type is explicit here.
- pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span)>,
+ pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, Constness)>,
/// A list of projection equality bounds. So if you had `T:
/// Iterator<Item = u32>` this would include `<T as
@@ -2979,7 +2998,7 @@
def_id: sized,
substs: tcx.mk_substs_trait(param_ty, &[]),
});
- (trait_ref.to_predicate(), span)
+ (trait_ref.without_const().to_predicate(), span)
})
});
@@ -2996,11 +3015,10 @@
let outlives = ty::OutlivesPredicate(param_ty, region_bound);
(ty::Binder::bind(outlives).to_predicate(), span)
})
- .chain(
- self.trait_bounds
- .iter()
- .map(|&(bound_trait_ref, span)| (bound_trait_ref.to_predicate(), span)),
- )
+ .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| {
+ let predicate = bound_trait_ref.with_constness(constness).to_predicate();
+ (predicate, span)
+ }))
.chain(
self.projection_bounds
.iter()
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index 3ea5de9..e4dec97 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -5,7 +5,7 @@
use rustc::session::DiagnosticMessageId;
use rustc::traits::{self, TraitEngine};
use rustc::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
-use rustc::ty::{self, TraitRef, Ty, TyCtxt};
+use rustc::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
use rustc::ty::{ToPredicate, TypeFoldable};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
@@ -122,8 +122,11 @@
let cause = traits::ObligationCause::misc(self.span, self.body_id);
- let obligation =
- traits::Obligation::new(cause.clone(), self.param_env, trait_ref.to_predicate());
+ let obligation = traits::Obligation::new(
+ cause.clone(),
+ self.param_env,
+ trait_ref.without_const().to_predicate(),
+ );
if !self.infcx.predicate_may_hold(&obligation) {
debug!("overloaded_deref_ty: cannot match obligation");
return None;
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 8c8e064..54b32c3 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -564,7 +564,7 @@
let obligation = queue.remove(0);
debug!("coerce_unsized resolve step: {:?}", obligation);
let trait_ref = match obligation.predicate {
- ty::Predicate::Trait(ref tr) if traits.contains(&tr.def_id()) => {
+ ty::Predicate::Trait(ref tr, _) if traits.contains(&tr.def_id()) => {
if unsize_did == tr.def_id() {
let sty = &tr.skip_binder().input_types().nth(1).unwrap().kind;
if let ty::Tuple(..) = sty {
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index f4aa53a..32773e2 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -232,7 +232,7 @@
let predicate_matches_closure = |p: &'_ Predicate<'tcx>| {
let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env);
match (predicate, p) {
- (Predicate::Trait(a), Predicate::Trait(b)) => relator.relate(a, b).is_ok(),
+ (Predicate::Trait(a, _), Predicate::Trait(b, _)) => relator.relate(a, b).is_ok(),
(Predicate::Projection(a), Predicate::Projection(b)) => {
relator.relate(a, b).is_ok()
}
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 636ea5b..2012a2a 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -569,7 +569,7 @@
traits::elaborate_predicates(self.tcx, predicates.predicates.clone())
.filter_map(|predicate| match predicate {
- ty::Predicate::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => {
+ ty::Predicate::Trait(trait_pred, _) if trait_pred.def_id() == sized_def_id => {
Some(trait_pred)
}
_ => None,
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 711c285..c1cf352 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -17,7 +17,7 @@
use rustc::ty::subst::Subst;
use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::GenericParamDefKind;
-use rustc::ty::{self, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TypeFoldable};
+use rustc::ty::{self, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TypeFoldable, WithConstness};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@@ -322,7 +322,7 @@
span,
self.body_id,
self.param_env,
- poly_trait_ref.to_predicate(),
+ poly_trait_ref.without_const().to_predicate(),
);
// Now we want to know if this can be matched
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 9d8f571..2adf125 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -25,6 +25,7 @@
use rustc::ty::GenericParamDefKind;
use rustc::ty::{
self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
+ WithConstness,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
@@ -824,7 +825,7 @@
// FIXME: do we want to commit to this behavior for param bounds?
let bounds = self.param_env.caller_bounds.iter().filter_map(|predicate| match *predicate {
- ty::Predicate::Trait(ref trait_predicate) => {
+ ty::Predicate::Trait(ref trait_predicate, _) => {
match trait_predicate.skip_binder().trait_ref.self_ty().kind {
ty::Param(ref p) if *p == param_ty => Some(trait_predicate.to_poly_trait_ref()),
_ => None,
@@ -1394,7 +1395,7 @@
}
TraitCandidate(trait_ref) => {
- let predicate = trait_ref.to_predicate();
+ let predicate = trait_ref.without_const().to_predicate();
let obligation = traits::Obligation::new(cause, self.param_env, predicate);
if !self.predicate_may_hold(&obligation) {
if self.probe(|_| self.select_trait_candidate(trait_ref).is_err()) {
@@ -1428,7 +1429,7 @@
let o = self.resolve_vars_if_possible(&o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
- if let &ty::Predicate::Trait(ref pred) = &o.predicate {
+ if let &ty::Predicate::Trait(ref pred, _) = &o.predicate {
possibly_unsatisfied_predicates.push(pred.skip_binder().trait_ref);
}
}
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 88de265..e9942fa 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -9,7 +9,7 @@
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::traits::Obligation;
use rustc::ty::print::with_crate_prefix;
-use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@@ -57,7 +57,7 @@
span,
self.body_id,
self.param_env,
- poly_trait_ref.to_predicate(),
+ poly_trait_ref.without_const().to_predicate(),
);
self.predicate_may_hold(&obligation)
})
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 7c0500c..2dc198b 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -90,6 +90,7 @@
use crate::astconv::{AstConv, PathSeg};
use crate::middle::lang_items;
use crate::namespace::Namespace;
+use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir::map::Map;
use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc::infer::error_reporting::TypeAnnotationNeeded::E0282;
@@ -112,7 +113,7 @@
use rustc::ty::util::{Discr, IntTypeExt, Representability};
use rustc::ty::{
self, AdtKind, CanonicalUserType, Const, GenericParamDefKind, RegionKind, ToPolyTraitRef,
- ToPredicate, Ty, TyCtxt, UserType,
+ ToPredicate, Ty, TyCtxt, UserType, WithConstness,
};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -1421,7 +1422,7 @@
inherited.register_predicate(traits::Obligation::new(
cause,
param_env,
- trait_ref.to_predicate(),
+ trait_ref.without_const().to_predicate(),
));
}
}
@@ -2610,6 +2611,16 @@
None
}
+ fn default_constness_for_trait_bounds(&self) -> ast::Constness {
+ // FIXME: refactor this into a method
+ let node = self.tcx.hir().get(self.body_id);
+ if let Some(fn_like) = FnLikeNode::from_node(node) {
+ fn_like.constness()
+ } else {
+ ast::Constness::NotConst
+ }
+ }
+
fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
let tcx = self.tcx;
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
@@ -2621,7 +2632,7 @@
parent: None,
predicates: tcx.arena.alloc_from_iter(self.param_env.caller_bounds.iter().filter_map(
|&predicate| match predicate {
- ty::Predicate::Trait(ref data)
+ ty::Predicate::Trait(ref data, _)
if data.skip_binder().self_ty().is_param(index) =>
{
// HACK(eddyb) should get the original `Span`.
@@ -3693,7 +3704,7 @@
ty::Predicate::Projection(ref data) => {
Some((data.to_poly_trait_ref(self.tcx), obligation))
}
- ty::Predicate::Trait(ref data) => Some((data.to_poly_trait_ref(), obligation)),
+ ty::Predicate::Trait(ref data, _) => Some((data.to_poly_trait_ref(), obligation)),
ty::Predicate::Subtype(..) => None,
ty::Predicate::RegionOutlives(..) => None,
ty::Predicate::TypeOutlives(..) => None,
@@ -3996,7 +4007,7 @@
continue;
}
- if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
+ if let ty::Predicate::Trait(predicate, _) = error.obligation.predicate {
// Collect the argument position for all arguments that could have caused this
// `FulfillmentError`.
let mut referenced_in = final_arg_types
@@ -4040,7 +4051,7 @@
if let hir::ExprKind::Path(qpath) = &path.kind {
if let hir::QPath::Resolved(_, path) = &qpath {
for error in errors {
- if let ty::Predicate::Trait(predicate) = error.obligation.predicate {
+ if let ty::Predicate::Trait(predicate, _) = error.obligation.predicate {
// If any of the type arguments in this path segment caused the
// `FullfillmentError`, point at its span (#61860).
for arg in path
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 910e680..8281182 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -6,7 +6,9 @@
use rustc::session::parse::feature_err;
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
use rustc::ty::subst::{InternalSubsts, Subst};
-use rustc::ty::{self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{
+ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
+};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir::def_id::DefId;
@@ -953,7 +955,8 @@
substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]),
};
- let obligation = traits::Obligation::new(cause, fcx.param_env, trait_ref.to_predicate());
+ let obligation =
+ traits::Obligation::new(cause, fcx.param_env, trait_ref.without_const().to_predicate());
if fcx.predicate_must_hold_modulo_regions(&obligation) {
true
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 9eee31c..5821977 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -20,6 +20,7 @@
use crate::lint;
use crate::middle::resolve_lifetime as rl;
use crate::middle::weak_lang_items;
+use rustc::hir::map::blocks::FnLikeNode;
use rustc::hir::map::Map;
use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc::mir::mono::Linkage;
@@ -30,7 +31,7 @@
use rustc::ty::subst::{InternalSubsts, Subst};
use rustc::ty::util::Discr;
use rustc::ty::util::IntTypeExt;
-use rustc::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt};
+use rustc::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt, WithConstness};
use rustc::ty::{ReprOptions, ToPredicate};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashMap;
@@ -286,6 +287,22 @@
Some(self.item_def_id)
}
+ fn default_constness_for_trait_bounds(&self) -> ast::Constness {
+ // FIXME: refactor this into a method
+ let hir_id = self
+ .tcx
+ .hir()
+ .as_local_hir_id(self.item_def_id)
+ .expect("Non-local call to local provider is_const_fn");
+
+ let node = self.tcx.hir().get(hir_id);
+ if let Some(fn_like) = FnLikeNode::from_node(node) {
+ fn_like.constness()
+ } else {
+ ast::Constness::NotConst
+ }
+ }
+
fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx> {
self.tcx.at(span).type_param_predicates((self.item_def_id, def_id))
}
@@ -409,7 +426,8 @@
// Implied `Self: Trait` and supertrait bounds.
if param_id == item_hir_id {
let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id);
- extend = Some((identity_trait_ref.to_predicate(), item.span));
+ extend =
+ Some((identity_trait_ref.without_const().to_predicate(), item.span));
}
generics
}
@@ -430,7 +448,7 @@
icx.type_parameter_bounds_in_generics(ast_generics, param_id, ty, OnlySelfBounds(true))
.into_iter()
.filter(|(predicate, _)| match predicate {
- ty::Predicate::Trait(ref data) => data.skip_binder().self_ty().is_param(index),
+ ty::Predicate::Trait(ref data, _) => data.skip_binder().self_ty().is_param(index),
_ => false,
}),
);
@@ -451,6 +469,7 @@
ty: Ty<'tcx>,
only_self_bounds: OnlySelfBounds,
) -> Vec<(ty::Predicate<'tcx>, Span)> {
+ let constness = self.default_constness_for_trait_bounds();
let from_ty_params = ast_generics
.params
.iter()
@@ -459,7 +478,7 @@
_ => None,
})
.flat_map(|bounds| bounds.iter())
- .flat_map(|b| predicates_from_bound(self, ty, b));
+ .flat_map(|b| predicates_from_bound(self, ty, b, constness));
let from_where_clauses = ast_generics
.where_clause
@@ -479,7 +498,7 @@
};
bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b)))
})
- .flat_map(|(bt, b)| predicates_from_bound(self, bt, b));
+ .flat_map(|(bt, b)| predicates_from_bound(self, bt, b, constness));
from_ty_params.chain(from_where_clauses).collect()
}
@@ -855,7 +874,7 @@
// which will, in turn, reach indirect supertraits.
for &(pred, span) in superbounds {
debug!("superbound: {:?}", pred);
- if let ty::Predicate::Trait(bound) = pred {
+ if let ty::Predicate::Trait(bound, _) = pred {
tcx.at(span).super_predicates_of(bound.def_id());
}
}
@@ -2054,7 +2073,7 @@
let span = tcx.def_span(def_id);
result.predicates =
tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
- ty::TraitRef::identity(tcx, def_id).to_predicate(),
+ ty::TraitRef::identity(tcx, def_id).without_const().to_predicate(),
span,
))));
}
@@ -2104,6 +2123,7 @@
let mut is_default_impl_trait = None;
let icx = ItemCtxt::new(tcx, def_id);
+ let constness = icx.default_constness_for_trait_bounds();
const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty();
@@ -2228,7 +2248,10 @@
// (see below). Recall that a default impl is not itself an impl, but rather a
// set of defaults that can be incorporated into another impl.
if let Some(trait_ref) = is_default_impl_trait {
- predicates.push((trait_ref.to_poly_trait_ref().to_predicate(), tcx.def_span(def_id)));
+ predicates.push((
+ trait_ref.to_poly_trait_ref().without_const().to_predicate(),
+ tcx.def_span(def_id),
+ ));
}
// Collect the region predicates that were declared inline as
@@ -2302,11 +2325,18 @@
for bound in bound_pred.bounds.iter() {
match bound {
- &hir::GenericBound::Trait(ref poly_trait_ref, _) => {
+ &hir::GenericBound::Trait(ref poly_trait_ref, modifier) => {
+ let constness = match modifier {
+ hir::TraitBoundModifier::MaybeConst => ast::Constness::NotConst,
+ hir::TraitBoundModifier::None => constness,
+ hir::TraitBoundModifier::Maybe => bug!("this wasn't handled"),
+ };
+
let mut bounds = Bounds::default();
let _ = AstConv::instantiate_poly_trait_ref(
&icx,
poly_trait_ref,
+ constness,
ty,
&mut bounds,
);
@@ -2482,11 +2512,18 @@
astconv: &dyn AstConv<'tcx>,
param_ty: Ty<'tcx>,
bound: &'tcx hir::GenericBound<'tcx>,
+ constness: ast::Constness,
) -> Vec<(ty::Predicate<'tcx>, Span)> {
match *bound {
- hir::GenericBound::Trait(ref tr, hir::TraitBoundModifier::None) => {
+ hir::GenericBound::Trait(ref tr, modifier) => {
+ let constness = match modifier {
+ hir::TraitBoundModifier::Maybe => return vec![],
+ hir::TraitBoundModifier::MaybeConst => ast::Constness::NotConst,
+ hir::TraitBoundModifier::None => constness,
+ };
+
let mut bounds = Bounds::default();
- let _ = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut bounds);
+ let _ = astconv.instantiate_poly_trait_ref(tr, constness, param_ty, &mut bounds);
bounds.predicates(astconv.tcx(), param_ty)
}
hir::GenericBound::Outlives(ref lifetime) => {
@@ -2494,7 +2531,6 @@
let pred = ty::Binder::bind(ty::OutlivesPredicate(param_ty, region));
vec![(ty::Predicate::TypeOutlives(pred), lifetime.span)]
}
- hir::GenericBound::Trait(_, hir::TraitBoundModifier::Maybe) => vec![],
}
}
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 7e63a02..3d27f91 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -380,6 +380,7 @@
&item_cx,
hir_trait,
DUMMY_SP,
+ syntax::ast::Constness::NotConst,
tcx.types.err,
&mut bounds,
true,
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index f37f692..27f8059 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -462,7 +462,7 @@
.filter(|p| {
!orig_bounds.contains(p)
|| match p {
- &&ty::Predicate::Trait(pred) => pred.def_id() == sized_trait,
+ ty::Predicate::Trait(pred, _) => pred.def_id() == sized_trait,
_ => false,
}
})
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 525b1b2..18ebd25 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -1,7 +1,7 @@
use rustc::infer::InferOk;
use rustc::traits;
use rustc::ty::subst::Subst;
-use rustc::ty::ToPredicate;
+use rustc::ty::{ToPredicate, WithConstness};
use rustc_hir as hir;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_span::DUMMY_SP;
@@ -64,7 +64,7 @@
match infcx.evaluate_obligation(&traits::Obligation::new(
cause,
param_env,
- trait_ref.to_predicate(),
+ trait_ref.without_const().to_predicate(),
)) {
Ok(eval_result) => eval_result.may_apply(),
Err(traits::OverflowError) => true, // overflow doesn't mean yes *or* no
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 20a5a6c..7a7d69c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -482,7 +482,7 @@
use rustc::ty::Predicate;
match *self {
- Predicate::Trait(ref pred) => Some(pred.clean(cx)),
+ Predicate::Trait(ref pred, _) => Some(pred.clean(cx)),
Predicate::Subtype(ref pred) => Some(pred.clean(cx)),
Predicate::RegionOutlives(ref pred) => pred.clean(cx),
Predicate::TypeOutlives(ref pred) => pred.clean(cx),
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index c7b12d3..2b59c60 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -141,7 +141,7 @@
.predicates
.iter()
.filter_map(|(pred, _)| {
- if let ty::Predicate::Trait(ref pred) = *pred {
+ if let ty::Predicate::Trait(ref pred, _) = *pred {
if pred.skip_binder().trait_ref.self_ty() == self_ty {
Some(pred.def_id())
} else {
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 178ba69..218674b 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -203,6 +203,7 @@
pub unsafety: hir::Unsafety,
pub polarity: hir::ImplPolarity,
pub defaultness: hir::Defaultness,
+ pub constness: ast::Constness,
pub generics: &'hir hir::Generics<'hir>,
pub trait_: &'hir Option<hir::TraitRef<'hir>>,
pub for_: &'hir hir::Ty<'hir>,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6434dcc..79923fc 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -361,6 +361,7 @@
let modifier_str = match modifier {
hir::TraitBoundModifier::None => "",
hir::TraitBoundModifier::Maybe => "?",
+ hir::TraitBoundModifier::MaybeConst => "?const",
};
if f.alternate() {
write!(f, "{}{:#}", modifier_str, ty.print())
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index fdff187..d3d45cc 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -562,6 +562,7 @@
unsafety,
polarity,
defaultness,
+ constness,
ref generics,
ref of_trait,
self_ty,
@@ -576,6 +577,7 @@
unsafety,
polarity,
defaultness,
+ constness,
generics,
trait_: of_trait,
for_: self_ty,
diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs
index c7d4828..135e830 100644
--- a/src/libstd/sys_common/net.rs
+++ b/src/libstd/sys_common/net.rs
@@ -13,75 +13,43 @@
use libc::{c_int, c_void};
-#[cfg(not(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "ios",
- target_os = "macos",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "solaris",
- target_os = "haiku",
- target_os = "l4re"
-)))]
-use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP;
-#[cfg(not(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "ios",
- target_os = "macos",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "solaris",
- target_os = "haiku",
- target_os = "l4re"
-)))]
-use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP;
-#[cfg(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "ios",
- target_os = "macos",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "solaris",
- target_os = "haiku",
- target_os = "l4re"
-))]
-use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
-#[cfg(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "ios",
- target_os = "macos",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "solaris",
- target_os = "haiku",
- target_os = "l4re"
-))]
-use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "solaris", target_os = "haiku", target_os = "l4re"))] {
+ use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
+ use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
+ } else {
+ use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP;
+ use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP;
+ }
+}
-#[cfg(any(
- target_os = "linux",
- target_os = "android",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "haiku"
-))]
-use libc::MSG_NOSIGNAL;
-#[cfg(not(any(
- target_os = "linux",
- target_os = "android",
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "openbsd",
- target_os = "netbsd",
- target_os = "haiku"
-)))]
-const MSG_NOSIGNAL: c_int = 0x0;
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku"))] {
+ use libc::MSG_NOSIGNAL;
+ } else {
+ const MSG_NOSIGNAL: c_int = 0x0;
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "solaris"))] {
+ use libc::c_uchar;
+ type IpV4MultiCastType = c_uchar;
+ } else {
+ type IpV4MultiCastType = c_int;
+ }
+}
////////////////////////////////////////////////////////////////////////////////
// sockaddr and misc bindings
@@ -566,20 +534,30 @@
}
pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> {
- setsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP, multicast_loop_v4 as c_int)
+ setsockopt(
+ &self.inner,
+ c::IPPROTO_IP,
+ c::IP_MULTICAST_LOOP,
+ multicast_loop_v4 as IpV4MultiCastType,
+ )
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
- let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?;
+ let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?;
Ok(raw != 0)
}
pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> {
- setsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl_v4 as c_int)
+ setsockopt(
+ &self.inner,
+ c::IPPROTO_IP,
+ c::IP_MULTICAST_TTL,
+ multicast_ttl_v4 as IpV4MultiCastType,
+ )
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
- let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?;
+ let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?;
Ok(raw as u32)
}
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index a5a4eb1..5f38ac4 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -266,12 +266,24 @@
/// small, positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;
-/// A modifier on a bound, currently this is only used for `?Sized`, where the
-/// modifier is `Maybe`. Negative bounds should also be handled here.
+/// A modifier on a bound, e.g., `?Sized` or `?const Trait`.
+///
+/// Negative bounds should also be handled here.
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
pub enum TraitBoundModifier {
+ /// No modifiers
None,
+
+ /// `?Trait`
Maybe,
+
+ /// `?const Trait`
+ MaybeConst,
+
+ /// `?const ?Trait`
+ //
+ // This parses but will be rejected during AST validation.
+ MaybeConstMaybe,
}
/// The AST represents all type param bounds as types.
@@ -1033,7 +1045,7 @@
pub fn to_bound(&self) -> Option<GenericBound> {
match &self.kind {
ExprKind::Path(None, path) => Some(GenericBound::Trait(
- PolyTraitRef::new(Vec::new(), path.clone(), None, self.span),
+ PolyTraitRef::new(Vec::new(), path.clone(), self.span),
TraitBoundModifier::None,
)),
_ => None,
@@ -2158,7 +2170,8 @@
}
}
-#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
+#[derive(HashStable_Generic)]
pub enum Constness {
Const,
NotConst,
@@ -2376,15 +2389,6 @@
pub struct TraitRef {
pub path: Path,
pub ref_id: NodeId,
-
- /// The `const` modifier, if any, that appears before this trait.
- ///
- /// | | `constness` |
- /// |----------------|-----------------------------|
- /// | `Trait` | `None` |
- /// | `const Trait` | `Some(Constness::Const)` |
- /// | `?const Trait` | `Some(Constness::NotConst)` |
- pub constness: Option<Constness>,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@@ -2399,15 +2403,10 @@
}
impl PolyTraitRef {
- pub fn new(
- generic_params: Vec<GenericParam>,
- path: Path,
- constness: Option<Constness>,
- span: Span,
- ) -> Self {
+ pub fn new(generic_params: Vec<GenericParam>, path: Path, span: Span) -> Self {
PolyTraitRef {
bound_generic_params: generic_params,
- trait_ref: TraitRef { path, constness, ref_id: DUMMY_NODE_ID },
+ trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
span,
}
}
@@ -2618,6 +2617,7 @@
unsafety: Unsafety,
polarity: ImplPolarity,
defaultness: Defaultness,
+ constness: Constness,
generics: Generics,
/// The trait being implemented, if any.
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 750d054..4a460c5 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -838,8 +838,7 @@
}
}
-pub fn noop_visit_trait_ref<T: MutVisitor>(tr: &mut TraitRef, vis: &mut T) {
- let TraitRef { path, ref_id, constness: _ } = tr;
+pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
vis.visit_path(path);
vis.visit_id(ref_id);
}
@@ -922,6 +921,7 @@
unsafety: _,
polarity: _,
defaultness: _,
+ constness: _,
generics,
of_trait,
self_ty,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index bc67980..3927e4f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1230,6 +1230,7 @@
unsafety,
polarity,
defaultness,
+ constness,
ref generics,
ref of_trait,
ref self_ty,
@@ -1240,6 +1241,7 @@
self.print_defaultness(defaultness);
self.print_unsafety(unsafety);
self.word_nbsp("impl");
+ self.print_constness(constness);
if !generics.params.is_empty() {
self.print_generic_params(&generics.params);
@@ -2773,6 +2775,13 @@
}
}
+ crate fn print_constness(&mut self, s: ast::Constness) {
+ match s {
+ ast::Constness::Const => self.word_nbsp("const"),
+ ast::Constness::NotConst => {}
+ }
+ }
+
crate fn print_is_auto(&mut self, s: ast::IsAuto) {
match s {
ast::IsAuto::Yes => self.word_nbsp("auto"),
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index d03a9df..946a0d2 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -312,6 +312,7 @@
unsafety: _,
polarity: _,
defaultness: _,
+ constness: _,
ref generics,
ref of_trait,
ref self_ty,
diff --git a/src/test/ui/lint/lint-uppercase-variables.rs b/src/test/ui/lint/lint-uppercase-variables.rs
index 86a3950..a98b4f2 100644
--- a/src/test/ui/lint/lint-uppercase-variables.rs
+++ b/src/test/ui/lint/lint-uppercase-variables.rs
@@ -25,6 +25,16 @@
//~^^^ WARN unused variable: `Foo`
}
+ let Foo = foo::Foo::Foo;
+ //~^ ERROR variable `Foo` should have a snake case name
+ //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^^ WARN unused variable: `Foo`
+
+ fn in_param(Foo: foo::Foo) {}
+ //~^ ERROR variable `Foo` should have a snake case name
+ //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^^ WARN unused variable: `Foo`
+
test(1);
let _ = Something { X: 0 };
diff --git a/src/test/ui/lint/lint-uppercase-variables.stderr b/src/test/ui/lint/lint-uppercase-variables.stderr
index b937832..a38f3e7 100644
--- a/src/test/ui/lint/lint-uppercase-variables.stderr
+++ b/src/test/ui/lint/lint-uppercase-variables.stderr
@@ -6,6 +6,18 @@
|
= note: `#[warn(bindings_with_variant_name)]` on by default
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+ --> $DIR/lint-uppercase-variables.rs:28:9
+ |
+LL | let Foo = foo::Foo::Foo;
+ | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
+
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+ --> $DIR/lint-uppercase-variables.rs:33:17
+ |
+LL | fn in_param(Foo: foo::Foo) {}
+ | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
+
warning: unused variable: `Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
|
@@ -19,6 +31,18 @@
| ^^^^^^
= note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+warning: unused variable: `Foo`
+ --> $DIR/lint-uppercase-variables.rs:28:9
+ |
+LL | let Foo = foo::Foo::Foo;
+ | ^^^ help: consider prefixing with an underscore: `_Foo`
+
+warning: unused variable: `Foo`
+ --> $DIR/lint-uppercase-variables.rs:33:17
+ |
+LL | fn in_param(Foo: foo::Foo) {}
+ | ^^^ help: consider prefixing with an underscore: `_Foo`
+
error: structure field `X` should have a snake case name
--> $DIR/lint-uppercase-variables.rs:10:5
|
@@ -49,6 +73,18 @@
LL | Foo => {}
| ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
-error: aborting due to 4 previous errors
+error: variable `Foo` should have a snake case name
+ --> $DIR/lint-uppercase-variables.rs:28:9
+ |
+LL | let Foo = foo::Foo::Foo;
+ | ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
+
+error: variable `Foo` should have a snake case name
+ --> $DIR/lint-uppercase-variables.rs:33:17
+ |
+LL | fn in_param(Foo: foo::Foo) {}
+ | ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
+
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0170`.
diff --git a/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs
new file mode 100644
index 0000000..95ead6b
--- /dev/null
+++ b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.rs
@@ -0,0 +1,26 @@
+pub enum EFoo {
+ A,
+}
+
+pub trait Foo {
+ const X: EFoo;
+}
+
+struct Abc;
+
+impl Foo for Abc {
+ const X: EFoo = EFoo::A;
+}
+
+struct Def;
+impl Foo for Def {
+ const X: EFoo = EFoo::A;
+}
+
+pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+ //~^ ERROR associated consts cannot be referenced in patterns
+ let A::X = arg;
+ //~^ ERROR associated consts cannot be referenced in patterns
+}
+
+fn main() {}
diff --git a/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
new file mode 100644
index 0000000..54ecc24
--- /dev/null
+++ b/src/test/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
@@ -0,0 +1,15 @@
+error[E0158]: associated consts cannot be referenced in patterns
+ --> $DIR/issue-68393-let-pat-assoc-constant.rs:20:40
+ |
+LL | pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+ | ^^^^
+
+error[E0158]: associated consts cannot be referenced in patterns
+ --> $DIR/issue-68393-let-pat-assoc-constant.rs:22:9
+ |
+LL | let A::X = arg;
+ | ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0158`.
diff --git a/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs
new file mode 100644
index 0000000..f10a7f2
--- /dev/null
+++ b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.rs
@@ -0,0 +1,5 @@
+fn main() {
+ let x = 255u8;
+ let 0u8..=x = 0;
+ //~^ ERROR runtime values cannot be referenced in patterns
+}
diff --git a/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr
new file mode 100644
index 0000000..c1508bd
--- /dev/null
+++ b/src/test/ui/pattern/issue-68394-let-pat-runtime-value.stderr
@@ -0,0 +1,9 @@
+error[E0080]: runtime values cannot be referenced in patterns
+ --> $DIR/issue-68394-let-pat-runtime-value.rs:3:15
+ |
+LL | let 0u8..=x = 0;
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/pattern/issue-68396-let-float-bug.rs b/src/test/ui/pattern/issue-68396-let-float-bug.rs
new file mode 100644
index 0000000..afc599a
--- /dev/null
+++ b/src/test/ui/pattern/issue-68396-let-float-bug.rs
@@ -0,0 +1,7 @@
+fn main() {
+ let 1234567890123456789012345678901234567890e-340: f64 = 0.0;
+ //~^ ERROR could not evaluate float literal (see issue #31407)
+
+ fn param(1234567890123456789012345678901234567890e-340: f64) {}
+ //~^ ERROR could not evaluate float literal (see issue #31407)
+}
diff --git a/src/test/ui/pattern/issue-68396-let-float-bug.stderr b/src/test/ui/pattern/issue-68396-let-float-bug.stderr
new file mode 100644
index 0000000..618aa4b
--- /dev/null
+++ b/src/test/ui/pattern/issue-68396-let-float-bug.stderr
@@ -0,0 +1,15 @@
+error[E0080]: could not evaluate float literal (see issue #31407)
+ --> $DIR/issue-68396-let-float-bug.rs:2:9
+ |
+LL | let 1234567890123456789012345678901234567890e-340: f64 = 0.0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0080]: could not evaluate float literal (see issue #31407)
+ --> $DIR/issue-68396-let-float-bug.rs:5:14
+ |
+LL | fn param(1234567890123456789012345678901234567890e-340: f64) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
index 5b0c930..0115fc0 100644
--- a/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
+++ b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr
@@ -1,8 +1,10 @@
error: unreachable pattern
--> $DIR/struct-pattern-match-useless.rs:12:9
|
+LL | Foo { x: _x, y: _y } => (),
+ | -------------------- matches any value
LL | Foo { .. } => ()
- | ^^^^^^^^^^
+ | ^^^^^^^^^^ unreachable pattern
|
note: lint level defined here
--> $DIR/struct-pattern-match-useless.rs:1:9
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr
index 0bf337a..e4f4d42 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr
@@ -1,8 +1,8 @@
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/feature-gate.rs:11:29
+error: fatal error triggered by #[rustc_error]
+ --> $DIR/feature-gate.rs:16:1
|
-LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
- | ^^^^^^^^
+LL | fn main() {}
+ | ^^^^^^^^^^^^
error: aborting due to previous error
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs
index cf1ed30..d600b53 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs
@@ -3,6 +3,7 @@
#![cfg_attr(gated, feature(const_trait_bound_opt_out))]
#![allow(incomplete_features)]
+#![feature(rustc_attrs)]
trait T {
const CONST: i32;
@@ -10,6 +11,6 @@
const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
//[stock]~^ ERROR `?const` on trait bounds is experimental
-//[stock,gated]~^^ ERROR `?const` on trait bounds is not yet implemented
-fn main() {}
+#[rustc_error]
+fn main() {} //[gated]~ ERROR fatal error triggered by #[rustc_error]
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr
index 6438800..fbd3840 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr
@@ -1,5 +1,5 @@
error[E0658]: `?const` on trait bounds is experimental
- --> $DIR/feature-gate.rs:11:29
+ --> $DIR/feature-gate.rs:12:29
|
LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
| ^^^^^^
@@ -7,12 +7,6 @@
= note: for more information, see https://github.com/rust-lang/rust/issues/67794
= help: add `#![feature(const_trait_bound_opt_out)]` to the crate attributes to enable
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/feature-gate.rs:11:29
- |
-LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
- | ^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs
index e4e6bed..f5561a9 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs
@@ -8,18 +8,14 @@
fn rpit() -> impl ?const T { S }
//~^ ERROR `?const` is not permitted in `impl Trait`
-//~| ERROR `?const` on trait bounds is not yet implemented
fn apit(_: impl ?const T) {}
//~^ ERROR `?const` is not permitted in `impl Trait`
-//~| ERROR `?const` on trait bounds is not yet implemented
fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
//~^ ERROR `?const` is not permitted in `impl Trait`
-//~| ERROR `?const` on trait bounds is not yet implemented
fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
//~^ ERROR `?const` is not permitted in `impl Trait`
-//~| ERROR `?const` on trait bounds is not yet implemented
fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr
index f4abd4b..06cd00a 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr
@@ -5,46 +5,22 @@
| ^^^^^^^^
error: `?const` is not permitted in `impl Trait`
- --> $DIR/in-impl-trait.rs:13:17
+ --> $DIR/in-impl-trait.rs:12:17
|
LL | fn apit(_: impl ?const T) {}
| ^^^^^^^^
error: `?const` is not permitted in `impl Trait`
- --> $DIR/in-impl-trait.rs:17:50
+ --> $DIR/in-impl-trait.rs:15:50
|
LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
| ^^^^^^^^
error: `?const` is not permitted in `impl Trait`
- --> $DIR/in-impl-trait.rs:21:48
+ --> $DIR/in-impl-trait.rs:18:48
|
LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
| ^^^^^^^^
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-impl-trait.rs:9:19
- |
-LL | fn rpit() -> impl ?const T { S }
- | ^^^^^^^^
-
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-impl-trait.rs:13:17
- |
-LL | fn apit(_: impl ?const T) {}
- | ^^^^^^^^
-
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-impl-trait.rs:17:50
- |
-LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
- | ^^^^^^^^
-
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-impl-trait.rs:21:48
- |
-LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
- | ^^^^^^^^
-
-error: aborting due to 8 previous errors
+error: aborting due to 4 previous errors
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs
index 4523b46..fc9ed5b 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs
@@ -4,6 +4,5 @@
trait Super {}
trait T: ?const Super {}
//~^ ERROR `?const` is not permitted in supertraits
-//~| ERROR `?const` on trait bounds is not yet implemented
fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr
index 8003361..a0d8f95 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr
@@ -4,11 +4,5 @@
LL | trait T: ?const Super {}
| ^^^^^^^^^^^^
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-trait-bounds.rs:5:10
- |
-LL | trait T: ?const Super {}
- | ^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs
index 6cfca71..b3d1f48 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs
@@ -9,14 +9,11 @@
// An inherent impl for the trait object `?const T`.
impl ?const T {}
//~^ ERROR `?const` is not permitted in trait objects
-//~| ERROR `?const` on trait bounds is not yet implemented
fn trait_object() -> &'static dyn ?const T { &S }
//~^ ERROR `?const` is not permitted in trait objects
-//~| ERROR `?const` on trait bounds is not yet implemented
fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
//~^ ERROR `?const` is not permitted in trait objects
-//~| ERROR `?const` on trait bounds is not yet implemented
fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr
index c059f16..331fe04 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr
@@ -5,34 +5,16 @@
| ^^^^^^^^
error: `?const` is not permitted in trait objects
- --> $DIR/in-trait-object.rs:14:35
+ --> $DIR/in-trait-object.rs:13:35
|
LL | fn trait_object() -> &'static dyn ?const T { &S }
| ^^^^^^^^
error: `?const` is not permitted in trait objects
- --> $DIR/in-trait-object.rs:18:61
+ --> $DIR/in-trait-object.rs:16:61
|
LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
| ^^^^^^^^
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-trait-object.rs:10:6
- |
-LL | impl ?const T {}
- | ^^^^^^^^
-
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-trait-object.rs:14:35
- |
-LL | fn trait_object() -> &'static dyn ?const T { &S }
- | ^^^^^^^^
-
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/in-trait-object.rs:18:61
- |
-LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
- | ^^^^^^^^
-
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs
index 425784f..c2c8689 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs
@@ -3,6 +3,5 @@
struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
//~^ ERROR `?const` and `?` are mutually exclusive
-//~| ERROR `?const` on trait bounds is not yet implemented
fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr
index 44f6d46..e8e9d6c 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr
@@ -4,11 +4,5 @@
LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^
-error: `?const` on trait bounds is not yet implemented
- --> $DIR/with-maybe-sized.rs:4:13
- |
-LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
- | ^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
index 9cffe75..7f064c0 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
+++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
@@ -1,5 +1,3 @@
-// compile-flags: -Z parse-only
-
#![feature(const_trait_impl)]
#![feature(const_trait_bound_opt_out)]
#![allow(incomplete_features)]
@@ -8,7 +6,12 @@
struct S;
trait T {}
+impl const S {}
+//~^ ERROR inherent impls cannot be `const`
+//~| ERROR const trait impls are not yet implemented
+
impl const T {}
-//~^ ERROR `const` cannot modify an inherent impl
+//~^ ERROR inherent impls cannot be `const`
+//~| ERROR const trait impls are not yet implemented
fn main() {}
diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
index 1d24557..508c6f4 100644
--- a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
+++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
@@ -1,10 +1,30 @@
-error: `const` cannot modify an inherent impl
- --> $DIR/inherent-impl.rs:11:6
+error: inherent impls cannot be `const`
+ --> $DIR/inherent-impl.rs:9:1
+ |
+LL | impl const S {}
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: only trait implementations may be annotated with `const`
+
+error: inherent impls cannot be `const`
+ --> $DIR/inherent-impl.rs:13:1
|
LL | impl const T {}
- | ^^^^^
+ | ^^^^^^^^^^^^^^^
|
- = help: only a trait impl can be `const`
+ = note: only trait implementations may be annotated with `const`
-error: aborting due to previous error
+error: const trait impls are not yet implemented
+ --> $DIR/inherent-impl.rs:9:1
+ |
+LL | impl const S {}
+ | ^^^^^^^^^^^^^^^
+
+error: const trait impls are not yet implemented
+ --> $DIR/inherent-impl.rs:13:1
+ |
+LL | impl const T {}
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to 4 previous errors
diff --git a/src/tools/cargo b/src/tools/cargo
index ad3dbe1..f6449ba 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit ad3dbe10e1e654fb1f032a5dd9481d7cbaa00d65
+Subproject commit f6449ba236db31995255ac5e4cad4ab88296a7c6
diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs
index 4664e2e..408be83 100644
--- a/src/tools/tidy/src/debug_artifacts.rs
+++ b/src/tools/tidy/src/debug_artifacts.rs
@@ -2,7 +2,7 @@
use std::path::{Path, PathBuf};
-const GRAPHVIZ_POSTFLOW_MSG: &'static str = "`borrowck_graphviz_postflow` attribute in test";
+const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test";
pub fn check(path: &Path, bad: &mut bool) {
let test_dir: PathBuf = path.join("test");
diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs
index ebaa81d..428c57d 100644
--- a/src/tools/tidy/src/error_codes_check.rs
+++ b/src/tools/tidy/src/error_codes_check.rs
@@ -53,7 +53,7 @@
error_codes.insert(err_code.clone(), false);
}
// Now we extract the tests from the markdown file!
- let md = some_or_continue!(s.splitn(2, "include_str!(\"").skip(1).next());
+ let md = some_or_continue!(s.splitn(2, "include_str!(\"").nth(1));
let md_file_name = some_or_continue!(md.splitn(2, "\")").next());
let path = some_or_continue!(path.parent()).join(md_file_name);
match read_to_string(&path) {
@@ -84,7 +84,7 @@
let s = line.trim();
if s.starts_with("error[E") || s.starts_with("warning[E") {
if let Some(err_code) = s.splitn(2, ']').next() {
- if let Some(err_code) = err_code.splitn(2, '[').skip(1).next() {
+ if let Some(err_code) = err_code.splitn(2, '[').nth(1) {
let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
*nb = true;
}
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 325b45e..12f93a8 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -232,7 +232,7 @@
}
}
}
- return false;
+ false
}
pub fn collect_lang_features(base_src_path: &Path, bad: &mut bool) -> Features {
@@ -344,7 +344,7 @@
}
None
} else {
- let s = issue_str.split('(').nth(1).unwrap().split(')').nth(0).unwrap();
+ let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();
Some(s.parse().unwrap())
};
Some((name.to_owned(), Feature { level, since, has_gate_test: false, tracking_issue }))
diff --git a/src/tools/tidy/src/features/version.rs b/src/tools/tidy/src/features/version.rs
index c8c39ad..620be2f 100644
--- a/src/tools/tidy/src/features/version.rs
+++ b/src/tools/tidy/src/features/version.rs
@@ -38,7 +38,7 @@
let parts = [part()?, part()?, part()?];
- if let Some(_) = iter.next() {
+ if iter.next().is_some() {
// Ensure we don't have more than 3 parts.
return Err(ParseVersionError::WrongNumberOfParts);
}
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index b15c299..4247fcb3b 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -58,7 +58,7 @@
fn line_is_url(columns: usize, line: &str) -> bool {
// more basic check for error_codes.rs, to avoid complexity in implementing two state machines
if columns == ERROR_CODE_COLS {
- return line.starts_with("[") && line.contains("]:") && line.contains("http");
+ return line.starts_with('[') && line.contains("]:") && line.contains("http");
}
use self::LIUState::*;