outside the submodule.
-It can also be more convenient during development to set `submodules = false`
-in the `config.toml` to prevent `x.py` from resetting to the original branch.
+In order to prepare your PR, you can run the build locally by doing
+`./x.py build src/tools/TOOL`. If you will be editing the sources
+there, you may wish to set `submodules = false` in the `config.toml`
+to prevent `x.py` from resetting to the original branch.
#### Breaking Tools Built With The Compiler
[breaking-tools-built-with-the-compiler]: #breaking-tools-built-with-the-compiler
[] LookupDeprecationEntry(DefId),
[] ItemBodyNestedBodies(DefId),
[] ConstIsRvaluePromotableToStatic(DefId),
+ [] RvaluePromotableMap(DefId),
[] ImplParent(DefId),
[] TraitOfItem(DefId),
[] IsExportedSymbol(DefId),
[] PostorderCnums,
[] HasCloneClosures(CrateNum),
[] HasCopyClosures(CrateNum),
+ [] EraseRegionsTy { ty: Ty<'tcx> },
[] Freevars(DefId),
[] MaybeUnusedTraitImport(DefId),
#![cfg_attr(stage0, feature(const_fn))]
#![cfg_attr(not(stage0), feature(const_atomic_bool_new))]
-#![recursion_limit="256"]
+#![recursion_limit="512"]
extern crate arena;
#[macro_use] extern crate bitflags;
use ty::{self, TyCtxt, adjustment};
use hir::{self, PatKind};
-
+use std::rc::Rc;
use syntax::ast;
use syntax::ptr::P;
use syntax_pos::Span;
+use util::nodemap::ItemLocalMap;
///////////////////////////////////////////////////////////////////////////
// The Delegate trait
}
impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx, 'tcx> {
+ /// Creates the ExprUseVisitor, configuring it with the various options provided:
+ ///
+ /// - `delegate` -- who receives the callbacks
+ /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
+ /// - `region_scope_tree` --- region scope tree for the code being analyzed
+ /// - `tables` --- typeck results for the code being analyzed
+ /// - `rvalue_promotable_map` --- if you care about rvalue promotion, then provide
+ /// the map here (it can be computed with `tcx.rvalue_promotable_map(def_id)`).
+ /// `None` means that rvalues will be given more conservative lifetimes.
+ ///
+ /// See also `with_infer`, which is used *during* typeck.
pub fn new(delegate: &'a mut (Delegate<'tcx>+'a),
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
region_scope_tree: &'a region::ScopeTree,
- tables: &'a ty::TypeckTables<'tcx>)
+ tables: &'a ty::TypeckTables<'tcx>,
+ rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>)
-> Self
{
ExprUseVisitor {
- mc: mc::MemCategorizationContext::new(tcx, region_scope_tree, tables),
+ mc: mc::MemCategorizationContext::new(tcx,
+ region_scope_tree,
+ tables,
+ rvalue_promotable_map),
delegate,
param_env,
}
use std::fmt;
use std::rc::Rc;
+use util::nodemap::ItemLocalMap;
#[derive(Clone, PartialEq)]
pub enum Categorization<'tcx> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
pub region_scope_tree: &'a region::ScopeTree,
pub tables: &'a ty::TypeckTables<'tcx>,
+ rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>,
infcx: Option<&'a InferCtxt<'a, 'gcx, 'tcx>>,
}
impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
region_scope_tree: &'a region::ScopeTree,
- tables: &'a ty::TypeckTables<'tcx>)
+ tables: &'a ty::TypeckTables<'tcx>,
+ rvalue_promotable_map: Option<Rc<ItemLocalMap<bool>>>)
-> MemCategorizationContext<'a, 'tcx, 'tcx> {
- MemCategorizationContext { tcx, region_scope_tree, tables, infcx: None }
+ MemCategorizationContext {
+ tcx,
+ region_scope_tree,
+ tables,
+ rvalue_promotable_map,
+ infcx: None
+ }
}
}
impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
+ /// Creates a `MemCategorizationContext` during type inference.
+ /// This is used during upvar analysis and a few other places.
+ /// Because the typeck tables are not yet complete, the results
+ /// from the analysis must be used with caution:
+ ///
+ /// - rvalue promotions are not known, so the lifetimes of
+ /// temporaries may be overly conservative;
+ /// - similarly, as the results of upvar analysis are not yet
+ /// known, the results around upvar accesses may be incorrect.
pub fn with_infer(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
region_scope_tree: &'a region::ScopeTree,
tables: &'a ty::TypeckTables<'tcx>)
-> MemCategorizationContext<'a, 'gcx, 'tcx> {
+ let tcx = infcx.tcx;
+
+ // Subtle: we can't do rvalue promotion analysis until the
+ // typeck phase is complete, which means that you can't trust
+ // the rvalue lifetimes that result, but that's ok, since we
+ // don't need to know those during type inference.
+ let rvalue_promotable_map = None;
+
MemCategorizationContext {
- tcx: infcx.tcx,
+ tcx,
region_scope_tree,
tables,
+ rvalue_promotable_map,
infcx: Some(infcx),
}
}
span: Span,
expr_ty: Ty<'tcx>)
-> cmt<'tcx> {
- let promotable = self.tcx.rvalue_promotable_to_static.borrow().get(&id).cloned()
- .unwrap_or(false);
+ let hir_id = self.tcx.hir.node_to_hir_id(id);
+ let promotable = self.rvalue_promotable_map.as_ref().map(|m| m[&hir_id.local_id])
+ .unwrap_or(false);
// Always promote `[T; 0]` (even when e.g. borrowed mutably).
let promotable = match expr_ty.sty {
let re = if promotable {
self.tcx.types.re_static
} else {
- self.temporary_scope(self.tcx.hir.node_to_hir_id(id).local_id)
+ self.temporary_scope(hir_id.local_id)
};
let ret = self.cat_rvalue(id, span, re, expr_ty);
debug!("cat_rvalue_node ret {:?}", ret);
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
use ty::TypeVariants::*;
use ty::layout::{Layout, TargetDataLayout};
-use ty::inhabitedness::DefIdForest;
use ty::maps;
use ty::steal::Steal;
use ty::BindingMode;
// Internal cache for metadata decoding. No need to track deps on this.
pub rcache: RefCell<FxHashMap<ty::CReaderCacheKey, Ty<'tcx>>>,
- // FIXME dep tracking -- should be harmless enough
- pub normalized_cache: RefCell<FxHashMap<Ty<'tcx>, Ty<'tcx>>>,
-
- pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, DefIdForest>>,
-
/// Caches the results of trait selection. This cache is used
/// for things that do not have to do with the parameters in scope.
pub selection_cache: traits::SelectionCache<'tcx>,
/// Merge this with `selection_cache`?
pub evaluation_cache: traits::EvaluationCache<'tcx>,
- /// Maps Expr NodeId's to `true` iff `&expr` can have 'static lifetime.
- pub rvalue_promotable_to_static: RefCell<NodeMap<bool>>,
-
/// The definite name of the current crate after taking into account
/// attributes, commandline parameters, etc.
pub crate_name: Symbol,
maps: maps::Maps::new(providers),
mir_passes,
rcache: RefCell::new(FxHashMap()),
- normalized_cache: RefCell::new(FxHashMap()),
- inhabitedness_cache: RefCell::new(FxHashMap()),
selection_cache: traits::SelectionCache::new(),
evaluation_cache: traits::EvaluationCache::new(),
- rvalue_promotable_to_static: RefCell::new(NodeMap()),
crate_name: Symbol::intern(crate_name),
data_layout,
layout_interner: RefCell::new(FxHashSet()),
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use ty::{self, Ty, TyCtxt};
+use ty::fold::{TypeFolder, TypeFoldable};
+
+pub(super) fn provide(providers: &mut ty::maps::Providers) {
+ *providers = ty::maps::Providers {
+ erase_regions_ty,
+ ..*providers
+ };
+}
+
+fn erase_regions_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ // NB: use `super_fold_with` here. If we used `fold_with`, it
+ // could invoke the `erase_regions_ty` query recursively.
+ ty.super_fold_with(&mut RegionEraserVisitor { tcx })
+}
+
+impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
+ /// Returns an equivalent value with all free regions removed (note
+ /// that late-bound regions remain, because they are important for
+ /// subtyping, but they are anonymized and normalized as well)..
+ pub fn erase_regions<T>(self, value: &T) -> T
+ where T : TypeFoldable<'tcx>
+ {
+ let value1 = value.fold_with(&mut RegionEraserVisitor { tcx: self });
+ debug!("erase_regions({:?}) = {:?}", value, value1);
+ value1
+ }
+}
+
+struct RegionEraserVisitor<'a, 'gcx: 'tcx, 'tcx: 'a> {
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+}
+
+impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
+ self.tcx
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if let Some(ty_lifted) = self.tcx.lift_to_global(&ty) {
+ self.tcx.erase_regions_ty(ty_lifted)
+ } else {
+ ty.super_fold_with(self)
+ }
+ }
+
+ fn fold_binder<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
+ where T : TypeFoldable<'tcx>
+ {
+ let u = self.tcx.anonymize_late_bound_regions(t);
+ u.super_fold_with(self)
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ // because late-bound regions affect subtyping, we can't
+ // erase the bound/free distinction, but we can replace
+ // all free regions with 'erased.
+ //
+ // Note that we *CAN* replace early-bound regions -- the
+ // type system never "sees" those, they get substituted
+ // away. In trans, they will always be erased to 'erased
+ // whenever a substitution occurs.
+ match *r {
+ ty::ReLateBound(..) => r,
+ _ => self.tcx.types.re_erased
+ }
+ }
+}
+
}
}
-///////////////////////////////////////////////////////////////////////////
-// Region eraser
-
-impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
- /// Returns an equivalent value with all free regions removed (note
- /// that late-bound regions remain, because they are important for
- /// subtyping, but they are anonymized and normalized as well)..
- pub fn erase_regions<T>(self, value: &T) -> T
- where T : TypeFoldable<'tcx>
- {
- let value1 = value.fold_with(&mut RegionEraser(self));
- debug!("erase_regions({:?}) = {:?}",
- value, value1);
- return value1;
-
- struct RegionEraser<'a, 'gcx: 'a+'tcx, 'tcx: 'a>(TyCtxt<'a, 'gcx, 'tcx>);
-
- impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraser<'a, 'gcx, 'tcx> {
- fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.0 }
-
- fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- if let Some(u) = self.tcx().normalized_cache.borrow().get(&ty).cloned() {
- return u;
- }
-
- // FIXME(eddyb) should local contexts have a cache too?
- if let Some(ty_lifted) = self.tcx().lift_to_global(&ty) {
- let tcx = self.tcx().global_tcx();
- let t_norm = ty_lifted.super_fold_with(&mut RegionEraser(tcx));
- tcx.normalized_cache.borrow_mut().insert(ty_lifted, t_norm);
- t_norm
- } else {
- ty.super_fold_with(self)
- }
- }
-
- fn fold_binder<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
- where T : TypeFoldable<'tcx>
- {
- let u = self.tcx().anonymize_late_bound_regions(t);
- u.super_fold_with(self)
- }
-
- fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- // because late-bound regions affect subtyping, we can't
- // erase the bound/free distinction, but we can replace
- // all free regions with 'erased.
- //
- // Note that we *CAN* replace early-bound regions -- the
- // type system never "sees" those, they get substituted
- // away. In trans, they will always be erased to 'erased
- // whenever a substitution occurs.
- match *r {
- ty::ReLateBound(..) => r,
- _ => self.tcx().types.re_erased
- }
- }
- }
- }
-}
-
///////////////////////////////////////////////////////////////////////////
// Region shifter
//
use util::nodemap::{FxHashMap, FxHashSet};
use ty::context::TyCtxt;
-use ty::{AdtDef, VariantDef, FieldDef, TyS};
+use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS};
use ty::{DefId, Substs};
use ty::{AdtKind, Visibility};
use ty::TypeVariants::*;
// This code should only compile in modules where the uninhabitedness of Foo is
// visible.
+impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
+ /// Checks whether a type is visibly uninhabited from a particular module.
+ /// # Example
+ /// ```rust
+ /// enum Void {}
+ /// mod a {
+ /// pub mod b {
+ /// pub struct SecretlyUninhabited {
+ /// _priv: !,
+ /// }
+ /// }
+ /// }
+ ///
+ /// mod c {
+ /// pub struct AlsoSecretlyUninhabited {
+ /// _priv: Void,
+ /// }
+ /// mod d {
+ /// }
+ /// }
+ ///
+ /// struct Foo {
+ /// x: a::b::SecretlyUninhabited,
+ /// y: c::AlsoSecretlyUninhabited,
+ /// }
+ /// ```
+ /// In this code, the type `Foo` will only be visibly uninhabited inside the
+ /// modules b, c and d. This effects pattern-matching on `Foo` or types that
+ /// contain `Foo`.
+ ///
+ /// # Example
+ /// ```rust
+ /// let foo_result: Result<T, Foo> = ... ;
+ /// let Ok(t) = foo_result;
+ /// ```
+ /// This code should only compile in modules where the uninhabitedness of Foo is
+ /// visible.
+ pub fn is_ty_uninhabited_from(self, module: DefId, ty: Ty<'tcx>) -> bool {
+ // To check whether this type is uninhabited at all (not just from the
+ // given node) you could check whether the forest is empty.
+ // ```
+ // forest.is_empty()
+ // ```
+ self.ty_inhabitedness_forest(ty).contains(self, module)
+ }
+
+ pub fn is_ty_uninhabited_from_all_modules(self, ty: Ty<'tcx>) -> bool {
+ !self.ty_inhabitedness_forest(ty).is_empty()
+ }
+
+ fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
+ ty.uninhabited_from(&mut FxHashMap(), self)
+ }
+
+ pub fn is_enum_variant_uninhabited_from(self,
+ module: DefId,
+ variant: &'tcx VariantDef,
+ substs: &'tcx Substs<'tcx>)
+ -> bool
+ {
+ self.variant_inhabitedness_forest(variant, substs).contains(self, module)
+ }
+
+ pub fn is_variant_uninhabited_from_all_modules(self,
+ variant: &'tcx VariantDef,
+ substs: &'tcx Substs<'tcx>)
+ -> bool
+ {
+ !self.variant_inhabitedness_forest(variant, substs).is_empty()
+ }
+
+ fn variant_inhabitedness_forest(self, variant: &'tcx VariantDef, substs: &'tcx Substs<'tcx>)
+ -> DefIdForest {
+ // Determine the ADT kind:
+ let adt_def_id = self.adt_def_id_of_variant(variant);
+ let adt_kind = self.adt_def(adt_def_id).adt_kind();
+
+ // Compute inhabitedness forest:
+ variant.uninhabited_from(&mut FxHashMap(), self, substs, adt_kind)
+ }
+}
+
impl<'a, 'gcx, 'tcx> AdtDef {
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
- pub fn uninhabited_from(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>) -> DefIdForest
+ fn uninhabited_from(
+ &self,
+ visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>) -> DefIdForest
{
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
impl<'a, 'gcx, 'tcx> VariantDef {
/// Calculate the forest of DefIds from which this variant is visibly uninhabited.
- pub fn uninhabited_from(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>,
- adt_kind: AdtKind) -> DefIdForest
+ fn uninhabited_from(
+ &self,
+ visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>,
+ adt_kind: AdtKind) -> DefIdForest
{
match adt_kind {
AdtKind::Union => {
impl<'a, 'gcx, 'tcx> FieldDef {
/// Calculate the forest of DefIds from which this field is visibly uninhabited.
- pub fn uninhabited_from(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- substs: &'tcx Substs<'tcx>,
- is_enum: bool) -> DefIdForest
+ fn uninhabited_from(
+ &self,
+ visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ substs: &'tcx Substs<'tcx>,
+ is_enum: bool) -> DefIdForest
{
let mut data_uninhabitedness = move || {
self.ty(tcx, substs).uninhabited_from(visited, tcx)
impl<'a, 'gcx, 'tcx> TyS<'tcx> {
/// Calculate the forest of DefIds from which this type is visibly uninhabited.
- pub fn uninhabited_from(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
- {
- match tcx.lift_to_global(&self) {
- Some(global_ty) => {
- {
- let cache = tcx.inhabitedness_cache.borrow();
- if let Some(forest) = cache.get(&global_ty) {
- return forest.clone();
- }
- }
- let forest = global_ty.uninhabited_from_inner(visited, tcx);
- let mut cache = tcx.inhabitedness_cache.borrow_mut();
- cache.insert(global_ty, forest.clone());
- forest
- },
- None => {
- let forest = self.uninhabited_from_inner(visited, tcx);
- forest
- },
- }
- }
-
- fn uninhabited_from_inner(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
+ fn uninhabited_from(
+ &self,
+ visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
+ tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
{
match self.sty {
TyAdt(def, substs) => {
impl<M: QueryConfig<Key=DefId>> QueryDescription for M {
default fn describe(tcx: TyCtxt, def_id: DefId) -> String {
- format!("processing `{}`", tcx.item_path_str(def_id))
+ if !tcx.sess.verbose() {
+ format!("processing `{}`", tcx.item_path_str(def_id))
+ } else {
+ let name = unsafe { ::std::intrinsics::type_name::<M>() };
+ format!("processing `{}` applied to `{:?}`", name, def_id)
+ }
}
}
}
}
+impl<'tcx> QueryDescription for queries::erase_regions_ty<'tcx> {
+ fn describe(_tcx: TyCtxt, ty: Ty<'tcx>) -> String {
+ format!("erasing regions from `{:?}`", ty)
+ }
+}
+
impl<'tcx> QueryDescription for queries::type_param_predicates<'tcx> {
fn describe(tcx: TyCtxt, (_, def_id): (DefId, DefId)) -> String {
let id = tcx.hir.as_local_node_id(def_id).unwrap();
}
}
+impl<'tcx> QueryDescription for queries::rvalue_promotable_map<'tcx> {
+ fn describe(tcx: TyCtxt, def_id: DefId) -> String {
+ format!("checking which parts of `{}` are promotable to static",
+ tcx.item_path_str(def_id))
+ }
+}
+
impl<'tcx> QueryDescription for queries::is_mir_available<'tcx> {
fn describe(tcx: TyCtxt, def_id: DefId) -> String {
format!("checking if item is mir available: `{}`",
use ty::layout::{Layout, LayoutError};
use ty::steal::Steal;
use ty::subst::Substs;
-use util::nodemap::{DefIdSet, DefIdMap};
+use util::nodemap::{DefIdSet, DefIdMap, ItemLocalMap};
use util::common::{profq_msg, ProfileQueriesMsg};
use rustc_data_structures::indexed_set::IdxSetBuf;
[] fn is_exported_symbol: IsExportedSymbol(DefId) -> bool,
[] fn item_body_nested_bodies: ItemBodyNestedBodies(DefId) -> ExternBodyNestedBodies,
[] fn const_is_rvalue_promotable_to_static: ConstIsRvaluePromotableToStatic(DefId) -> bool,
+ [] fn rvalue_promotable_map: RvaluePromotableMap(DefId) -> Rc<ItemLocalMap<bool>>,
[] fn is_mir_available: IsMirAvailable(DefId) -> bool,
[] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>)
-> Rc<Vec<Option<(DefId, &'tcx Substs<'tcx>)>>>,
[] fn has_copy_closures: HasCopyClosures(CrateNum) -> bool,
[] fn has_clone_closures: HasCloneClosures(CrateNum) -> bool,
+
+ // Erases regions from `ty` to yield a new type.
+ // Normally you would just use `tcx.erase_regions(&value)`,
+ // however, which uses this query as a kind of cache.
+ [] fn erase_regions_ty: erase_regions_ty(Ty<'tcx>) -> Ty<'tcx>,
}
//////////////////////////////////////////////////////////////////////
// These functions are little shims used to find the dep-node for a
// given query when there is not a *direct* mapping:
+fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> {
+ DepConstructor::EraseRegionsTy { ty }
+}
+
fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> {
DepConstructor::TypeParamPredicates {
item_id,
DepKind::CompileCodegenUnit |
DepKind::FulfillObligation |
DepKind::VtableMethods |
+ DepKind::EraseRegionsTy |
// These are just odd
DepKind::Null |
DepKind::ConstIsRvaluePromotableToStatic => {
force!(const_is_rvalue_promotable_to_static, def_id!());
}
+ DepKind::RvaluePromotableMap => { force!(rvalue_promotable_map, def_id!()); }
DepKind::ImplParent => { force!(impl_parent, def_id!()); }
DepKind::TraitOfItem => { force!(trait_of_item, def_id!()); }
DepKind::IsExportedSymbol => { force!(is_exported_symbol, def_id!()); }
use hir::{map as hir_map, FreevarMap, TraitMap};
use hir::def::{Def, CtorKind, ExportMap};
use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
+use hir::map::DefPathData;
use ich::StableHashingContext;
use middle::const_val::ConstVal;
use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
pub mod binding;
pub mod cast;
pub mod error;
+mod erase_regions;
pub mod fast_reject;
pub mod fold;
pub mod inhabitedness;
}
}
+ /// Given a `VariantDef`, returns the def-id of the `AdtDef` of which it is a part.
+ pub fn adt_def_id_of_variant(self, variant_def: &'tcx VariantDef) -> DefId {
+ let def_key = self.def_key(variant_def.did);
+ match def_key.disambiguated_data.data {
+ // for enum variants and tuple structs, the def-id of the ADT itself
+ // is the *parent* of the variant
+ DefPathData::EnumVariant(..) | DefPathData::StructCtor =>
+ DefId { krate: variant_def.did.krate, index: def_key.parent.unwrap() },
+
+ // otherwise, for structs and unions, they share a def-id
+ _ => variant_def.did,
+ }
+ }
+
pub fn item_name(self, id: DefId) -> InternedString {
if let Some(id) = self.hir.as_local_node_id(id) {
self.hir.name(id).as_str()
pub fn provide(providers: &mut ty::maps::Providers) {
util::provide(providers);
context::provide(providers);
+ erase_regions::provide(providers);
*providers = ty::maps::Providers {
associated_item,
associated_item_def_ids,
use syntax::abi;
use syntax::ast::{self, Name};
use syntax::symbol::keywords;
-use util::nodemap::FxHashMap;
use serialize;
}
}
- /// Checks whether a type is visibly uninhabited from a particular module.
- /// # Example
- /// ```rust
- /// enum Void {}
- /// mod a {
- /// pub mod b {
- /// pub struct SecretlyUninhabited {
- /// _priv: !,
- /// }
- /// }
- /// }
- ///
- /// mod c {
- /// pub struct AlsoSecretlyUninhabited {
- /// _priv: Void,
- /// }
- /// mod d {
- /// }
- /// }
- ///
- /// struct Foo {
- /// x: a::b::SecretlyUninhabited,
- /// y: c::AlsoSecretlyUninhabited,
- /// }
- /// ```
- /// In this code, the type `Foo` will only be visibly uninhabited inside the
- /// modules b, c and d. This effects pattern-matching on `Foo` or types that
- /// contain `Foo`.
- ///
- /// # Example
- /// ```rust
- /// let foo_result: Result<T, Foo> = ... ;
- /// let Ok(t) = foo_result;
- /// ```
- /// This code should only compile in modules where the uninhabitedness of Foo is
- /// visible.
- pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
- let mut visited = FxHashMap::default();
- let forest = self.uninhabited_from(&mut visited, tcx);
-
- // To check whether this type is uninhabited at all (not just from the
- // given node) you could check whether the forest is empty.
- // ```
- // forest.is_empty()
- // ```
- forest.contains(tcx, module)
- }
-
pub fn is_primitive(&self) -> bool {
match self.sty {
TyBool | TyChar | TyInt(_) | TyUint(_) | TyFloat(_) => true,
all_loans,
param_env,
};
- euv::ExprUseVisitor::new(&mut clcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables)
+ let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
+ euv::ExprUseVisitor::new(&mut clcx,
+ bccx.tcx,
+ param_env,
+ &bccx.region_scope_tree,
+ bccx.tables,
+ Some(rvalue_promotable_map))
.consume_body(body);
}
move_error_collector: move_error::MoveErrorCollector::new(),
};
- euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, param_env, &bccx.region_scope_tree, bccx.tables)
+ let rvalue_promotable_map = bccx.tcx.rvalue_promotable_map(def_id);
+ euv::ExprUseVisitor::new(&mut glcx,
+ bccx.tcx,
+ param_env,
+ &bccx.region_scope_tree,
+ bccx.tables,
+ Some(rvalue_promotable_map))
.consume_body(bccx.body);
glcx.report_potential_errors();
use rustc::hir::def_id::DefId;
use rustc::hir::RangeEnd;
-use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
+use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::mir::Field;
use rustc::util::common::ErrorReported;
fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
if self.tcx.sess.features.borrow().never_type {
- ty.is_uninhabited_from(self.module, self.tcx)
+ self.tcx.is_ty_uninhabited_from(self.module, ty)
} else {
false
}
fn is_variant_uninhabited(&self,
variant: &'tcx ty::VariantDef,
- substs: &'tcx ty::subst::Substs<'tcx>) -> bool
+ substs: &'tcx ty::subst::Substs<'tcx>)
+ -> bool
{
if self.tcx.sess.features.borrow().never_type {
- let forest = variant.uninhabited_from(
- &mut FxHashMap::default(), self.tcx, substs, AdtKind::Enum
- );
- forest.contains(self.tcx, self.module)
+ self.tcx.is_enum_variant_uninhabited_from(self.module, variant, substs)
} else {
false
}
let module = self.tcx.hir.get_module_parent(scrut.id);
if inlined_arms.is_empty() {
let scrutinee_is_uninhabited = if self.tcx.sess.features.borrow().never_type {
- pat_ty.is_uninhabited_from(module, self.tcx)
+ self.tcx.is_ty_uninhabited_from(module, pat_ty)
} else {
self.conservative_is_uninhabited(pat_ty)
};
let mut checker = MutationChecker {
cx,
};
- ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables)
+ ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables, None)
.walk_expr(guard);
}
use rustc_privacy;
use rustc_plugin::registry::Registry;
use rustc_plugin as plugin;
-use rustc_passes::{ast_validation, no_asm, loops, consts, static_recursion, hir_stats};
+use rustc_passes::{self, ast_validation, no_asm, loops, consts, static_recursion, hir_stats};
use rustc_const_eval::{self, check_match};
use super::Compilation;
use ::DefaultTransCrate;
traits::provide(&mut local_providers);
reachable::provide(&mut local_providers);
rustc_const_eval::provide(&mut local_providers);
+ rustc_passes::provide(&mut local_providers);
middle::region::provide(&mut local_providers);
cstore::provide_local(&mut local_providers);
lint::provide(&mut local_providers);
};
let lazy_body = self.lazy(body);
- let tables = self.tcx.body_tables(body_id);
+ let body_owner_def_id = self.tcx.hir.body_owner_def_id(body_id);
+ let tables = self.tcx.typeck_tables_of(body_owner_def_id);
let lazy_tables = self.lazy(tables);
let mut visitor = NestedBodyCollector {
let lazy_nested_bodies = self.lazy_seq_ref_from_slice(&visitor.bodies_found);
let rvalue_promotable_to_static =
- self.tcx.rvalue_promotable_to_static.borrow()[&body.value.id];
+ self.tcx.const_is_rvalue_promotable_to_static(body_owner_def_id);
self.lazy(&Ast {
body: lazy_body,
use build::matches::{Binding, MatchPair, Candidate};
use hair::*;
use rustc::mir::*;
-use rustc_data_structures::fx::FxHashMap;
use std::mem;
if self.hir.tcx().sess.features.borrow().never_type {
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
i == variant_index || {
- let mut visited = FxHashMap::default();
- let node_set = v.uninhabited_from(&mut visited,
- self.hir.tcx(),
- substs,
- adt_def.adt_kind());
- !node_set.is_empty()
+ self.hir.tcx().is_variant_uninhabited_from_all_modules(v, substs)
}
});
if irrefutable {
of `x` when we set `y`. This is fundamental to Rust's ownership system: outside
of workarounds like `Rc`, a value cannot be owned by more than one variable.
-If we own the type, the easiest way to address this problem is to implement
-`Copy` and `Clone` on it, as shown below. This allows `y` to copy the
-information in `x`, while leaving the original version owned by `x`. Subsequent
-changes to `x` will not be reflected when accessing `y`.
+Sometimes we don't need to move the value. Using a reference, we can let another
+function borrow the value without changing its ownership. In the example below,
+we don't actually have to move our string to `calculate_length`, we can give it
+a reference to it with `&` instead.
+
+```
+fn main() {
+ let s1 = String::from("hello");
+
+ let len = calculate_length(&s1);
+
+ println!("The length of '{}' is {}.", s1, len);
+}
+
+fn calculate_length(s: &String) -> usize {
+ s.len()
+}
+```
+
+A mutable reference can be created with `&mut`.
+
+Sometimes we don't want a reference, but a duplicate. All types marked `Clone`
+can be duplicated by calling `.clone()`. Subsequent changes to a clone do not
+affect the original variable.
+
+Most types in the standard library are marked `Clone`. The example below
+demonstrates using `clone()` on a string. `s1` is first set to "many", and then
+copied to `s2`. Then the first character of `s1` is removed, without affecting
+`s2`. "any many" is printed to the console.
+
+```
+fn main() {
+ let mut s1 = String::from("many");
+ let s2 = s1.clone();
+ s1.remove(0);
+ println!("{} {}", s1, s2);
+}
+```
+
+If we control the definition of a type, we can implement `Clone` on it ourselves
+with `#[derive(Clone)]`.
+
+Some types have no ownership semantics at all and are trivial to duplicate. An
+example is `i32` and the other number types. We don't have to call `.clone()` to
+clone them, because they are marked `Copy` in addition to `Clone`. Implicit
+cloning is more convienient in this case. We can mark our own types `Copy` if
+all their members also are marked `Copy`.
+
+In the example below, we implement a `Point` type. Because it only stores two
+integers, we opt-out of ownership semantics with `Copy`. Then we can
+`let p2 = p1` without `p1` being moved.
```
#[derive(Copy, Clone)]
-struct MyStruct { s: u32 }
+struct Point { x: i32, y: i32 }
fn main() {
- let mut x = MyStruct{ s: 5u32 };
- let y = x;
- x.s = 6;
- println!("{}", x.s);
+ let mut p1 = Point{ x: -1, y: 2 };
+ let p2 = p1;
+ p1.x = 1;
+ println!("p1: {}, {}", p1.x, p1.y);
+ println!("p2: {}, {}", p2.x, p2.y);
}
```
use rustc::middle::mem_categorization::Categorization;
use rustc::mir::transform::MirSource;
use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::maps::{queries, Providers};
use rustc::ty::subst::Substs;
use rustc::traits::Reveal;
use rustc::util::common::ErrorReported;
-use rustc::util::nodemap::NodeSet;
+use rustc::util::nodemap::{ItemLocalMap, NodeSet};
use rustc::lint::builtin::CONST_ERR;
-
use rustc::hir::{self, PatKind, RangeEnd};
+use std::rc::Rc;
use syntax::ast;
use syntax_pos::{Span, DUMMY_SP};
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use std::collections::hash_map::Entry;
use std::cmp::Ordering;
+pub fn provide(providers: &mut Providers) {
+ *providers = Providers {
+ rvalue_promotable_map,
+ const_is_rvalue_promotable_to_static,
+ ..*providers
+ };
+}
+
+pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+ for &body_id in &tcx.hir.krate().body_ids {
+ let def_id = tcx.hir.body_owner_def_id(body_id);
+ tcx.const_is_rvalue_promotable_to_static(def_id);
+ }
+ tcx.sess.abort_if_errors();
+}
+
+fn const_is_rvalue_promotable_to_static<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ def_id: DefId)
+ -> bool
+{
+ assert!(def_id.is_local());
+
+ let node_id = tcx.hir.as_local_node_id(def_id)
+ .expect("rvalue_promotable_map invoked with non-local def-id");
+ let body_id = tcx.hir.body_owned_by(node_id);
+ let body_hir_id = tcx.hir.node_to_hir_id(body_id.node_id);
+ tcx.rvalue_promotable_map(def_id).contains_key(&body_hir_id.local_id)
+}
+
+fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ def_id: DefId)
+ -> Rc<ItemLocalMap<bool>>
+{
+ let outer_def_id = tcx.closure_base_def_id(def_id);
+ if outer_def_id != def_id {
+ return tcx.rvalue_promotable_map(outer_def_id);
+ }
+
+ let mut visitor = CheckCrateVisitor {
+ tcx,
+ tables: &ty::TypeckTables::empty(None),
+ in_fn: false,
+ in_static: false,
+ promotable: false,
+ mut_rvalue_borrows: NodeSet(),
+ param_env: ty::ParamEnv::empty(Reveal::UserFacing),
+ identity_substs: Substs::empty(),
+ result_map: ItemLocalMap(),
+ };
+
+ // `def_id` should be a `Body` owner
+ let node_id = tcx.hir.as_local_node_id(def_id)
+ .expect("rvalue_promotable_map invoked with non-local def-id");
+ let body_id = tcx.hir.body_owned_by(node_id);
+ visitor.visit_nested_body(body_id);
+
+ Rc::new(visitor.result_map)
+}
+
struct CheckCrateVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
in_fn: bool,
param_env: ty::ParamEnv<'tcx>,
identity_substs: &'tcx Substs<'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
+ result_map: ItemLocalMap<bool>,
}
impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
+ // note that we *do* visit nested bodies, because we override `visit_nested_body` below
NestedVisitorMap::None
}
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
- match self.tcx.rvalue_promotable_to_static.borrow_mut().entry(body_id.node_id) {
- Entry::Occupied(_) => return,
- Entry::Vacant(entry) => {
- // Prevent infinite recursion on re-entry.
- entry.insert(false);
- }
- }
-
let item_id = self.tcx.hir.body_owner(body_id);
let item_def_id = self.tcx.hir.local_def_id(item_id);
let tcx = self.tcx;
let param_env = self.param_env;
let region_scope_tree = self.tcx.region_scope_tree(item_def_id);
- euv::ExprUseVisitor::new(self, tcx, param_env, ®ion_scope_tree, self.tables)
+ euv::ExprUseVisitor::new(self, tcx, param_env, ®ion_scope_tree, self.tables, None)
.consume_body(body);
self.visit_body(body);
}
}
- self.tcx.rvalue_promotable_to_static.borrow_mut().insert(ex.id, self.promotable);
+ self.result_map.insert(ex.hir_id.local_id, self.promotable);
self.promotable &= outer;
}
}
let promotable = if v.tcx.trait_of_item(did).is_some() {
// Don't peek inside trait associated constants.
false
- } else if let Some(node_id) = v.tcx.hir.as_local_node_id(did) {
- match v.tcx.hir.maybe_body_owned_by(node_id) {
- Some(body) => {
- v.visit_nested_body(body);
- v.tcx.rvalue_promotable_to_static.borrow()[&body.node_id]
- }
- None => false
- }
} else {
- v.tcx.const_is_rvalue_promotable_to_static(did)
+ queries::const_is_rvalue_promotable_to_static::try_get(v.tcx, e.span, did)
+ .unwrap_or_else(|mut err| {
+ // A cycle between constants ought to be reported elsewhere.
+ err.cancel();
+ v.tcx.sess.delay_span_bug(
+ e.span,
+ &format!("cycle encountered during const qualification: {:?}",
+ did));
+ false
+ })
};
// Just in case the type is more specific than the definition,
}
}
-pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
- tcx.hir.krate().visit_all_item_likes(&mut CheckCrateVisitor {
- tcx,
- tables: &ty::TypeckTables::empty(None),
- in_fn: false,
- in_static: false,
- promotable: false,
- mut_rvalue_borrows: NodeSet(),
- param_env: ty::ParamEnv::empty(Reveal::UserFacing),
- identity_substs: Substs::empty(),
- }.as_deep_visitor());
- tcx.sess.abort_if_errors();
-}
-
impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
fn consume(&mut self,
_consume_id: ast::NodeId,
extern crate syntax_pos;
extern crate rustc_errors as errors;
+use rustc::ty::maps::Providers;
+
mod diagnostics;
pub mod ast_validation;
pub mod static_recursion;
__build_diagnostic_array! { librustc_passes, DIAGNOSTICS }
+
+pub fn provide(providers: &mut Providers) {
+ consts::provide(providers);
+}
stable("no-default", |o| {
o.optflag("", "no-defaults", "don't run the default passes")
}),
+ stable("document-private-items", |o| {
+ o.optflag("", "document-private-items", "document private items")
+ }),
stable("test", |o| o.optflag("", "test", "run code examples as tests")),
stable("test-args", |o| {
o.optmulti("", "test-args", "arguments to pass to the test runner",
// Check for unstable options.
nightly_options::check_nightly_options(&matches, &opts());
+ // check for deprecated options
+ check_deprecated_options(&matches);
+
if matches.opt_present("h") || matches.opt_present("help") {
usage("rustdoc");
return 0;
let mut passes = matches.opt_strs("passes");
let mut plugins = matches.opt_strs("plugins");
+ // We hardcode in the passes here, as this is a new flag and we
+ // are generally deprecating passes.
+ if matches.opt_present("document-private-items") {
+ default_passes = false;
+
+ passes = vec![
+ String::from("strip-hidden"),
+ String::from("collapse-docs"),
+ String::from("unindent-comments"),
+ ];
+ }
+
// First, parse the crate and extract all relevant information.
let mut paths = SearchPaths::new();
for s in &matches.opt_strs("L") {
});
rx.recv().unwrap()
}
+
+/// Prints deprecation warnings for deprecated options
+fn check_deprecated_options(matches: &getopts::Matches) {
+ let deprecated_flags = [
+ "input-format",
+ "output-format",
+ "plugin-path",
+ "plugins",
+ "no-defaults",
+ "passes",
+ ];
+
+ for flag in deprecated_flags.into_iter() {
+ if matches.opt_present(flag) {
+ eprintln!("WARNING: the '{}' flag is considered deprecated", flag);
+ eprintln!("WARNING: please see https://github.com/rust-lang/rust/issues/44136");
+ }
+ }
+
+ if matches.opt_present("no-defaults") {
+ eprintln!("WARNING: (you may want to use --document-private-items)");
+ }
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(never_type)]
+
+// Regression test for inhabitedness check. The old
+// cache used to cause us to incorrectly decide
+// that `test_b` was invalid.
+
+struct Foo {
+ field1: !,
+ field2: Option<&'static Bar>,
+}
+
+struct Bar {
+ field1: &'static Foo
+}
+
+fn test_a() {
+ let x: Option<Foo> = None;
+ match x { None => () }
+}
+
+fn test_b() {
+ let x: Option<Bar> = None;
+ match x { None => () }
+}
+
+fn main() { }
// except according to those terms.
// ignore-tidy-linelength
-// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports
+// compile-flags: --document-private-items
// @has 'empty_mod_private/index.html' '//a[@href="foo/index.html"]' 'foo'
// @has 'empty_mod_private/sidebar-items.js' 'foo'
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// compile-flags:--no-defaults --passes collapse-docs --passes unindent-comments
+// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments
// @has issue_15347/fn.foo.html
#[doc(hidden)]
// except according to those terms.
// ignore-tidy-linelength
-// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports
+// compile-flags: --document-private-items
#![crate_name = "foo"]
miri = "Broken"
# ping @Manishearth @llogiq @mcarton @oli-obk
-clippy = "Compiling"
+clippy = "Broken"
# ping @nrc
rls = "Testing"