Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of empty types in patterns. #38069

Merged
merged 22 commits into from
Jan 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ declare_lint! {
"detects unreachable code paths"
}

declare_lint! {
pub UNREACHABLE_PATTERNS,
Warn,
"detects unreachable patterns"
}

declare_lint! {
pub WARNINGS,
Warn,
Expand Down Expand Up @@ -239,6 +245,7 @@ impl LintPass for HardwiredLints {
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
WARNINGS,
UNUSED_FEATURES,
STABLE_FEATURES,
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,10 @@ impl<'tcx> Lvalue<'tcx> {
self.elem(ProjectionElem::Deref)
}

pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Downcast(adt_def, variant_index))
}

pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Index(index))
}
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate};
use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid};
use ty::TypeVariants::*;
use ty::layout::{Layout, TargetDataLayout};
use ty::inhabitedness::DefIdForest;
use ty::maps;
use util::common::MemoizationMap;
use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
Expand Down Expand Up @@ -459,6 +460,8 @@ pub struct GlobalCtxt<'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>>,

pub lang_items: middle::lang_items::LanguageItems,

/// Maps from def-id of a type or region parameter to its
Expand Down Expand Up @@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
ty_param_defs: RefCell::new(NodeMap()),
normalized_cache: RefCell::new(FxHashMap()),
inhabitedness_cache: RefCell::new(FxHashMap()),
lang_items: lang_items,
inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())),
used_unsafe: RefCell::new(NodeSet()),
Expand Down
133 changes: 133 additions & 0 deletions src/librustc/ty/inhabitedness/def_id_forest.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2012-2015 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 std::mem;
use rustc_data_structures::small_vec::SmallVec;
use syntax::ast::CRATE_NODE_ID;
use ty::context::TyCtxt;
use ty::{DefId, DefIdTree};

/// Represents a forest of DefIds closed under the ancestor relation. That is,
/// if a DefId representing a module is contained in the forest then all
/// DefIds defined in that module or submodules are also implicitly contained
/// in the forest.
///
/// This is used to represent a set of modules in which a type is visibly
/// uninhabited.
#[derive(Clone)]
pub struct DefIdForest {
/// The minimal set of DefIds required to represent the whole set.
/// If A and B are DefIds in the DefIdForest, and A is a desecendant
/// of B, then only B will be in root_ids.
/// We use a SmallVec here because (for its use for cacheing inhabitedness)
/// its rare that this will contain even two ids.
root_ids: SmallVec<[DefId; 1]>,
}

impl<'a, 'gcx, 'tcx> DefIdForest {
/// Create an empty forest.
pub fn empty() -> DefIdForest {
DefIdForest {
root_ids: SmallVec::new(),
}
}

/// Create a forest consisting of a single tree representing the entire
/// crate.
#[inline]
pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest {
let crate_id = tcx.map.local_def_id(CRATE_NODE_ID);
DefIdForest::from_id(crate_id)
}

/// Create a forest containing a DefId and all its descendants.
pub fn from_id(id: DefId) -> DefIdForest {
let mut root_ids = SmallVec::new();
root_ids.push(id);
DefIdForest {
root_ids: root_ids,
}
}

/// Test whether the forest is empty.
pub fn is_empty(&self) -> bool {
self.root_ids.is_empty()
}

/// Test whether the forest conains a given DefId.
pub fn contains(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
id: DefId) -> bool
{
for root_id in self.root_ids.iter() {
if tcx.is_descendant_of(id, *root_id) {
return true;
}
}
false
}

/// Calculate the intersection of a collection of forests.
pub fn intersection<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
iter: I) -> DefIdForest
where I: IntoIterator<Item=DefIdForest>
{
let mut ret = DefIdForest::full(tcx);
let mut next_ret = SmallVec::new();
let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new();
for next_forest in iter {
for id in ret.root_ids.drain(..) {
if next_forest.contains(tcx, id) {
next_ret.push(id);
} else {
old_ret.push(id);
}
}
ret.root_ids.extend(old_ret.drain(..));

for id in next_forest.root_ids {
if ret.contains(tcx, id) {
next_ret.push(id);
}
}

mem::swap(&mut next_ret, &mut ret.root_ids);
next_ret.drain(..);
}
ret
}

