-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #38069 - canndrew:empty-sub-patterns-again, r=nikomatsakis
Fix handling of empty types in patterns. Fix for #12609.
- Loading branch information
Showing
43 changed files
with
1,172 additions
and
300 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.