Skip to content

Commit

Permalink
Auto merge of #38069 - canndrew:empty-sub-patterns-again, r=nikomatsakis
Browse files Browse the repository at this point in the history
Fix handling of empty types in patterns.

Fix for #12609.
  • Loading branch information
bors committed Jan 6, 2017
2 parents 42bed72 + 275c19d commit 6f1ae66
Show file tree
Hide file tree
Showing 43 changed files with 1,172 additions and 300 deletions.
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

0 comments on commit 6f1ae66

Please sign in to comment.