/// Calculate the union of a collection of forests.
pub fn union<I>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
iter: I) -> DefIdForest
where I: IntoIterator<Item=DefIdForest>
{
let mut ret = DefIdForest::empty();
let mut next_ret = SmallVec::new();
for next_forest in iter {
for id in ret.root_ids.drain(..) {
if !next_forest.contains(tcx, id) {
next_ret.push(id);
}
}

for id in next_forest.root_ids {
if !next_ret.contains(&id) {
next_ret.push(id);
}
}

mem::swap(&mut next_ret, &mut ret.root_ids);
next_ret.drain(..);
}
ret
}
}

199 changes: 199 additions & 0 deletions src/librustc/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2012-2015 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 util::nodemap::FxHashSet;
use ty::context::TyCtxt;
use ty::{AdtDef, VariantDef, FieldDef, TyS};
use ty::{DefId, Substs};
use ty::{AdtKind, Visibility};
use ty::TypeVariants::*;

pub use self::def_id_forest::DefIdForest;

mod def_id_forest;

// The methods in this module calculate DefIdForests of modules in which a
// AdtDef/VariantDef/FieldDef is visibly uninhabited.
//
// # 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. Calling uninhabited_from on Foo or its AdtDef will
// return the forest of modules {b, c->d} (represented in a DefIdForest by the
// set {b, c})
//
// We need this information for 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.

impl<'a, 'gcx, 'tcx> AdtDef {
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
pub fn uninhabited_from(
&self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>) -> DefIdForest
{
if !visited.insert((self.did, substs)) {
return DefIdForest::empty();
}

let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
}));
visited.remove(&(self.did, substs));
ret
}
}

impl<'a, 'gcx, 'tcx> VariantDef {
/// Calculate the forest of DefIds from which this variant is visibly uninhabited.
pub fn uninhabited_from(
&self,
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>,
adt_kind: AdtKind) -> DefIdForest
{
match adt_kind {
AdtKind::Union => {
DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
f.uninhabited_from(visited, tcx, substs, false)
}))
},
AdtKind::Struct => {
DefIdForest::union(tcx, self.fields.iter().map(|f| {
f.uninhabited_from(visited, tcx, substs, false)
}))
},
AdtKind::Enum => {
DefIdForest::union(tcx, self.fields.iter().map(|f| {
f.uninhabited_from(visited, tcx, substs, true)
}))
},
}
}
}

impl<'a, 'gcx, 'tcx> FieldDef {
/// Calculate the forest of DefIds from which this field is visibly uninhabited.
pub fn uninhabited_from(
&self,
visited: &mut FxHashSet<(DefId, &'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);
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
// Visibility::Invisible so we need to override self.vis if we're
// dealing with an enum.
if is_enum {
data_uninhabitedness()
} else {
match self.vis {
Visibility::Invisible => DefIdForest::empty(),
Visibility::Restricted(from) => {
let forest = DefIdForest::from_id(from);
let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness()));
DefIdForest::intersection(tcx, iter)
},
Visibility::Public => data_uninhabitedness(),
}
}
}
}

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 FxHashSet<(DefId, &'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 FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
{
match self.sty {
TyAdt(def, substs) => {
def.uninhabited_from(visited, tcx, substs)
},

TyNever => DefIdForest::full(tcx),
TyTuple(ref tys) => {
DefIdForest::union(tcx, tys.iter().map(|ty| {
ty.uninhabited_from(visited, tcx)
}))
},
TyArray(ty, len) => {
if len == 0 {
DefIdForest::empty()
} else {
ty.uninhabited_from(visited, tcx)
}
}
TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx),

_ => DefIdForest::empty(),
}
}
}

Loading