Merge remote-tracking branch 'origin/master' into miri

This commit is contained in:
Oliver Schneider 2017-12-11 10:01:29 +01:00
commit 7e5583b7f8
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
519 changed files with 11739 additions and 3359 deletions

1
.gitattributes vendored
View File

@ -7,3 +7,4 @@
src/etc/installer/gfx/* binary
*.woff binary
src/vendor/** -text
Cargo.lock -merge

View File

@ -336,7 +336,7 @@ will run all the tests on every platform we support. If it all works out,
Speaking of tests, Rust has a comprehensive test suite. More information about
it can be found
[here](https://github.com/rust-lang/rust-wiki-backup/blob/master/Note-testsuite.md).
[here](https://github.com/rust-lang/rust/blob/master/src/test/COMPILER_TESTS.md).
### External Dependencies
[external-dependencies]: #external-dependencies

View File

@ -13,20 +13,20 @@ Some things that might be helpful to you though:
# Search
* <form action="https://duckduckgo.com/">
<form action="https://duckduckgo.com/">
<input type="text" id="site-search" name="q" size="80"></input>
<input type="submit" value="Search DuckDuckGo">
</form>
* Rust doc search: <span id="core-search"></span>
<input type="submit" value="Search DuckDuckGo"></form>
Rust doc search: <span id="core-search"></span>
# Reference
* [The Rust official site](https://www.rust-lang.org)
* [The Rust reference](https://doc.rust-lang.org/reference/index.html)
* [The Rust official site](https://www.rust-lang.org)
* [The Rust reference](https://doc.rust-lang.org/reference/index.html)
# Docs
* [The standard library](https://doc.rust-lang.org/std/)
[The standard library](https://doc.rust-lang.org/std/)
<script>
function get_url_fragments() {

View File

@ -217,14 +217,8 @@ impl Layout {
/// On arithmetic overflow, returns `None`.
#[inline]
pub fn repeat(&self, n: usize) -> Option<(Self, usize)> {
let padded_size = match self.size.checked_add(self.padding_needed_for(self.align)) {
None => return None,
Some(padded_size) => padded_size,
};
let alloc_size = match padded_size.checked_mul(n) {
None => return None,
Some(alloc_size) => alloc_size,
};
let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?;
let alloc_size = padded_size.checked_mul(n)?;
// We can assume that `self.align` is a power-of-two that does
// not exceed 2<sup>31</sup>. Furthermore, `alloc_size` has already been
@ -246,26 +240,14 @@ impl Layout {
/// On arithmetic overflow, returns `None`.
pub fn extend(&self, next: Self) -> Option<(Self, usize)> {
let new_align = cmp::max(self.align, next.align);
let realigned = match Layout::from_size_align(self.size, new_align) {
None => return None,
Some(l) => l,
};
let realigned = Layout::from_size_align(self.size, new_align)?;
let pad = realigned.padding_needed_for(next.align);
let offset = match self.size.checked_add(pad) {
None => return None,
Some(offset) => offset,
};
let new_size = match offset.checked_add(next.size) {
None => return None,
Some(new_size) => new_size,
};
let offset = self.size.checked_add(pad)?;
let new_size = offset.checked_add(next.size)?;
let layout = match Layout::from_size_align(new_size, new_align) {
None => return None,
Some(l) => l,
};
let layout = Layout::from_size_align(new_size, new_align)?;
Some((layout, offset))
}
@ -282,11 +264,7 @@ impl Layout {
///
/// On arithmetic overflow, returns `None`.
pub fn repeat_packed(&self, n: usize) -> Option<Self> {
let size = match self.size().checked_mul(n) {
None => return None,
Some(scaled) => scaled,
};
let size = self.size().checked_mul(n)?;
Layout::from_size_align(size, self.align)
}
@ -306,14 +284,8 @@ impl Layout {
///
/// On arithmetic overflow, returns `None`.
pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> {
let new_size = match self.size().checked_add(next.size()) {
None => return None,
Some(new_size) => new_size,
};
let layout = match Layout::from_size_align(new_size, self.align) {
None => return None,
Some(l) => l,
};
let new_size = self.size().checked_add(next.size())?;
let layout = Layout::from_size_align(new_size, self.align)?;
Some((layout, self.size()))
}

View File

@ -12,7 +12,7 @@
use std::iter::Iterator;
use std::vec::Vec;
use std::collections::BTreeMap;
use std::__rand::{Rng, thread_rng};
use rand::{Rng, thread_rng};
use test::{Bencher, black_box};
macro_rules! map_insert_rand_bench {

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::__rand::{thread_rng};
use rand::{thread_rng};
use std::mem;
use std::ptr;

View File

@ -1067,21 +1067,15 @@ impl<'a, T: Ord> Iterator for Intersection<'a, T> {
fn next(&mut self) -> Option<&'a T> {
loop {
let o_cmp = match (self.a.peek(), self.b.peek()) {
(None, _) => None,
(_, None) => None,
(Some(a1), Some(b1)) => Some(a1.cmp(b1)),
};
match o_cmp {
None => return None,
Some(Less) => {
match Ord::cmp(self.a.peek()?, self.b.peek()?) {
Less => {
self.a.next();
}
Some(Equal) => {
Equal => {
self.b.next();
return self.a.next();
}
Some(Greater) => {
Greater => {
self.b.next();
}
}

View File

@ -1071,6 +1071,15 @@ impl<'a, T, F> Iterator for DrainFilter<'a, T, F>
}
}
#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
impl<'a, T, F> Drop for DrainFilter<'a, T, F>
where F: FnMut(&mut T) -> bool,
{
fn drop(&mut self) {
for _ in self { }
}
}
#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")]
impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for DrainFilter<'a, T, F>
where F: FnMut(&mut T) -> bool

View File

@ -1044,10 +1044,7 @@ impl String {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn pop(&mut self) -> Option<char> {
let ch = match self.chars().rev().next() {
Some(ch) => ch,
None => return None,
};
let ch = self.chars().rev().next()?;
let newlen = self.len() - ch.len_utf8();
unsafe {
self.vec.set_len(newlen);

View File

@ -1423,10 +1423,7 @@ impl<T: PartialEq> Vec<T> {
/// ```
#[unstable(feature = "vec_remove_item", reason = "recently added", issue = "40062")]
pub fn remove_item(&mut self, item: &T) -> Option<T> {
let pos = match self.iter().position(|x| *x == *item) {
Some(x) => x,
None => return None,
};
let pos = self.iter().position(|x| *x == *item)?;
Some(self.remove(pos))
}
}

View File

@ -1084,9 +1084,11 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
where F: FnOnce(&mut T) -> &mut U
{
// FIXME(nll-rfc#40): fix borrow-check
let RefMut { value, borrow } = orig;
RefMut {
value: f(orig.value),
borrow: orig.borrow,
value: f(value),
borrow: borrow,
}
}
}

View File

@ -1776,12 +1776,18 @@ impl<I: Iterator> Iterator for Peekable<I> {
#[inline]
fn nth(&mut self, n: usize) -> Option<I::Item> {
match self.peeked.take() {
// the .take() below is just to avoid "move into pattern guard"
Some(ref mut v) if n == 0 => v.take(),
Some(None) => None,
Some(Some(_)) => self.iter.nth(n - 1),
None => self.iter.nth(n),
// FIXME(#6393): merge these when borrow-checking gets better.
if n == 0 {
match self.peeked.take() {
Some(v) => v,
None => self.iter.nth(n),
}
} else {
match self.peeked.take() {
Some(None) => None,
Some(Some(_)) => self.iter.nth(n - 1),
None => self.iter.nth(n),
}
}
}

View File

@ -596,9 +596,9 @@ mod builtin {
/// Unconditionally causes compilation to fail with the given error message when encountered.
///
/// For more information, see the [RFC].
/// For more information, see the documentation for [`std::compile_error!`].
///
/// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/1695-add-error-macro.md
/// [`std::compile_error!`]: ../std/macro.compile_error.html
#[stable(feature = "compile_error_macro", since = "1.20.0")]
#[macro_export]
#[cfg(dox)]
@ -736,7 +736,7 @@ mod builtin {
#[cfg(dox)]
macro_rules! module_path { () => ({ /* compiler built-in */ }) }
/// Boolean evaluation of configuration flags.
/// Boolean evaluation of configuration flags, at compile-time.
///
/// For more information, see the documentation for [`std::cfg!`].
///

View File

@ -338,6 +338,12 @@ impl<T> Option<T> {
/// Returns the contained value or a default.
///
/// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
/// the result of a function call, it is recommended to use [`unwrap_or_else`],
/// which is lazily evaluated.
///
/// [`unwrap_or_else`]: #method.unwrap_or_else
///
/// # Examples
///
/// ```
@ -451,11 +457,16 @@ impl<T> Option<T> {
/// Transforms the `Option<T>` into a [`Result<T, E>`], mapping [`Some(v)`] to
/// [`Ok(v)`] and [`None`] to [`Err(err)`].
///
/// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the
/// result of a function call, it is recommended to use [`ok_or_else`], which is
/// lazily evaluated.
///
/// [`Result<T, E>`]: ../../std/result/enum.Result.html
/// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok
/// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err
/// [`None`]: #variant.None
/// [`Some(v)`]: #variant.Some
/// [`ok_or_else`]: #method.ok_or_else
///
/// # Examples
///
@ -644,6 +655,12 @@ impl<T> Option<T> {
/// Returns the option if it contains a value, otherwise returns `optb`.
///
/// Arguments passed to `or` are eagerly evaluated; if you are passing the
/// result of a function call, it is recommended to use [`or_else`], which is
/// lazily evaluated.
///
/// [`or_else`]: #method.or_else
///
/// # Examples
///
/// ```

View File

@ -91,8 +91,12 @@ pub const fn null<T>() -> *const T { 0 as *const T }
pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
/// Swaps the values at two mutable locations of the same type, without
/// deinitializing either. They may overlap, unlike `mem::swap` which is
/// otherwise equivalent.
/// deinitializing either.
///
/// The values pointed at by `x` and `y` may overlap, unlike `mem::swap` which
/// is otherwise equivalent. If the values do overlap, then the overlapping
/// region of memory from `x` will be used. This is demonstrated in the
/// examples section below.
///
/// # Safety
///
@ -100,6 +104,40 @@ pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
/// as arguments.
///
/// Ensure that these pointers are valid before calling `swap`.
///
/// # Examples
///
/// Swapping two non-overlapping regions:
///
/// ```
/// use std::ptr;
///
/// let mut array = [0, 1, 2, 3];
///
/// let x = array[0..].as_mut_ptr() as *mut [u32; 2];
/// let y = array[2..].as_mut_ptr() as *mut [u32; 2];
///
/// unsafe {
/// ptr::swap(x, y);
/// assert_eq!([2, 3, 0, 1], array);
/// }
/// ```
///
/// Swapping two overlapping regions:
///
/// ```
/// use std::ptr;
///
/// let mut array = [0, 1, 2, 3];
///
/// let x = array[0..].as_mut_ptr() as *mut [u32; 3];
/// let y = array[1..].as_mut_ptr() as *mut [u32; 3];
///
/// unsafe {
/// ptr::swap(x, y);
/// assert_eq!([1, 0, 1, 2], array);
/// }
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn swap<T>(x: *mut T, y: *mut T) {

View File

@ -153,12 +153,12 @@
//! }
//! ```
//!
//! # The `?` syntax
//! # The question mark operator, `?`
//!
//! When writing code that calls many functions that return the
//! [`Result`] type, the error handling can be tedious. The [`?`]
//! syntax hides some of the boilerplate of propagating errors up the
//! call stack.
//! [`Result`] type, the error handling can be tedious. The question mark
//! operator, [`?`], hides some of the boilerplate of propagating errors
//! up the call stack.
//!
//! It replaces this:
//!
@ -625,8 +625,13 @@ impl<T, E> Result<T, E> {
/// Returns `res` if the result is [`Err`], otherwise returns the [`Ok`] value of `self`.
///
/// Arguments passed to `or` are eagerly evaluated; if you are passing the
/// result of a function call, it is recommended to use [`or_else`], which is
/// lazily evaluated.
///
/// [`Ok`]: enum.Result.html#variant.Ok
/// [`Err`]: enum.Result.html#variant.Err
/// [`or_else`]: #method.or_else
///
/// # Examples
///
@ -690,8 +695,13 @@ impl<T, E> Result<T, E> {
/// Unwraps a result, yielding the content of an [`Ok`].
/// Else, it returns `optb`.
///
/// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing
/// the result of a function call, it is recommended to use [`unwrap_or_else`],
/// which is lazily evaluated.
///
/// [`Ok`]: enum.Result.html#variant.Ok
/// [`Err`]: enum.Result.html#variant.Err
/// [`unwrap_or_else`]: #method.unwrap_or_else
///
/// # Examples
///

View File

@ -494,11 +494,10 @@ fn unwrap_or_0(opt: Option<&u8>) -> u8 {
#[inline]
pub fn next_code_point<'a, I: Iterator<Item = &'a u8>>(bytes: &mut I) -> Option<u32> {
// Decode UTF-8
let x = match bytes.next() {
None => return None,
Some(&next_byte) if next_byte < 128 => return Some(next_byte as u32),
Some(&next_byte) => next_byte,
};
let x = *bytes.next()?;
if x < 128 {
return Some(x as u32)
}
// Multibyte case follows
// Decode from a byte combination out of: [[[x y] z] w]

View File

@ -1641,14 +1641,14 @@ impl Foo {
These attributes do not work on typedefs, since typedefs are just aliases.
Representations like `#[repr(u8)]`, `#[repr(i64)]` are for selecting the
discriminant size for C-like enums (when there is no associated data, e.g.
`enum Color {Red, Blue, Green}`), effectively setting the size of the enum to
discriminant size for enums with no data fields on any of the variants, e.g.
`enum Color {Red, Blue, Green}`, effectively setting the size of the enum to
the size of the provided type. Such an enum can be cast to a value of the same
type as well. In short, `#[repr(u8)]` makes the enum behave like an integer
with a constrained set of allowed values.
Only C-like enums can be cast to numerical primitives, so this attribute will
not apply to structs.
Only field-less enums can be cast to numerical primitives, so this attribute
will not apply to structs.
`#[repr(packed)]` reduces padding to make the struct size smaller. The
representation of enums isn't strictly defined in Rust, and this attribute

View File

@ -978,9 +978,8 @@ impl<'a, 'hir> NodesMatchingSuffix<'a, 'hir> {
// chain, then returns `None`.
fn find_first_mod_parent<'a>(map: &'a Map, mut id: NodeId) -> Option<(NodeId, Name)> {
loop {
match map.find(id) {
None => return None,
Some(NodeItem(item)) if item_is_mod(&item) =>
match map.find(id)? {
NodeItem(item) if item_is_mod(&item) =>
return Some((id, item.name)),
_ => {}
}

View File

@ -14,7 +14,6 @@ use hir::map::DefPathHash;
use hir::map::definitions::Definitions;
use ich::{self, CachingCodemapView};
use middle::cstore::CrateStore;
use session::config::DebugInfoLevel::NoDebugInfo;
use ty::{TyCtxt, fast_reject};
use session::Session;
@ -24,7 +23,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
use syntax::ast;
use syntax::attr;
use syntax::codemap::CodeMap;
use syntax::ext::hygiene::SyntaxContext;
use syntax::symbol::Symbol;
@ -51,7 +50,6 @@ pub struct StableHashingContext<'gcx> {
body_resolver: BodyResolver<'gcx>,
hash_spans: bool,
hash_bodies: bool,
overflow_checks_enabled: bool,
node_id_hashing_mode: NodeIdHashingMode,
// Very often, we are hashing something that does not need the
@ -89,8 +87,7 @@ impl<'gcx> StableHashingContext<'gcx> {
definitions: &'gcx Definitions,
cstore: &'gcx CrateStore)
-> Self {
let hash_spans_initial = sess.opts.debuginfo != NoDebugInfo;
let check_overflow_initial = sess.overflow_checks();
let hash_spans_initial = !sess.opts.debugging_opts.incremental_ignore_spans;
debug_assert!(ich::IGNORED_ATTRIBUTES.len() > 0);
IGNORED_ATTR_NAMES.with(|names| {
@ -110,7 +107,6 @@ impl<'gcx> StableHashingContext<'gcx> {
raw_codemap: sess.codemap(),
hash_spans: hash_spans_initial,
hash_bodies: true,
overflow_checks_enabled: check_overflow_initial,
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
}
}
@ -120,11 +116,6 @@ impl<'gcx> StableHashingContext<'gcx> {
self.sess
}
pub fn force_span_hashing(mut self) -> Self {
self.hash_spans = true;
self
}
#[inline]
pub fn while_hashing_hir_bodies<F: FnOnce(&mut Self)>(&mut self,
hash_bodies: bool,
@ -174,11 +165,6 @@ impl<'gcx> StableHashingContext<'gcx> {
self.definitions.node_to_hir_id(node_id)
}
#[inline]
pub fn hash_spans(&self) -> bool {
self.hash_spans
}
#[inline]
pub fn hash_bodies(&self) -> bool {
self.hash_bodies
@ -204,58 +190,13 @@ impl<'gcx> StableHashingContext<'gcx> {
})
}
pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self,
item_attrs: &[ast::Attribute],
is_const: bool,
f: F) {
let prev_overflow_checks = self.overflow_checks_enabled;
if is_const || attr::contains_name(item_attrs, "rustc_inherit_overflow_checks") {
self.overflow_checks_enabled = true;
}
pub fn hash_hir_item_like<F: FnOnce(&mut Self)>(&mut self, f: F) {
let prev_hash_node_ids = self.node_id_hashing_mode;
self.node_id_hashing_mode = NodeIdHashingMode::Ignore;
f(self);
self.node_id_hashing_mode = prev_hash_node_ids;
self.overflow_checks_enabled = prev_overflow_checks;
}
#[inline]
pub fn binop_can_panic_at_runtime(&self, binop: hir::BinOp_) -> bool
{
match binop {
hir::BiAdd |
hir::BiSub |
hir::BiShl |
hir::BiShr |
hir::BiMul => self.overflow_checks_enabled,
hir::BiDiv |
hir::BiRem => true,
hir::BiAnd |
hir::BiOr |
hir::BiBitXor |
hir::BiBitAnd |
hir::BiBitOr |
hir::BiEq |
hir::BiLt |
hir::BiLe |
hir::BiNe |
hir::BiGe |
hir::BiGt => false
}
}
#[inline]
pub fn unop_can_panic_at_runtime(&self, unop: hir::UnOp) -> bool
{
match unop {
hir::UnDeref |
hir::UnNot => false,
hir::UnNeg => self.overflow_checks_enabled,
}
}
}

View File

@ -529,63 +529,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Expr {
ref attrs
} = *self;
let spans_always_on = match *node {
hir::ExprBox(..) |
hir::ExprArray(..) |
hir::ExprCall(..) |
hir::ExprLit(..) |
hir::ExprCast(..) |
hir::ExprType(..) |
hir::ExprIf(..) |
hir::ExprWhile(..) |
hir::ExprLoop(..) |
hir::ExprMatch(..) |
hir::ExprClosure(..) |
hir::ExprBlock(..) |
hir::ExprAssign(..) |
hir::ExprTupField(..) |
hir::ExprAddrOf(..) |
hir::ExprBreak(..) |
hir::ExprAgain(..) |
hir::ExprRet(..) |
hir::ExprYield(..) |
hir::ExprInlineAsm(..) |
hir::ExprRepeat(..) |
hir::ExprTup(..) |
hir::ExprMethodCall(..) |
hir::ExprPath(..) |
hir::ExprStruct(..) |
hir::ExprField(..) => {
// For these we only hash the span when debuginfo is on.
false
}
// For the following, spans might be significant because of
// panic messages indicating the source location.
hir::ExprBinary(op, ..) => {
hcx.binop_can_panic_at_runtime(op.node)
}
hir::ExprUnary(op, _) => {
hcx.unop_can_panic_at_runtime(op)
}
hir::ExprAssignOp(op, ..) => {
hcx.binop_can_panic_at_runtime(op.node)
}
hir::ExprIndex(..) => {
true
}
};
if spans_always_on {
hcx.while_hashing_spans(true, |hcx| {
span.hash_stable(hcx, hasher);
node.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
});
} else {
span.hash_stable(hcx, hasher);
node.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
}
span.hash_stable(hcx, hasher);
node.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
})
}
}
@ -712,15 +658,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::TraitItem {
span
} = *self;
let is_const = match *node {
hir::TraitItemKind::Const(..) |
hir::TraitItemKind::Type(..) => true,
hir::TraitItemKind::Method(hir::MethodSig { constness, .. }, _) => {
constness == hir::Constness::Const
}
};
hcx.hash_hir_item_like(attrs, is_const, |hcx| {
hcx.hash_hir_item_like(|hcx| {
name.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
generics.hash_stable(hcx, hasher);
@ -757,15 +695,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::ImplItem {
span
} = *self;
let is_const = match *node {
hir::ImplItemKind::Const(..) |
hir::ImplItemKind::Type(..) => true,
hir::ImplItemKind::Method(hir::MethodSig { constness, .. }, _) => {
constness == hir::Constness::Const
}
};
hcx.hash_hir_item_like(attrs, is_const, |hcx| {
hcx.hash_hir_item_like(|hcx| {
name.hash_stable(hcx, hasher);
vis.hash_stable(hcx, hasher);
defaultness.hash_stable(hcx, hasher);
@ -884,30 +814,6 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Item {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let is_const = match self.node {
hir::ItemStatic(..) |
hir::ItemConst(..) => {
true
}
hir::ItemFn(_, _, constness, ..) => {
constness == hir::Constness::Const
}
hir::ItemUse(..) |
hir::ItemExternCrate(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemMod(..) |
hir::ItemAutoImpl(..) |
hir::ItemTrait(..) |
hir::ItemImpl(..) |
hir::ItemTy(..) |
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
false
}
};
let hir::Item {
name,
ref attrs,
@ -918,7 +824,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::Item {
span
} = *self;
hcx.hash_hir_item_like(attrs, is_const, |hcx| {
hcx.hash_hir_item_like(|hcx| {
name.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
node.hash_stable(hcx, hasher);

View File

@ -55,48 +55,11 @@ for mir::UnsafetyViolationKind {
}
}
}
impl<'gcx> HashStable<StableHashingContext<'gcx>>
for mir::Terminator<'gcx> {
#[inline]
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let mir::Terminator {
ref kind,
ref source_info,
} = *self;
let hash_spans_unconditionally = match *kind {
mir::TerminatorKind::Assert { .. } => {
// Assert terminators generate a panic message that contains the
// source location, so we always have to feed its span into the
// ICH.
true
}
mir::TerminatorKind::Goto { .. } |
mir::TerminatorKind::SwitchInt { .. } |
mir::TerminatorKind::Resume |
mir::TerminatorKind::Return |
mir::TerminatorKind::GeneratorDrop |
mir::TerminatorKind::Unreachable |
mir::TerminatorKind::Drop { .. } |
mir::TerminatorKind::DropAndReplace { .. } |
mir::TerminatorKind::Yield { .. } |
mir::TerminatorKind::Call { .. } |
mir::TerminatorKind::FalseEdges { .. } => false,
};
if hash_spans_unconditionally {
hcx.while_hashing_spans(true, |hcx| {
source_info.hash_stable(hcx, hasher);
})
} else {
source_info.hash_stable(hcx, hasher);
}
kind.hash_stable(hcx, hasher);
}
}
impl_stable_hash_for!(struct mir::Terminator<'tcx> {
kind,
source_info
});
impl<'gcx, T> HashStable<StableHashingContext<'gcx>> for mir::ClearCrossCrate<T>
where T: HashStable<StableHashingContext<'gcx>>
@ -572,3 +535,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
}
impl_stable_hash_for!(struct mir::Location { block, statement_index });
impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
num_external_vids,
outlives_requirements
});
impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
free_region,
outlived_free_region,
blame_span
});

View File

@ -84,6 +84,16 @@ for ty::RegionKind {
}
}
impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid {
#[inline]
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
use rustc_data_structures::indexed_vec::Idx;
self.index().hash_stable(hcx, hasher);
}
}
impl<'gcx> HashStable<StableHashingContext<'gcx>>
for ty::adjustment::AutoBorrow<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,

View File

@ -141,7 +141,7 @@ will make the permanent. Snapshots can be nested as long as you follow
a stack-like discipline.
Rather than use snapshots directly, it is often helpful to use the
methods like `commit_if_ok` or `probe` that encapsulte higher-level
methods like `commit_if_ok` or `probe` that encapsulate higher-level
patterns.
## Subtyping obligations

View File

@ -91,10 +91,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.iter()
.enumerate()
.filter_map(|(index, arg)| {
let ty = match tables.borrow().node_id_to_type_opt(arg.hir_id) {
Some(v) => v,
None => return None, // sometimes the tables are not yet populated
};
// May return None; sometimes the tables are not yet populated.
let ty = tables.borrow().node_id_to_type_opt(arg.hir_id)?;
let mut found_anon_region = false;
let new_arg_ty = self.tcx
.fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {

View File

@ -1062,6 +1062,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
}
/// Number of region variables created so far.
pub fn num_region_vars(&self) -> usize {
self.borrow_region_constraints().var_origins().len()
}
/// Just a convenient wrapper of `next_region_var` for using during NLL.
pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
-> ty::Region<'tcx> {
@ -1475,38 +1480,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
closure_kind_ty.to_opt_closure_kind()
}
/// Obtain the signature of a function or closure.
/// For closures, unlike `tcx.fn_sig(def_id)`, this method will
/// work during the type-checking of the enclosing function and
/// return the closure signature in its partially inferred state.
pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
// Do we have an in-progress set of tables we are inferring?
if let Some(tables) = self.in_progress_tables {
// Is this a local item?
if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
// Is it a local *closure*?
if self.tcx.is_closure(def_id) {
let hir_id = self.tcx.hir.node_to_hir_id(id);
// Is this local closure contained within the tables we are inferring?
if tables.borrow().local_id_root == Some(DefId::local(hir_id.owner)) {
// if so, extract signature from there.
let closure_ty = tables.borrow().node_id_to_type(hir_id);
let (closure_def_id, closure_substs) = match closure_ty.sty {
ty::TyClosure(closure_def_id, closure_substs) =>
(closure_def_id, closure_substs),
_ =>
bug!("closure with non-closure type: {:?}", closure_ty),
};
assert_eq!(def_id, closure_def_id);
let closure_sig_ty = closure_substs.closure_sig_ty(def_id, self.tcx);
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
return closure_sig_ty.fn_sig(self.tcx);
}
}
}
}
self.tcx.fn_sig(def_id)
/// Obtain the signature of a closure. For closures, unlike
/// `tcx.fn_sig(def_id)`, this method will work during the
/// type-checking of the enclosing function and return the closure
/// signature in its partially inferred state.
pub fn closure_sig(
&self,
def_id: DefId,
substs: ty::ClosureSubsts<'tcx>
) -> ty::PolyFnSig<'tcx> {
let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
closure_sig_ty.fn_sig(self.tcx)
}
/// Normalizes associated types in `value`, potentially returning

View File

@ -204,6 +204,12 @@ declare_lint! {
"detects generic lifetime arguments in path segments with late bound lifetime parameters"
}
declare_lint! {
pub INCOHERENT_FUNDAMENTAL_IMPLS,
Warn,
"potentially-conflicting impls were erroneously allowed"
}
declare_lint! {
pub DEPRECATED,
Warn,
@ -267,6 +273,7 @@ impl LintPass for HardwiredLints {
MISSING_FRAGMENT_SPECIFIER,
PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
LATE_BOUND_LIFETIME_ARGUMENTS,
INCOHERENT_FUNDAMENTAL_IMPLS,
DEPRECATED,
UNUSED_UNSAFE,
UNUSED_MUT,

View File

@ -37,7 +37,7 @@ use errors::{DiagnosticBuilder, DiagnosticId};
use hir::def_id::{CrateNum, LOCAL_CRATE};
use hir::intravisit::{self, FnKind};
use hir;
use session::Session;
use session::{Session, DiagnosticMessageId};
use std::hash;
use syntax::ast;
use syntax::codemap::MultiSpan;
@ -423,7 +423,7 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
LintSource::Default => {
sess.diag_note_once(
&mut err,
lint,
DiagnosticMessageId::from(lint),
&format!("#[{}({})] on by default", level.as_str(), name));
}
LintSource::CommandLine(lint_flag_val) => {
@ -437,24 +437,25 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
if lint_flag_val.as_str() == name {
sess.diag_note_once(
&mut err,
lint,
DiagnosticMessageId::from(lint),
&format!("requested on the command line with `{} {}`",
flag, hyphen_case_lint_name));
} else {
let hyphen_case_flag_val = lint_flag_val.as_str().replace("_", "-");
sess.diag_note_once(
&mut err,
lint,
DiagnosticMessageId::from(lint),
&format!("`{} {}` implied by `{} {}`",
flag, hyphen_case_lint_name, flag,
hyphen_case_flag_val));
}
}
LintSource::Node(lint_attr_name, src) => {
sess.diag_span_note_once(&mut err, lint, src, "lint level defined here");
sess.diag_span_note_once(&mut err, DiagnosticMessageId::from(lint),
src, "lint level defined here");
if lint_attr_name.as_str() != name {
let level_str = level.as_str();
sess.diag_note_once(&mut err, lint,
sess.diag_note_once(&mut err, DiagnosticMessageId::from(lint),
&format!("#[{}({})] implied by #[{}({})]",
level_str, name, level_str, lint_attr_name));
}

View File

@ -1790,6 +1790,75 @@ pub struct GeneratorLayout<'tcx> {
pub fields: Vec<LocalDecl<'tcx>>,
}
/// After we borrow check a closure, we are left with various
/// requirements that we have inferred between the free regions that
/// appear in the closure's signature or on its field types. These
/// requirements are then verified and proved by the closure's
/// creating function. This struct encodes those requirements.
///
/// The requirements are listed as being between various
/// `RegionVid`. The 0th region refers to `'static`; subsequent region
/// vids refer to the free regions that appear in the closure (or
/// generator's) type, in order of appearance. (This numbering is
/// actually defined by the `UniversalRegions` struct in the NLL
/// region checker. See for example
/// `UniversalRegions::closure_mapping`.) Note that we treat the free
/// regions in the closure's type "as if" they were erased, so their
/// precise identity is not important, only their position.
///
/// Example: If type check produces a closure with the closure substs:
///
/// ```
/// ClosureSubsts = [
/// i8, // the "closure kind"
/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
/// &'a String, // some upvar
/// ]
/// ```
///
/// here, there is one unique free region (`'a`) but it appears
/// twice. We would "renumber" each occurence to a unique vid, as follows:
///
/// ```
/// ClosureSubsts = [
/// i8, // the "closure kind"
/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
/// &'2 String, // some upvar
/// ]
/// ```
///
/// Now the code might impose a requirement like `'1: '2`. When an
/// instance of the closure is created, the corresponding free regions
/// can be extracted from its type and constrained to have the given
/// outlives relationship.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ClosureRegionRequirements {
/// The number of external regions defined on the closure. In our
/// example above, it would be 3 -- one for `'static`, then `'1`
/// and `'2`. This is just used for a sanity check later on, to
/// make sure that the number of regions we see at the callsite
/// matches.
pub num_external_vids: usize,
/// Requirements between the various free regions defined in
/// indices.
pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
}
/// Indicates an outlives constraint between two free-regions declared
/// on the closure.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
pub struct ClosureOutlivesRequirement {
// This region ...
pub free_region: ty::RegionVid,
// .. must outlive this one.
pub outlived_free_region: ty::RegionVid,
// If not, report an error here.
pub blame_span: Span,
}
/*
* TypeFoldable implementations for MIR types
*/

View File

@ -1084,6 +1084,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"dump hash information in textual format to stdout"),
incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED],
"verify incr. comp. hashes of green query instances"),
incremental_ignore_spans: bool = (false, parse_bool, [UNTRACKED],
"ignore spans during ICH computation -- used for testing"),
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],

View File

@ -161,6 +161,7 @@ pub struct PerfStats {
enum DiagnosticBuilderMethod {
Note,
SpanNote,
SpanSuggestion(String), // suggestion
// add more variants as needed to support one-time diagnostics
}
@ -173,6 +174,12 @@ pub enum DiagnosticMessageId {
StabilityId(u32) // issue number
}
impl From<&'static lint::Lint> for DiagnosticMessageId {
fn from(lint: &'static lint::Lint) -> Self {
DiagnosticMessageId::LintId(lint::LintId::of(lint))
}
}
impl Session {
pub fn local_crate_disambiguator(&self) -> CrateDisambiguator {
match *self.crate_disambiguator.borrow() {
@ -358,10 +365,11 @@ impl Session {
fn diag_once<'a, 'b>(&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
method: DiagnosticBuilderMethod,
lint: &'static lint::Lint, message: &str, span: Option<Span>) {
msg_id: DiagnosticMessageId,
message: &str,
span_maybe: Option<Span>) {
let lint_id = DiagnosticMessageId::LintId(lint::LintId::of(lint));
let id_span_message = (lint_id, span, message.to_owned());
let id_span_message = (msg_id, span_maybe, message.to_owned());
let fresh = self.one_time_diagnostics.borrow_mut().insert(id_span_message);
if fresh {
match method {
@ -369,7 +377,12 @@ impl Session {
diag_builder.note(message);
},
DiagnosticBuilderMethod::SpanNote => {
diag_builder.span_note(span.expect("span_note expects a span"), message);
let span = span_maybe.expect("span_note needs a span");
diag_builder.span_note(span, message);
},
DiagnosticBuilderMethod::SpanSuggestion(suggestion) => {
let span = span_maybe.expect("span_suggestion needs a span");
diag_builder.span_suggestion(span, message, suggestion);
}
}
}
@ -377,14 +390,25 @@ impl Session {
pub fn diag_span_note_once<'a, 'b>(&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
lint: &'static lint::Lint, span: Span, message: &str) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanNote, lint, message, Some(span));
msg_id: DiagnosticMessageId, span: Span, message: &str) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanNote,
msg_id, message, Some(span));
}
pub fn diag_note_once<'a, 'b>(&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
lint: &'static lint::Lint, message: &str) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, lint, message, None);
msg_id: DiagnosticMessageId, message: &str) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None);
}
pub fn diag_span_suggestion_once<'a, 'b>(&'a self,
diag_builder: &'b mut DiagnosticBuilder<'a>,
msg_id: DiagnosticMessageId,
span: Span,
message: &str,
suggestion: String) {
self.diag_once(diag_builder, DiagnosticBuilderMethod::SpanSuggestion(suggestion),
msg_id, message, Some(span));
}
pub fn codemap<'a>(&'a self) -> &'a codemap::CodeMap {

View File

@ -13,14 +13,27 @@
use hir::def_id::{DefId, LOCAL_CRATE};
use syntax_pos::DUMMY_SP;
use traits::{self, Normalized, SelectionContext, Obligation, ObligationCause, Reveal};
use traits::IntercrateMode;
use traits::select::IntercrateAmbiguityCause;
use ty::{self, Ty, TyCtxt};
use ty::fold::TypeFoldable;
use ty::subst::Subst;
use infer::{InferCtxt, InferOk};
#[derive(Copy, Clone)]
struct InferIsLocal(bool);
/// Whether we do the orphan check relative to this crate or
/// to some remote crate.
#[derive(Copy, Clone, Debug)]
enum InCrate {
Local,
Remote
}
#[derive(Debug, Copy, Clone)]
pub enum Conflict {
Upstream,
Downstream { used_to_be_broken: bool }
}
pub struct OverlapResult<'tcx> {
pub impl_header: ty::ImplHeader<'tcx>,
@ -31,16 +44,19 @@ pub struct OverlapResult<'tcx> {
/// `ImplHeader` with those types substituted
pub fn overlapping_impls<'cx, 'gcx, 'tcx>(infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId)
impl2_def_id: DefId,
intercrate_mode: IntercrateMode)
-> Option<OverlapResult<'tcx>>
{
debug!("impl_can_satisfy(\
impl1_def_id={:?}, \
impl2_def_id={:?})",
impl2_def_id={:?},
intercrate_mode={:?})",
impl1_def_id,
impl2_def_id);
impl2_def_id,
intercrate_mode);
let selcx = &mut SelectionContext::intercrate(infcx);
let selcx = &mut SelectionContext::intercrate(infcx, intercrate_mode);
overlap(selcx, impl1_def_id, impl2_def_id)
}
@ -126,32 +142,49 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
}
pub fn trait_ref_is_knowable<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: ty::TraitRef<'tcx>) -> bool
trait_ref: ty::TraitRef<'tcx>)
-> Option<Conflict>
{
debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
// A downstream or cousin crate is allowed to implement some
// substitution of this trait-ref.
// if the orphan rules pass, that means that no ancestor crate can
// impl this, so it's up to us.
if orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(false)).is_ok() {
// A trait can be implementable for a trait ref by both the current
// crate and crates downstream of it. Older versions of rustc
// were not aware of this, causing incoherence (issue #43355).
let used_to_be_broken =
orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok();
if used_to_be_broken {
debug!("trait_ref_is_knowable({:?}) - USED TO BE BROKEN", trait_ref);
}
return Some(Conflict::Downstream { used_to_be_broken });
}
if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
// This is a local or fundamental trait, so future-compatibility
// is no concern. We know that downstream/cousin crates are not
// allowed to implement a substitution of this trait ref, which
// means impls could only come from dependencies of this crate,
// which we already know about.
return None;
}
// This is a remote non-fundamental trait, so if another crate
// can be the "final owner" of a substitution of this trait-ref,
// they are allowed to implement it future-compatibly.
//
// However, if we are a final owner, then nobody else can be,
// and if we are an intermediate owner, then we don't care
// about future-compatibility, which means that we're OK if
// we are an owner.
if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() {
debug!("trait_ref_is_knowable: orphan check passed");
return true;
return None;
} else {
debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned");
return Some(Conflict::Upstream);
}
// if the trait is not marked fundamental, then it's always possible that
// an ancestor crate will impl this in the future, if they haven't
// already
if !trait_ref_is_local_or_fundamental(tcx, trait_ref) {
debug!("trait_ref_is_knowable: trait is neither local nor fundamental");
return false;
}
// find out when some downstream (or cousin) crate could impl this
// trait-ref, presuming that all the parameters were instantiated
// with downstream types. If not, then it could only be
// implemented by an upstream crate, which means that the impl
// must be visible to us, and -- since the trait is fundamental
// -- we can test.
orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(true)).is_err()
}
pub fn trait_ref_is_local_or_fundamental<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
@ -189,30 +222,123 @@ pub fn orphan_check<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
return Ok(());
}
orphan_check_trait_ref(tcx, trait_ref, InferIsLocal(false))
orphan_check_trait_ref(tcx, trait_ref, InCrate::Local)
}
/// Check whether a trait-ref is potentially implementable by a crate.
///
/// The current rule is that a trait-ref orphan checks in a crate C:
///
/// 1. Order the parameters in the trait-ref in subst order - Self first,
/// others linearly (e.g. `<U as Foo<V, W>>` is U < V < W).
/// 2. Of these type parameters, there is at least one type parameter
/// in which, walking the type as a tree, you can reach a type local
/// to C where all types in-between are fundamental types. Call the
/// first such parameter the "local key parameter".
/// - e.g. `Box<LocalType>` is OK, because you can visit LocalType
/// going through `Box`, which is fundamental.
/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for
/// the same reason.
/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's
/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between
/// the local type and the type parameter.
/// 3. Every type parameter before the local key parameter is fully known in C.
/// - e.g. `impl<T> T: Trait<LocalType>` is bad, because `T` might be
/// an unknown type.
/// - but `impl<T> LocalType: Trait<T>` is OK, because `LocalType`
/// occurs before `T`.
/// 4. Every type in the local key parameter not known in C, going
/// through the parameter's type tree, must appear only as a subtree of
/// a type local to C, with only fundamental types between the type
/// local to C and the local key parameter.
/// - e.g. `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`)
/// is bad, because the only local type with `T` as a subtree is
/// `LocalType<T>`, and `Vec<->` is between it and the type parameter.
/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because
/// the second occurence of `T` is not a subtree of *any* local type.
/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of
/// `LocalType<Vec<T>>`, which is local and has no types between it and
/// the type parameter.
///
/// The orphan rules actually serve several different purposes:
///
/// 1. They enable link-safety - i.e. 2 mutually-unknowing crates (where
/// every type local to one crate is unknown in the other) can't implement
/// the same trait-ref. This follows because it can be seen that no such
/// type can orphan-check in 2 such crates.
///
/// To check that a local impl follows the orphan rules, we check it in
/// InCrate::Local mode, using type parameters for the "generic" types.
///
/// 2. They ground negative reasoning for coherence. If a user wants to
/// write both a conditional blanket impl and a specific impl, we need to
/// make sure they do not overlap. For example, if we write
/// ```
/// impl<T> IntoIterator for Vec<T>
/// impl<T: Iterator> IntoIterator for T
/// ```
/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0.
/// We can observe that this holds in the current crate, but we need to make
/// sure this will also hold in all unknown crates (both "independent" crates,
/// which we need for link-safety, and also child crates, because we don't want
/// child crates to get error for impl conflicts in a *dependency*).
///
/// For that, we only allow negative reasoning if, for every assignment to the
/// inference variables, every unknown crate would get an orphan error if they
/// try to implement this trait-ref. To check for this, we use InCrate::Remote
/// mode. That is sound because we already know all the impls from known crates.
///
/// 3. For non-#[fundamental] traits, they guarantee that parent crates can
/// add "non-blanket" impls without breaking negative reasoning in dependent
/// crates. This is the "rebalancing coherence" (RFC 1023) restriction.
///
/// For that, we only a allow crate to perform negative reasoning on
/// non-local-non-#[fundamental] only if there's a local key parameter as per (2).
///
/// Because we never perform negative reasoning generically (coherence does
/// not involve type parameters), this can be interpreted as doing the full
/// orphan check (using InCrate::Local mode), substituting non-local known
/// types for all inference variables.
///
/// This allows for crates to future-compatibly add impls as long as they
/// can't apply to types with a key parameter in a child crate - applying
/// the rules, this basically means that every type parameter in the impl
/// must appear behind a non-fundamental type (because this is not a
/// type-system requirement, crate owners might also go for "semantic
/// future-compatibility" involving things such as sealed traits, but
/// the above requirement is sufficient, and is necessary in "open world"
/// cases).
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
trait_ref: ty::TraitRef<'tcx>,
infer_is_local: InferIsLocal)
in_crate: InCrate)
-> Result<(), OrphanCheckErr<'tcx>>
{
debug!("orphan_check_trait_ref(trait_ref={:?}, infer_is_local={})",
trait_ref, infer_is_local.0);
debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})",
trait_ref, in_crate);
if trait_ref.needs_infer() && trait_ref.needs_subst() {
bug!("can't orphan check a trait ref with both params and inference variables {:?}",
trait_ref);
}
// First, create an ordered iterator over all the type parameters to the trait, with the self
// type appearing first.
// Find the first input type that either references a type parameter OR
// some local type.
for input_ty in trait_ref.input_types() {
if ty_is_local(tcx, input_ty, infer_is_local) {
if ty_is_local(tcx, input_ty, in_crate) {
debug!("orphan_check_trait_ref: ty_is_local `{:?}`", input_ty);
// First local input type. Check that there are no
// uncovered type parameters.
let uncovered_tys = uncovered_tys(tcx, input_ty, infer_is_local);
let uncovered_tys = uncovered_tys(tcx, input_ty, in_crate);
for uncovered_ty in uncovered_tys {
if let Some(param) = uncovered_ty.walk().find(|t| is_type_parameter(t)) {
if let Some(param) = uncovered_ty.walk()
.find(|t| is_possibly_remote_type(t, in_crate))
{
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
return Err(OrphanCheckErr::UncoveredTy(param));
}
@ -224,11 +350,11 @@ fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
// Otherwise, enforce invariant that there are no type
// parameters reachable.
if !infer_is_local.0 {
if let Some(param) = input_ty.walk().find(|t| is_type_parameter(t)) {
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
return Err(OrphanCheckErr::UncoveredTy(param));
}
if let Some(param) = input_ty.walk()
.find(|t| is_possibly_remote_type(t, in_crate))
{
debug!("orphan_check_trait_ref: uncovered type `{:?}`", param);
return Err(OrphanCheckErr::UncoveredTy(param));
}
}
@ -237,29 +363,29 @@ fn orphan_check_trait_ref<'tcx>(tcx: TyCtxt,
return Err(OrphanCheckErr::NoLocalInputType);
}
fn uncovered_tys<'tcx>(tcx: TyCtxt, ty: Ty<'tcx>, infer_is_local: InferIsLocal)
fn uncovered_tys<'tcx>(tcx: TyCtxt, ty: Ty<'tcx>, in_crate: InCrate)
-> Vec<Ty<'tcx>> {
if ty_is_local_constructor(ty, infer_is_local) {
if ty_is_local_constructor(ty, in_crate) {
vec![]
} else if fundamental_ty(tcx, ty) {
ty.walk_shallow()
.flat_map(|t| uncovered_tys(tcx, t, infer_is_local))
.flat_map(|t| uncovered_tys(tcx, t, in_crate))
.collect()
} else {
vec![ty]
}
}
fn is_type_parameter(ty: Ty) -> bool {
fn is_possibly_remote_type(ty: Ty, _in_crate: InCrate) -> bool {
match ty.sty {
ty::TyProjection(..) | ty::TyParam(..) => true,
_ => false,
}
}
fn ty_is_local(tcx: TyCtxt, ty: Ty, infer_is_local: InferIsLocal) -> bool {
ty_is_local_constructor(ty, infer_is_local) ||
fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, infer_is_local))
fn ty_is_local(tcx: TyCtxt, ty: Ty, in_crate: InCrate) -> bool {
ty_is_local_constructor(ty, in_crate) ||
fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate))
}
fn fundamental_ty(tcx: TyCtxt, ty: Ty) -> bool {
@ -273,7 +399,16 @@ fn fundamental_ty(tcx: TyCtxt, ty: Ty) -> bool {
}
}
fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
fn def_id_is_local(def_id: DefId, in_crate: InCrate) -> bool {
match in_crate {
// The type is local to *this* crate - it will not be
// local in any other crate.
InCrate::Remote => false,
InCrate::Local => def_id.is_local()
}
}
fn ty_is_local_constructor(ty: Ty, in_crate: InCrate) -> bool {
debug!("ty_is_local_constructor({:?})", ty);
match ty.sty {
@ -296,20 +431,20 @@ fn ty_is_local_constructor(ty: Ty, infer_is_local: InferIsLocal)-> bool {
false
}
ty::TyInfer(..) => {
infer_is_local.0
}
ty::TyInfer(..) => match in_crate {
InCrate::Local => false,
// The inference variable might be unified with a local
// type in that remote crate.
InCrate::Remote => true,
},
ty::TyAdt(def, _) => {
def.did.is_local()
}
ty::TyForeign(did) => {
did.is_local()
}
ty::TyAdt(def, _) => def_id_is_local(def.did, in_crate),
ty::TyForeign(did) => def_id_is_local(did, in_crate),
ty::TyDynamic(ref tt, ..) => {
tt.principal().map_or(false, |p| p.def_id().is_local())
tt.principal().map_or(false, |p| {
def_id_is_local(p.def_id(), in_crate)
})
}
ty::TyError => {

View File

@ -60,6 +60,13 @@ mod structural_impls;
pub mod trans;
mod util;
// Whether to enable bug compatibility with issue #43355
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum IntercrateMode {
Issue43355,
Fixed
}
/// An `Obligation` represents some trait reference (e.g. `int:Eq`) for
/// which the vtable must be found. The process of finding a vtable is
/// called "resolving" the `Obligation`. This process consists of

View File

@ -1339,26 +1339,27 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
vtable: VtableClosureData<'tcx, PredicateObligation<'tcx>>)
-> Progress<'tcx>
{
let closure_typer = selcx.closure_typer();
let closure_type = closure_typer.fn_sig(vtable.closure_def_id)
.subst(selcx.tcx(), vtable.substs.substs);
let tcx = selcx.tcx();
let infcx = selcx.infcx();
let closure_sig_ty = vtable.substs.closure_sig_ty(vtable.closure_def_id, tcx);
let closure_sig = infcx.shallow_resolve(&closure_sig_ty).fn_sig(tcx);
let Normalized {
value: closure_type,
value: closure_sig,
obligations
} = normalize_with_depth(selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth+1,
&closure_type);
&closure_sig);
debug!("confirm_closure_candidate: obligation={:?},closure_type={:?},obligations={:?}",
debug!("confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}",
obligation,
closure_type,
closure_sig,
obligations);
confirm_callable_candidate(selcx,
obligation,
closure_type,
closure_sig,
util::TupleArgumentsFlag::No)
.with_addl_obligations(vtable.nested)
.with_addl_obligations(obligations)

View File

@ -13,8 +13,9 @@
use self::SelectionCandidate::*;
use self::EvaluationResult::*;
use super::coherence;
use super::coherence::{self, Conflict};
use super::DerivedObligationCause;
use super::IntercrateMode;
use super::project;
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
use super::{PredicateObligation, TraitObligation, ObligationCause};
@ -87,7 +88,7 @@ pub struct SelectionContext<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
/// other words, we consider `$0 : Bar` to be unimplemented if
/// there is no type that the user could *actually name* that
/// would satisfy it. This avoids crippling inference, basically.
intercrate: bool,
intercrate: Option<IntercrateMode>,
inferred_obligations: SnapshotVec<InferredObligationsSnapshotVecDelegate<'tcx>>,
@ -111,21 +112,24 @@ impl IntercrateAmbiguityCause {
/// See #23980 for details.
pub fn add_intercrate_ambiguity_hint<'a, 'tcx>(&self,
err: &mut ::errors::DiagnosticBuilder) {
err.note(&self.intercrate_ambiguity_hint());
}
pub fn intercrate_ambiguity_hint(&self) -> String {
match self {
&IntercrateAmbiguityCause::DownstreamCrate { ref trait_desc, ref self_desc } => {
let self_desc = if let &Some(ref ty) = self_desc {
format!(" for type `{}`", ty)
} else { "".to_string() };
err.note(&format!("downstream crates may implement trait `{}`{}",
trait_desc, self_desc));
format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc)
}
&IntercrateAmbiguityCause::UpstreamCrateUpdate { ref trait_desc, ref self_desc } => {
let self_desc = if let &Some(ref ty) = self_desc {
format!(" for type `{}`", ty)
} else { "".to_string() };
err.note(&format!("upstream crates may add new impl of trait `{}`{} \
in future versions",
trait_desc, self_desc));
format!("upstream crates may add new impl of trait `{}`{} \
in future versions",
trait_desc, self_desc)
}
}
}
@ -417,17 +421,19 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
SelectionContext {
infcx,
freshener: infcx.freshener(),
intercrate: false,
intercrate: None,
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: Vec::new(),
}
}
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>) -> SelectionContext<'cx, 'gcx, 'tcx> {
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
mode: IntercrateMode) -> SelectionContext<'cx, 'gcx, 'tcx> {
debug!("intercrate({:?})", mode);
SelectionContext {
infcx,
freshener: infcx.freshener(),
intercrate: true,
intercrate: Some(mode),
inferred_obligations: SnapshotVec::new(),
intercrate_ambiguity_causes: Vec::new(),
}
@ -758,7 +764,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
debug!("evaluate_trait_predicate_recursively({:?})",
obligation);
if !self.intercrate && obligation.is_global() {
if !self.intercrate.is_some() && obligation.is_global() {
// If a param env is consistent, global obligations do not depend on its particular
// value in order to work, so we can clear out the param env and get better
// caching. (If the current param env is inconsistent, we don't care what happens).
@ -814,7 +820,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// terms of `Fn` etc, but we could probably make this more
// precise still.
let unbound_input_types = stack.fresh_trait_ref.input_types().any(|ty| ty.is_fresh());
if unbound_input_types && self.intercrate {
// this check was an imperfect workaround for a bug n the old
// intercrate mode, it should be removed when that goes away.
if unbound_input_types &&
self.intercrate == Some(IntercrateMode::Issue43355)
{
debug!("evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous",
stack.fresh_trait_ref);
// Heuristics: show the diagnostics when there are no candidates in crate.
@ -1077,28 +1087,32 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
return Ok(None);
}
if !self.is_knowable(stack) {
debug!("coherence stage: not knowable");
// Heuristics: show the diagnostics when there are no candidates in crate.
let candidate_set = self.assemble_candidates(stack)?;
if !candidate_set.ambiguous && candidate_set.vec.is_empty() {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
let self_ty = trait_ref.self_ty();
let trait_desc = trait_ref.to_string();
let self_desc = if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
};
let cause = if !coherence::trait_ref_is_local_or_fundamental(self.tcx(),
trait_ref) {
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
} else {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
self.intercrate_ambiguity_causes.push(cause);
match self.is_knowable(stack) {
None => {}
Some(conflict) => {
debug!("coherence stage: not knowable");
// Heuristics: show the diagnostics when there are no candidates in crate.
let candidate_set = self.assemble_candidates(stack)?;
if !candidate_set.ambiguous && candidate_set.vec.iter().all(|c| {
!self.evaluate_candidate(stack, &c).may_apply()
}) {
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
let self_ty = trait_ref.self_ty();
let trait_desc = trait_ref.to_string();
let self_desc = if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
};
let cause = if let Conflict::Upstream = conflict {
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
} else {
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
};
self.intercrate_ambiguity_causes.push(cause);
}
return Ok(None);
}
return Ok(None);
}
let candidate_set = self.assemble_candidates(stack)?;
@ -1205,12 +1219,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
fn is_knowable<'o>(&mut self,
stack: &TraitObligationStack<'o, 'tcx>)
-> bool
-> Option<Conflict>
{
debug!("is_knowable(intercrate={})", self.intercrate);
debug!("is_knowable(intercrate={:?})", self.intercrate);
if !self.intercrate {
return true;
if !self.intercrate.is_some() {
return None;
}
let obligation = &stack.obligation;
@ -1221,7 +1235,14 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// bound regions
let trait_ref = predicate.skip_binder().trait_ref;
coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
let result = coherence::trait_ref_is_knowable(self.tcx(), trait_ref);
if let (Some(Conflict::Downstream { used_to_be_broken: true }),
Some(IntercrateMode::Issue43355)) = (result, self.intercrate) {
debug!("is_knowable: IGNORING conflict to be bug-compatible with #43355");
None
} else {
result
}
}
/// Returns true if the global caches can be used.
@ -1246,7 +1267,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// the master cache. Since coherence executes pretty quickly,
// it's not worth going to more trouble to increase the
// hit-rate I don't think.
if self.intercrate {
if self.intercrate.is_some() {
return false;
}
@ -3162,8 +3183,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
substs: ty::ClosureSubsts<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
let closure_type = self.infcx.fn_sig(closure_def_id)
.subst(self.tcx(), substs.substs);
let closure_type = self.infcx.closure_sig(closure_def_id, substs);
let ty::Binder((trait_ref, _)) =
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
obligation.predicate.0.self_ty(), // (1)

View File

@ -30,6 +30,8 @@ use ty::{self, TyCtxt, TypeFoldable};
use syntax_pos::DUMMY_SP;
use std::rc::Rc;
use lint;
pub mod specialization_graph;
/// Information pertinent to an overlapping impl error.
@ -325,16 +327,33 @@ pub(super) fn specialization_graph_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
// This is where impl overlap checking happens:
let insert_result = sg.insert(tcx, impl_def_id);
// Report error if there was one.
if let Err(overlap) = insert_result {
let mut err = struct_span_err!(tcx.sess,
tcx.span_of_impl(impl_def_id).unwrap(),
E0119,
"conflicting implementations of trait `{}`{}:",
overlap.trait_desc,
overlap.self_desc.clone().map_or(String::new(),
|ty| {
format!(" for type `{}`", ty)
}));
let (overlap, used_to_be_allowed) = match insert_result {
Err(overlap) => (Some(overlap), false),
Ok(opt_overlap) => (opt_overlap, true)
};
if let Some(overlap) = overlap {
let msg = format!("conflicting implementations of trait `{}`{}:{}",
overlap.trait_desc,
overlap.self_desc.clone().map_or(
String::new(), |ty| {
format!(" for type `{}`", ty)
}),
if used_to_be_allowed { " (E0119)" } else { "" }
);
let mut err = if used_to_be_allowed {
tcx.struct_span_lint_node(
lint::builtin::INCOHERENT_FUNDAMENTAL_IMPLS,
tcx.hir.as_local_node_id(impl_def_id).unwrap(),
tcx.span_of_impl(impl_def_id).unwrap(),
&msg)
} else {
struct_span_err!(tcx.sess,
tcx.span_of_impl(impl_def_id).unwrap(),
E0119,
"{}",
msg)
};
match tcx.span_of_impl(overlap.with_impl) {
Ok(span) => {

View File

@ -68,7 +68,7 @@ struct Children {
/// The result of attempting to insert an impl into a group of children.
enum Inserted {
/// The impl was inserted as a new child in this group of children.
BecameNewSibling,
BecameNewSibling(Option<OverlapError>),
/// The impl replaced an existing impl that specializes it.
Replaced(DefId),
@ -105,17 +105,39 @@ impl<'a, 'gcx, 'tcx> Children {
simplified_self: Option<SimplifiedType>)
-> Result<Inserted, OverlapError>
{
let mut last_lint = None;
for slot in match simplified_self {
Some(sty) => self.filtered_mut(sty),
None => self.iter_mut(),
} {
let possible_sibling = *slot;
let overlap_error = |overlap: traits::coherence::OverlapResult| {
// overlap, but no specialization; error out
let trait_ref = overlap.impl_header.trait_ref.unwrap();
let self_ty = trait_ref.self_ty();
OverlapError {
with_impl: possible_sibling,
trait_desc: trait_ref.to_string(),
// only report the Self type if it has at least
// some outer concrete shell; otherwise, it's
// not adding much information.
self_desc: if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
},
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
}
};
let tcx = tcx.global_tcx();
let (le, ge) = tcx.infer_ctxt().enter(|infcx| {
let overlap = traits::overlapping_impls(&infcx,
possible_sibling,
impl_def_id);
impl_def_id,
traits::IntercrateMode::Issue43355);
if let Some(overlap) = overlap {
if tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
return Ok((false, false));
@ -125,22 +147,7 @@ impl<'a, 'gcx, 'tcx> Children {
let ge = tcx.specializes((possible_sibling, impl_def_id));
if le == ge {
// overlap, but no specialization; error out
let trait_ref = overlap.impl_header.trait_ref.unwrap();
let self_ty = trait_ref.self_ty();
Err(OverlapError {
with_impl: possible_sibling,
trait_desc: trait_ref.to_string(),
// only report the Self type if it has at least
// some outer concrete shell; otherwise, it's
// not adding much information.
self_desc: if self_ty.has_concrete_skeleton() {
Some(self_ty.to_string())
} else {
None
},
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
})
Err(overlap_error(overlap))
} else {
Ok((le, ge))
}
@ -163,6 +170,19 @@ impl<'a, 'gcx, 'tcx> Children {
*slot = impl_def_id;
return Ok(Inserted::Replaced(possible_sibling));
} else {
if !tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
tcx.infer_ctxt().enter(|infcx| {
if let Some(overlap) = traits::overlapping_impls(
&infcx,
possible_sibling,
impl_def_id,
traits::IntercrateMode::Fixed)
{
last_lint = Some(overlap_error(overlap));
}
});
}
// no overlap (error bailed already via ?)
}
}
@ -170,7 +190,7 @@ impl<'a, 'gcx, 'tcx> Children {
// no overlap with any potential siblings, so add as a new sibling
debug!("placing as new sibling");
self.insert_blindly(tcx, impl_def_id);
Ok(Inserted::BecameNewSibling)
Ok(Inserted::BecameNewSibling(last_lint))
}
fn iter_mut(&'a mut self) -> Box<Iterator<Item = &'a mut DefId> + 'a> {
@ -199,7 +219,7 @@ impl<'a, 'gcx, 'tcx> Graph {
pub fn insert(&mut self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl_def_id: DefId)
-> Result<(), OverlapError> {
-> Result<Option<OverlapError>, OverlapError> {
assert!(impl_def_id.is_local());
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@ -220,10 +240,11 @@ impl<'a, 'gcx, 'tcx> Graph {
self.parent.insert(impl_def_id, trait_def_id);
self.children.entry(trait_def_id).or_insert(Children::new())
.insert_blindly(tcx, impl_def_id);
return Ok(());
return Ok(None);
}
let mut parent = trait_def_id;
let mut last_lint = None;
let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), false);
// Descend the specialization tree, where `parent` is the current parent node
@ -234,7 +255,8 @@ impl<'a, 'gcx, 'tcx> Graph {
.insert(tcx, impl_def_id, simplified)?;
match insert_result {
BecameNewSibling => {
BecameNewSibling(opt_lint) => {
last_lint = opt_lint;
break;
}
Replaced(new_child) => {
@ -251,7 +273,7 @@ impl<'a, 'gcx, 'tcx> Graph {
}
self.parent.insert(impl_def_id, parent);
Ok(())
Ok(last_lint)
}
/// Insert cached metadata mapping from a child impl back to its parent.

View File

@ -10,7 +10,7 @@
use hir::def_id::DefId;
use ty::{self, Ty, TypeFoldable, Substs, TyCtxt};
use ty::subst::{Kind, Subst};
use ty::subst::Kind;
use traits;
use syntax::abi::Abi;
use util::ppaux;
@ -311,7 +311,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
let self_ty = tcx.mk_closure_from_closure_substs(
closure_did, substs);
let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
let sig = substs.closure_sig(closure_did, tcx);
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
assert_eq!(sig.inputs().len(), 1);
let substs = tcx.mk_substs([

View File

@ -27,6 +27,7 @@ pub trait QueryConfig {
pub(super) trait QueryDescription<'tcx>: QueryConfig {
fn describe(tcx: TyCtxt, key: Self::Key) -> String;
#[inline]
fn cache_on_disk(_: Self::Key) -> bool {
false
}
@ -34,7 +35,7 @@ pub(super) trait QueryDescription<'tcx>: QueryConfig {
fn try_load_from_disk(_: TyCtxt<'_, 'tcx, 'tcx>,
_: SerializedDepNodeIndex)
-> Option<Self::Value> {
bug!("QueryDescription::load_from_disk() called for unsupport query.")
bug!("QueryDescription::load_from_disk() called for an unsupported query.")
}
}
@ -166,6 +167,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::symbol_name<'tcx> {
fn describe(_tcx: TyCtxt, instance: ty::Instance<'tcx>) -> String {
format!("computing the symbol for `{}`", instance)
}
#[inline]
fn cache_on_disk(_: Self::Key) -> bool {
true
}
#[inline]
fn try_load_from_disk<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: SerializedDepNodeIndex)
-> Option<Self::Value> {
tcx.on_disk_query_result_cache.try_load_query_result(tcx, id)
}
}
impl<'tcx> QueryDescription<'tcx> for queries::describe_def<'tcx> {
@ -234,6 +247,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::const_is_rvalue_promotable_to_sta
format!("const checking if rvalue is promotable to static `{}`",
tcx.item_path_str(def_id))
}
#[inline]
fn cache_on_disk(_: Self::Key) -> bool {
true
}
#[inline]
fn try_load_from_disk<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: SerializedDepNodeIndex)
-> Option<Self::Value> {
tcx.on_disk_query_result_cache.try_load_query_result(tcx, id)
}
}
impl<'tcx> QueryDescription<'tcx> for queries::rvalue_promotable_map<'tcx> {
@ -254,6 +279,18 @@ impl<'tcx> QueryDescription<'tcx> for queries::trans_fulfill_obligation<'tcx> {
fn describe(tcx: TyCtxt, key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> String {
format!("checking if `{}` fulfills its obligations", tcx.item_path_str(key.1.def_id()))
}
#[inline]
fn cache_on_disk(_: Self::Key) -> bool {
true
}
#[inline]
fn try_load_from_disk<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: SerializedDepNodeIndex)
-> Option<Self::Value> {
tcx.on_disk_query_result_cache.try_load_query_result(tcx, id)
}
}
impl<'tcx> QueryDescription<'tcx> for queries::trait_impls_of<'tcx> {
@ -567,3 +604,42 @@ impl<'tcx> QueryDescription<'tcx> for queries::typeck_tables_of<'tcx> {
}
}
impl<'tcx> QueryDescription<'tcx> for queries::optimized_mir<'tcx> {
#[inline]
fn cache_on_disk(def_id: Self::Key) -> bool {
def_id.is_local()
}
fn try_load_from_disk<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: SerializedDepNodeIndex)
-> Option<Self::Value> {
let mir: Option<::mir::Mir<'tcx>> = tcx.on_disk_query_result_cache
.try_load_query_result(tcx, id);
mir.map(|x| tcx.alloc_mir(x))
}
}
macro_rules! impl_disk_cacheable_query(
($query_name:ident, |$key:tt| $cond:expr) => {
impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {
#[inline]
fn cache_on_disk($key: Self::Key) -> bool {
$cond
}
#[inline]
fn try_load_from_disk<'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
id: SerializedDepNodeIndex)
-> Option<Self::Value> {
tcx.on_disk_query_result_cache.try_load_query_result(tcx, id)
}
}
}
);
impl_disk_cacheable_query!(unsafety_check_result, |def_id| def_id.is_local());
impl_disk_cacheable_query!(borrowck, |def_id| def_id.is_local());
impl_disk_cacheable_query!(mir_borrowck, |def_id| def_id.is_local());
impl_disk_cacheable_query!(mir_const_qualif, |def_id| def_id.is_local());
impl_disk_cacheable_query!(contains_extern_indicator, |_| true);
impl_disk_cacheable_query!(def_symbol_name, |_| true);

View File

@ -190,8 +190,10 @@ define_maps! { <'tcx>
[] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
[] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
// FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
[] fn mir_borrowck: MirBorrowCheck(DefId) -> (),
/// Borrow checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,
/// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence.

View File

@ -207,6 +207,16 @@ impl<'sess> OnDiskCache<'sess> {
// Encode TypeckTables
encode_query_results::<typeck_tables_of, _>(tcx, enc, qri)?;
encode_query_results::<optimized_mir, _>(tcx, enc, qri)?;
encode_query_results::<unsafety_check_result, _>(tcx, enc, qri)?;
encode_query_results::<borrowck, _>(tcx, enc, qri)?;
encode_query_results::<mir_borrowck, _>(tcx, enc, qri)?;
encode_query_results::<mir_const_qualif, _>(tcx, enc, qri)?;
encode_query_results::<def_symbol_name, _>(tcx, enc, qri)?;
encode_query_results::<const_is_rvalue_promotable_to_static, _>(tcx, enc, qri)?;
encode_query_results::<contains_extern_indicator, _>(tcx, enc, qri)?;
encode_query_results::<symbol_name, _>(tcx, enc, qri)?;
encode_query_results::<trans_fulfill_obligation, _>(tcx, enc, qri)?;
}
// Encode diagnostics

View File

@ -974,4 +974,7 @@ impl_load_from_cache!(
BorrowCheck => borrowck,
MirBorrowCheck => mir_borrowck,
MirConstQualif => mir_const_qualif,
SymbolName => def_symbol_name,
ConstIsRvaluePromotableToStatic => const_is_rvalue_promotable_to_static,
ContainsExternIndicator => contains_extern_indicator,
);

View File

@ -356,6 +356,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
/// Returns the closure kind for this closure; only usable outside
/// of an inference context, because in that context we know that
/// there are no type variables.
///
/// If you have an inference context, use `infcx.closure_kind()`.
pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
self.split(def_id, tcx).closure_kind_ty.to_opt_closure_kind().unwrap()
}
@ -363,6 +365,8 @@ impl<'tcx> ClosureSubsts<'tcx> {
/// Extracts the signature from the closure; only usable outside
/// of an inference context, because in that context we know that
/// there are no type variables.
///
/// If you have an inference context, use `infcx.closure_sig()`.
pub fn closure_sig(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::PolyFnSig<'tcx> {
match self.closure_sig_ty(def_id, tcx).sty {
ty::TyFnPtr(sig) => sig,
@ -646,6 +650,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
pub struct Binder<T>(pub T);
impl<T> Binder<T> {
/// Wraps `value` in a binder, asserting that `value` does not
/// contain any bound regions that would be bound by the
/// binder. This is commonly used to 'inject' a value T into a
/// different binding level.
pub fn dummy<'tcx>(value: T) -> Binder<T>
where T: TypeFoldable<'tcx>
{
assert!(!value.has_escaping_regions());
Binder(value)
}
/// Skips the binder and returns the "bound" value. This is a
/// risky thing to do because it's easy to get confused about
/// debruijn indices and the like. It is usually better to
@ -700,6 +715,32 @@ impl<T> Binder<T> {
Some(self.skip_binder().clone())
}
}
/// Given two things that have the same binder level,
/// and an operation that wraps on their contents, execute the operation
/// and then wrap its result.
///
/// `f` should consider bound regions at depth 1 to be free, and
/// anything it produces with bound regions at depth 1 will be
/// bound in the resulting return value.
pub fn fuse<U,F,R>(self, u: Binder<U>, f: F) -> Binder<R>
where F: FnOnce(T, U) -> R
{
ty::Binder(f(self.0, u.0))
}
/// Split the contents into two things that share the same binder
/// level as the original, returning two distinct binders.
///
/// `f` should consider bound regions at depth 1 to be free, and
/// anything it produces with bound regions at depth 1 will be
/// bound in the resulting return values.
pub fn split<U,V,F>(self, f: F) -> (Binder<U>, Binder<V>)
where F: FnOnce(T) -> (U, V)
{
let (u, v) = f(self.0);
(ty::Binder(u), ty::Binder(v))
}
}
/// Represents the projection of an associated type. In explicit UFCS
@ -799,6 +840,9 @@ impl<'tcx> PolyFnSig<'tcx> {
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
}
pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
}
pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output().clone())
}

View File

@ -439,12 +439,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
});
let dtor_did = match dtor_did {
Some(dtor) => dtor,
None => return None,
};
Some(ty::Destructor { did: dtor_did })
Some(ty::Destructor { did: dtor_did? })
}
/// Return the set of types that are required to be alive in

View File

@ -215,12 +215,6 @@ pub fn record_time<T, F>(accu: &Cell<Duration>, f: F) -> T where
rv
}
// Like std::macros::try!, but for Option<>.
#[cfg(unix)]
macro_rules! option_try(
($e:expr) => (match $e { Some(e) => e, None => return None })
);
// Memory reporting
#[cfg(unix)]
fn get_resident() -> Option<usize> {
@ -228,11 +222,11 @@ fn get_resident() -> Option<usize> {
use std::io::Read;
let field = 1;
let mut f = option_try!(File::open("/proc/self/statm").ok());
let mut f = File::open("/proc/self/statm").ok()?;
let mut contents = String::new();
option_try!(f.read_to_string(&mut contents).ok());
let s = option_try!(contents.split_whitespace().nth(field));
let npages = option_try!(s.parse::<usize>().ok());
f.read_to_string(&mut contents).ok()?;
let s = contents.split_whitespace().nth(field)?;
let npages = s.parse::<usize>().ok()?;
Some(npages * 4096)
}

View File

@ -291,10 +291,8 @@ impl<'a, T: Idx> Iterator for Iter<'a, T> {
}
}
match self.iter.next() {
Some((i, word)) => self.cur = Some((*word, word_bits * i)),
None => return None,
}
let (i, word) = self.iter.next()?;
self.cur = Some((*word, word_bits * i));
}
}
}

View File

@ -1069,7 +1069,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,
time(time_passes,
"MIR borrow checking",
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) });
|| for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id); });
time(time_passes,
"MIR effect checking",

View File

@ -982,7 +982,7 @@ Available lint options:
println!("Lint groups provided by rustc:\n");
println!(" {} {}", padded("name"), "sub-lints");
println!(" {} {}", padded("----"), "---------");
println!(" {} {}", padded("warnings"), "all built-in lints");
println!(" {} {}", padded("warnings"), "all lints that are set to issue warnings");
let print_lint_groups = |lints: Vec<(&'static str, Vec<lint::LintId>)>| {
for (name, to) in lints {

View File

@ -27,10 +27,21 @@ impl StyledBuffer {
}
fn replace_tabs(&mut self) {
for line in self.text.iter_mut() {
for c in line.iter_mut() {
for (line_pos, line) in self.text.iter_mut().enumerate() {
let mut tab_pos = vec![];
for (pos, c) in line.iter().enumerate() {
if *c == '\t' {
*c = ' ';
tab_pos.push(pos);
}
}
// start with the tabs at the end of the line to replace them with 4 space chars
for pos in tab_pos.iter().rev() {
assert_eq!(line.remove(*pos), '\t');
// fix the position of the style to match up after replacing the tabs
let s = self.styles[line_pos].remove(*pos);
for _ in 0..4 {
line.insert(*pos, ' ');
self.styles[line_pos].insert(*pos, s);
}
}
}

View File

@ -247,11 +247,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
id: LintId::of(SAFE_PACKED_BORROWS),
reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
},
FutureIncompatibleInfo {
id: LintId::of(INCOHERENT_FUNDAMENTAL_IMPLS),
reference: "issue #46205 <https://github.com/rust-lang/rust/issues/46205>",
},
FutureIncompatibleInfo {
id: LintId::of(COERCE_NEVER),
reference: "issue #46325 <https://github.com/rust-lang/rust/issues/46325>",
},
]);
// Register renamed and removed lints

View File

@ -47,9 +47,7 @@ impl<'a, 'b, 'tcx> IsolatedEncoder<'a, 'b, 'tcx> {
let mut hasher = StableHasher::new();
hcx.while_hashing_hir_bodies(true, |hcx| {
hcx.while_hashing_spans(false, |hcx| {
body.hash_stable(hcx, &mut hasher);
});
body.hash_stable(hcx, &mut hasher);
});
hasher.finish()

View File

@ -385,10 +385,7 @@ impl<'a> CrateLoader<'a> {
}
fn load(&mut self, locate_ctxt: &mut locator::Context) -> Option<LoadResult> {
let library = match locate_ctxt.maybe_load_library_crate() {
Some(lib) => lib,
None => return None,
};
let library = locate_ctxt.maybe_load_library_crate()?;
// In the case that we're loading a crate, but not matching
// against a hash, we could load a crate which has the same hash

View File

@ -273,25 +273,23 @@ impl<'a, 'tcx> SpecializedDecoder<Span> for DecodeContext<'a, 'tcx> {
let lo = BytePos::decode(self)?;
let hi = BytePos::decode(self)?;
if lo == BytePos(0) && hi == BytePos(0) {
// Don't try to rebase DUMMY_SP. Otherwise it will look like a valid
// Span again.
return Ok(DUMMY_SP)
}
if hi < lo {
// Consistently map invalid spans to DUMMY_SP.
return Ok(DUMMY_SP)
}
let sess = if let Some(sess) = self.sess {
sess
} else {
bug!("Cannot decode Span without Session.")
};
let (lo, hi) = if lo > hi {
// Currently macro expansion sometimes produces invalid Span values
// where lo > hi. In order not to crash the compiler when trying to
// translate these values, let's transform them into something we
// can handle (and which will produce useful debug locations at
// least some of the time).
// This workaround is only necessary as long as macro expansion is
// not fixed. FIXME(#23480)
(lo, lo)
} else {
(lo, hi)
};
let imported_filemaps = self.cdata().imported_filemaps(&sess.codemap());
let filemap = {
// Optimize for the case that most spans within a translated item
@ -321,6 +319,16 @@ impl<'a, 'tcx> SpecializedDecoder<Span> for DecodeContext<'a, 'tcx> {
}
};
// Make sure our binary search above is correct.
debug_assert!(lo >= filemap.original_start_pos &&
lo <= filemap.original_end_pos);
if hi < filemap.original_start_pos || hi > filemap.original_end_pos {
// `hi` points to a different FileMap than `lo` which is invalid.
// Again, map invalid Spans to DUMMY_SP.
return Ok(DUMMY_SP)
}
let lo = (lo + filemap.translated_filemap.start_pos) - filemap.original_start_pos;
let hi = (hi + filemap.translated_filemap.start_pos) - filemap.original_start_pos;

View File

@ -12,12 +12,14 @@
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::infer::InferCtxt;
use rustc::ty::{self, ParamEnv, TyCtxt};
use rustc::ty::maps::Providers;
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
use rustc::mir::ClosureRegionRequirements;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
@ -36,6 +38,9 @@ use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex};
use util::borrowck_errors::{BorrowckErrors, Origin};
use std::fmt;
use std::iter;
use self::MutateMode::{JustWrite, WriteAndRead};
pub(crate) mod nll;
@ -47,7 +52,10 @@ pub fn provide(providers: &mut Providers) {
};
}
fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
fn mir_borrowck<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
) -> Option<ClosureRegionRequirements> {
let input_mir = tcx.mir_validated(def_id);
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
@ -55,21 +63,23 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
!tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
&& !tcx.sess.opts.debugging_opts.nll
} {
return;
return None;
}
tcx.infer_ctxt().enter(|infcx| {
let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
let input_mir: &Mir = &input_mir.borrow();
do_mir_borrowck(&infcx, input_mir, def_id);
do_mir_borrowck(&infcx, input_mir, def_id)
});
debug!("mir_borrowck done");
opt_closure_req
}
fn do_mir_borrowck<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
input_mir: &Mir<'gcx>,
def_id: DefId,
) {
) -> Option<ClosureRegionRequirements> {
let tcx = infcx.tcx;
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
@ -87,7 +97,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
let mir = &mut mir;
// Replace all regions with fresh inference variables.
Some(nll::replace_regions_in_mir(infcx, def_id, mir))
Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
};
let mir = &mir;
@ -128,6 +138,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
move_data: move_data,
param_env: param_env,
};
let body_id = match tcx.def_key(def_id).disambiguated_data.data {
DefPathData::StructCtor |
DefPathData::EnumVariant(_) => None,
_ => Some(tcx.hir.body_owned_by(id))
};
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
let mut flow_inits = FlowInProgress::new(do_dataflow(
tcx,
@ -167,8 +183,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
));
// If we are in non-lexical mode, compute the non-lexical lifetimes.
let opt_regioncx = if let Some(free_regions) = free_regions {
Some(nll::compute_regions(
let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
let (regioncx, opt_closure_req) = nll::compute_regions(
infcx,
def_id,
free_regions,
@ -176,10 +192,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
param_env,
&mut flow_inits,
&mdpe.move_data,
))
);
(Some(regioncx), opt_closure_req)
} else {
assert!(!tcx.sess.opts.debugging_opts.nll);
None
(None, None)
};
let flow_inits = flow_inits; // remove mut
@ -189,6 +206,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
node_id: id,
move_data: &mdpe.move_data,
param_env: param_env,
locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
hir::BodyOwnerKind::Const |
hir::BodyOwnerKind::Static(_) => false,
hir::BodyOwnerKind::Fn => true,
},
storage_dead_or_drop_error_reported: FxHashSet(),
};
@ -198,7 +220,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
id,
&attributes,
&dead_unwinds,
Borrows::new(tcx, mir, opt_regioncx),
Borrows::new(tcx, mir, opt_regioncx, def_id, body_id),
|bd, i| bd.location(i),
));
@ -211,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
);
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
opt_closure_req
}
#[allow(dead_code)]
@ -220,6 +244,13 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
node_id: ast::NodeId,
move_data: &'cx MoveData<'tcx>,
param_env: ParamEnv<'gcx>,
/// This keeps track of whether local variables are free-ed when the function
/// exits even without a `StorageDead`, which appears to be the case for
/// constants.
///
/// I'm not sure this is the right approach - @eddyb could you try and
/// figure this out?
locals_are_invalidated_at_exit: bool,
/// This field keeps track of when storage dead or drop errors are reported
/// in order to stop duplicate error reporting and identify the conditions required
/// for a "temporary value dropped here while still borrowed" error. See #45360.
@ -306,8 +337,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
}
fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
let summary = flow_state.summary();
debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, flow_state);
}
fn visit_statement_entry(
@ -316,12 +346,11 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
stmt: &Statement<'tcx>,
flow_state: &Self::FlowState,
) {
let summary = flow_state.summary();
debug!(
"MirBorrowckCtxt::process_statement({:?}, {:?}): {}",
location,
stmt,
summary
flow_state
);
let span = stmt.source_info.span;
match stmt.kind {
@ -423,12 +452,11 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
flow_state: &Self::FlowState,
) {
let loc = location;
let summary = flow_state.summary();
debug!(
"MirBorrowckCtxt::process_terminator({:?}, {:?}): {}",
location,
term,
summary
flow_state
);
let span = term.source_info.span;
match term.kind {
@ -540,14 +568,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
if self.place_is_invalidated_at_exit(&borrow.place) {
debug!("borrow conflicts at exit {:?}", borrow);
let borrow_span = self.mir.source_info(borrow.location).span;
// FIXME: should be talking about the region lifetime instead
// of just a span here.
let end_span = domain.opt_region_end_span(&borrow.region);
self.report_borrowed_value_does_not_live_long_enough(
ContextKind::StorageDead.new(loc),
(&borrow.place, borrow_span),
(&borrow.place, end_span.unwrap_or(span)),
end_span,
)
}
@ -641,8 +668,9 @@ enum WriteKind {
/// - Take flow state into consideration in `is_assignable()` for local variables
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum LocalMutationIsAllowed {
Move,
Yes,
No,
No
}
#[derive(Copy, Clone)]
@ -710,7 +738,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
context,
(sd, place_span.0),
flow_state,
|this, _index, borrow, common_prefix| match (rw, borrow.kind) {
|this, _index, borrow| match (rw, borrow.kind) {
(Read(_), BorrowKind::Shared) => Control::Continue,
(Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => {
match kind {
@ -727,7 +755,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
error_reported = true;
this.report_conflicting_borrow(
context,
common_prefix,
place_span,
bk,
&borrow,
@ -748,7 +775,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
error_reported = true;
this.report_conflicting_borrow(
context,
common_prefix,
place_span,
bk,
&borrow,
@ -934,7 +960,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
context,
(place, span),
(Deep, Write(WriteKind::Move)),
LocalMutationIsAllowed::Yes,
LocalMutationIsAllowed::Move,
flow_state,
);
@ -952,14 +978,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// Returns whether a borrow of this place is invalidated when the function
/// exits
fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool {
fn place_is_invalidated_at_exit(&mut self, place: &Place<'tcx>) -> bool {
debug!("place_is_invalidated_at_exit({:?})", place);
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
// we just know that all locals are dropped at function exit (otherwise
// we'll have a memory leak) and assume that all statics have a destructor.
let (might_be_alive, will_be_dropped) = match root_place {
//
// FIXME: allow thread-locals to borrow other thread locals?
let (might_be_alive, will_be_dropped, local) = match root_place {
Place::Static(statik) => {
// Thread-locals might be dropped after the function exits, but
// "true" statics will never be.
@ -968,12 +996,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
.iter()
.any(|attr| attr.check_name("thread_local"));
(true, is_thread_local)
(true, is_thread_local, None)
}
Place::Local(_) => {
Place::Local(local) => {
// Locals are always dropped at function exit, and if they
// have a destructor it would've been called already.
(false, true)
(false, self.locals_are_invalidated_at_exit, Some(*local))
}
Place::Projection(..) => {
bug!("root of {:?} is a projection ({:?})?", place, root_place)
@ -996,8 +1024,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
PrefixSet::Shallow
};
self.prefixes(place, prefix_set)
.any(|prefix| prefix == root_place)
let result =
self.prefixes(place, prefix_set).any(|prefix| prefix == root_place);
if result {
if let Some(local) = local {
if let Some(_) = self.storage_dead_or_drop_error_reported.replace(local) {
debug!("place_is_invalidated_at_exit({:?}) - suppressed", place);
return false;
}
}
}
result
}
}
@ -1343,7 +1382,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let local = &self.mir.local_decls[local];
match local.mutability {
Mutability::Not => match is_local_mutation_allowed {
LocalMutationIsAllowed::Yes => Ok(()),
LocalMutationIsAllowed::Yes |
LocalMutationIsAllowed::Move => Ok(()),
LocalMutationIsAllowed::No => Err(place),
},
Mutability::Mut => Ok(()),
@ -1368,10 +1408,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// Mutably borrowed data is mutable, but only if we have a
// unique path to the `&mut`
hir::MutMutable => {
if self.is_upvar_field_projection(&proj.base).is_some() {
self.is_mutable(&proj.base, is_local_mutation_allowed)
} else {
self.is_unique(&proj.base)
match self.is_upvar_field_projection(&proj.base) {
Some(field) if {
self.mir.upvar_decls[field.index()].by_ref
} => {
self.is_mutable(&proj.base,
is_local_mutation_allowed)
}
_ => self.is_unique(&proj.base)
}
}
}
@ -1387,7 +1431,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}
// `Box<T>` owns its content, so mutable if its location is mutable
_ if base_ty.is_box() => {
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
self.is_mutable(&proj.base, is_local_mutation_allowed)
}
// Deref should only be for reference, pointers or boxes
_ => bug!("Deref of unexpected type: {:?}", base_ty),
@ -1404,14 +1448,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
if let Some(field) = field_projection {
let decl = &self.mir.upvar_decls[field.index()];
return match decl.mutability {
Mutability::Mut => self.is_unique(&proj.base),
Mutability::Not => Err(place),
debug!("decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
decl, is_local_mutation_allowed, place);
return match (decl.mutability, is_local_mutation_allowed) {
(Mutability::Not, LocalMutationIsAllowed::No) |
(Mutability::Not, LocalMutationIsAllowed::Yes) => Err(place),
(Mutability::Not, LocalMutationIsAllowed::Move) |
(Mutability::Mut, _) => self.is_unique(&proj.base),
};
}
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
self.is_mutable(&proj.base, is_local_mutation_allowed)
}
}
}
@ -1425,9 +1472,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// Local variables are unique
Ok(())
}
Place::Static(..) => {
// Static variables are not
Err(place)
Place::Static(ref static_) => {
if !self.tcx.is_static_mut(static_.def_id) {
Err(place)
} else {
Ok(())
}
}
Place::Projection(ref proj) => {
match proj.elem {
@ -1478,7 +1528,350 @@ enum NoMovePathFound {
ReachedStatic,
}
/// The degree of overlap between 2 places for borrow-checking.
enum Overlap {
/// The places might partially overlap - in this case, we give
/// up and say that they might conflict. This occurs when
/// different fields of a union are borrowed. For example,
/// if `u` is a union, we have no way of telling how disjoint
/// `u.a.x` and `a.b.y` are.
Arbitrary,
/// The places have the same type, and are either completely disjoint
/// or equal - i.e. they can't "partially" overlap as can occur with
/// unions. This is the "base case" on which we recur for extensions
/// of the place.
EqualOrDisjoint,
/// The places are disjoint, so we know all extensions of them
/// will also be disjoint.
Disjoint,
}
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
// Given that the bases of `elem1` and `elem2` are always either equal
// or disjoint (and have the same type!), return the overlap situation
// between `elem1` and `elem2`.
fn place_element_conflict(&self,
elem1: &Place<'tcx>,
elem2: &Place<'tcx>)
-> Overlap
{
match (elem1, elem2) {
(Place::Local(l1), Place::Local(l2)) => {
if l1 == l2 {
// the same local - base case, equal
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
Overlap::EqualOrDisjoint
} else {
// different locals - base case, disjoint
debug!("place_element_conflict: DISJOINT-LOCAL");
Overlap::Disjoint
}
}
(Place::Static(..), Place::Static(..)) => {
// Borrows of statics do not have to be tracked here.
debug!("place_element_conflict: IGNORED-STATIC");
Overlap::Disjoint
}
(Place::Local(_), Place::Static(_)) |
(Place::Static(_), Place::Local(_)) => {
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL");
Overlap::Disjoint
}
(Place::Projection(pi1), Place::Projection(pi2)) => {
match (&pi1.elem, &pi2.elem) {
(ProjectionElem::Deref, ProjectionElem::Deref) => {
// derefs (e.g. `*x` vs. `*x`) - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
Overlap::EqualOrDisjoint
}
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
if f1 == f2 {
// same field (e.g. `a.y` vs. `a.y`) - recur.
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
let ty = pi1.base.ty(self.mir, self.tcx).to_ty(self.tcx);
match ty.sty {
ty::TyAdt(def, _) if def.is_union() => {
// Different fields of a union, we are basically stuck.
debug!("place_element_conflict: STUCK-UNION");
Overlap::Arbitrary
}
_ => {
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
debug!("place_element_conflict: DISJOINT-FIELD");
Overlap::Disjoint
}
}
}
}
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
// different variants are treated as having disjoint fields,
// even if they occupy the same "space", because it's
// impossible for 2 variants of the same enum to exist
// (and therefore, to be borrowed) at the same time.
//
// Note that this is different from unions - we *do* allow
// this code to compile:
//
// ```
// fn foo(x: &mut Result<i32, i32>) {
// let mut v = None;
// if let Ok(ref mut a) = *x {
// v = Some(a);
// }
// // here, you would *think* that the
// // *entirety* of `x` would be borrowed,
// // but in fact only the `Ok` variant is,
// // so the `Err` variant is *entirely free*:
// if let Err(ref mut a) = *x {
// v = Some(a);
// }
// drop(v);
// }
// ```
if v1 == v2 {
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-FIELD");
Overlap::Disjoint
}
}
(ProjectionElem::Index(..), ProjectionElem::Index(..)) |
(ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) |
(ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) |
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) |
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::ConstantIndex { .. }) |
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) |
(ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) |
(ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) |
(ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
// (if the indexes differ) or equal (if they are the same), so this
// is the recursive case that gives "equal *or* disjoint" its meaning.
//
// Note that by construction, MIR at borrowck can't subdivide
// `Subslice` accesses (e.g. `a[2..3][i]` will never be present) - they
// are only present in slice patterns, and we "merge together" nested
// slice patterns. That means we don't have to think about these. It's
// probably a good idea to assert this somewhere, but I'm too lazy.
//
// FIXME(#8636) we might want to return Disjoint if
// both projections are constant and disjoint.
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY");
Overlap::EqualOrDisjoint
}
(ProjectionElem::Deref, _) |
(ProjectionElem::Field(..), _) |
(ProjectionElem::Index(..), _) |
(ProjectionElem::ConstantIndex { .. }, _) |
(ProjectionElem::Subslice { .. }, _) |
(ProjectionElem::Downcast(..), _) => {
bug!("mismatched projections in place_element_conflict: {:?} and {:?}",
elem1, elem2)
}
}
}
(Place::Projection(_), _) |
(_, Place::Projection(_)) => {
bug!("unexpected elements in place_element_conflict: {:?} and {:?}",
elem1, elem2)
}
}
}
fn borrow_conflicts_with_place(&mut self,
borrow: &BorrowData<'tcx>,
place: &Place<'tcx>,
access: ShallowOrDeep)
-> bool
{
debug!("borrow_conflicts_with_place({:?},{:?},{:?})", borrow, place, access);
// Return all the prefixes of `place` in reverse order, including
// downcasts.
fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>>
{
let mut result = vec![];
let mut place = place;
loop {
result.push(place);
match place {
Place::Projection(interior) => {
place = &interior.base;
}
Place::Local(_) | Place::Static(_) => {
result.reverse();
return result;
}
}
}
}
let borrow_components = place_elements(&borrow.place);
let access_components = place_elements(place);
debug!("borrow_conflicts_with_place: components {:?} / {:?}",
borrow_components, access_components);
let borrow_components = borrow_components.into_iter()
.map(Some).chain(iter::repeat(None));
let access_components = access_components.into_iter()
.map(Some).chain(iter::repeat(None));
// The borrowck rules for proving disjointness are applied from the "root" of the
// borrow forwards, iterating over "similar" projections in lockstep until
// we can prove overlap one way or another. Essentially, we treat `Overlap` as
// a monoid and report a conflict if the product ends up not being `Disjoint`.
//
// At each step, if we didn't run out of borrow or place, we know that our elements
// have the same type, and that they only overlap if they are the identical.
//
// For example, if we are comparing these:
// BORROW: (*x1[2].y).z.a
// ACCESS: (*x1[i].y).w.b
//
// Then our steps are:
// x1 | x1 -- places are the same
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
// x1[2].y | x1[i].y -- equal or disjoint
// *x1[2].y | *x1[i].y -- equal or disjoint
// (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
//
// Because `zip` does potentially bad things to the iterator inside, this loop
// also handles the case where the access might be a *prefix* of the borrow, e.g.
//
// BORROW: (*x1[2].y).z.a
// ACCESS: x1[i].y
//
// Then our steps are:
// x1 | x1 -- places are the same
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
// x1[2].y | x1[i].y -- equal or disjoint
//
// -- here we run out of access - the borrow can access a part of it. If this
// is a full deep access, then we *know* the borrow conflicts with it. However,
// if the access is shallow, then we can proceed:
//
// x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
// are disjoint
//
// Our invariant is, that at each step of the iteration:
// - If we didn't run out of access to match, our borrow and access are comparable
// and either equal or disjoint.
// - If we did run out of accesss, the borrow can access a part of it.
for (borrow_c, access_c) in borrow_components.zip(access_components) {
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
debug!("borrow_conflicts_with_place: {:?} vs. {:?}", borrow_c, access_c);
match (borrow_c, access_c) {
(None, _) => {
// If we didn't run out of access, the borrow can access all of our
// place (e.g. a borrow of `a.b` with an access to `a.b.c`),
// so we have a conflict.
//
// If we did, then we still know that the borrow can access a *part*
// of our place that our access cares about (a borrow of `a.b.c`
// with an access to `a.b`), so we still have a conflict.
//
// FIXME: Differs from AST-borrowck; includes drive-by fix
// to #38899. Will probably need back-compat mode flag.
debug!("borrow_conflict_with_place: full borrow, CONFLICT");
return true;
}
(Some(borrow_c), None) => {
// We know that the borrow can access a part of our place. This
// is a conflict if that is a part our access cares about.
let (base, elem) = match borrow_c {
Place::Projection(box Projection { base, elem }) => (base, elem),
_ => bug!("place has no base?")
};
let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
match (elem, &base_ty.sty, access) {
(_, _, Shallow(Some(ArtificialField::Discriminant))) |
(_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
// The discriminant and array length are like
// additional fields on the type; they do not
// overlap any existing data there. Furthermore,
// they cannot actually be a prefix of any
// borrowed place (at least in MIR as it is
// currently.)
//
// e.g. a (mutable) borrow of `a[5]` while we read the
// array length of `a`.
debug!("borrow_conflicts_with_place: implicit field");
return false;
}
(ProjectionElem::Deref, _, Shallow(None)) => {
// e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
// prefix thereof - the shallow access can't touch anything behind
// the pointer.
debug!("borrow_conflicts_with_place: shallow access behind ptr");
return false;
}
(ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
ty: _, mutbl: hir::MutImmutable
}), _) => {
// the borrow goes through a dereference of a shared reference.
//
// I'm not sure why we are tracking these borrows - shared
// references can *always* be aliased, which means the
// permission check already account for this borrow.
debug!("borrow_conflicts_with_place: behind a shared ref");
return false;
}
(ProjectionElem::Deref, _, Deep) |
(ProjectionElem::Field { .. }, _, _) |
(ProjectionElem::Index { ..}, _, _) |
(ProjectionElem::ConstantIndex { .. }, _, _) |
(ProjectionElem::Subslice { .. }, _, _) |
(ProjectionElem::Downcast { .. }, _, _) => {
// Recursive case. This can still be disjoint on a
// further iteration if this a shallow access and
// there's a deref later on, e.g. a borrow
// of `*x.y` while accessing `x`.
}
}
}
(Some(borrow_c), Some(access_c)) => {
match self.place_element_conflict(&borrow_c, access_c) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
//
// There is no *easy* way of comparing the fields
// further on, because they might have different types
// (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
// `.y` come from different structs).
//
// We could try to do some things here - e.g. count
// dereferences - but that's probably not a good
// idea, at least for now, so just give up and
// report a conflict. This is unsafe code anyway so
// the user could always use raw pointers.
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
return true;
}
Overlap::EqualOrDisjoint => {
// This is the recursive case - proceed to the next element.
}
Overlap::Disjoint => {
// We have proven the borrow disjoint - further
// projections will remain disjoint.
debug!("borrow_conflicts_with_place: disjoint");
return false;
}
}
}
}
}
unreachable!("iter::repeat returned None")
}
fn each_borrow_involving_path<F>(
&mut self,
_context: Context,
@ -1486,7 +1879,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
flow_state: &InProgress<'cx, 'gcx, 'tcx>,
mut op: F,
) where
F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control,
F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>) -> Control,
{
let (access, place) = access_place;
@ -1501,47 +1894,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
'next_borrow: for i in flow_state.borrows.elems_incoming() {
let borrowed = &data[i];
// Is `place` (or a prefix of it) already borrowed? If
// so, that's relevant.
//
// FIXME: Differs from AST-borrowck; includes drive-by fix
// to #38899. Will probably need back-compat mode flag.
for accessed_prefix in self.prefixes(place, PrefixSet::All) {
if *accessed_prefix == borrowed.place {
// FIXME: pass in enum describing case we are in?
let ctrl = op(self, i, borrowed, accessed_prefix);
if ctrl == Control::Break {
return;
}
}
}
// Is `place` a prefix (modulo access type) of the
// `borrowed.place`? If so, that's relevant.
let prefix_kind = match access {
Shallow(Some(ArtificialField::Discriminant)) |
Shallow(Some(ArtificialField::ArrayLength)) => {
// The discriminant and array length are like
// additional fields on the type; they do not
// overlap any existing data there. Furthermore,
// they cannot actually be a prefix of any
// borrowed place (at least in MIR as it is
// currently.)
continue 'next_borrow;
}
Shallow(None) => PrefixSet::Shallow,
Deep => PrefixSet::Supporting,
};
for borrowed_prefix in self.prefixes(&borrowed.place, prefix_kind) {
if borrowed_prefix == place {
// FIXME: pass in enum describing case we are in?
let ctrl = op(self, i, borrowed, borrowed_prefix);
if ctrl == Control::Break {
return;
}
}
if self.borrow_conflicts_with_place(borrowed, place, access) {
let ctrl = op(self, i, borrowed);
if ctrl == Control::Break { return; }
}
}
}
@ -1595,6 +1950,7 @@ mod prefixes {
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[allow(dead_code)]
pub(super) enum PrefixSet {
/// Doesn't stop until it returns the base case (a Local or
/// Static prefix).
@ -1626,10 +1982,7 @@ mod prefixes {
impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
type Item = &'cx Place<'tcx>;
fn next(&mut self) -> Option<Self::Item> {
let mut cursor = match self.next {
None => return None,
Some(place) => place,
};
let mut cursor = self.next?;
// Post-processing `place`: Enqueue any remaining
// work. Also, `place` may not be a prefix itself, but
@ -1907,17 +2260,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
fn report_conflicting_borrow(
&mut self,
context: Context,
common_prefix: &Place<'tcx>,
(place, span): (&Place<'tcx>, Span),
gen_borrow_kind: BorrowKind,
issued_borrow: &BorrowData,
end_issued_loan_span: Option<Span>,
) {
use self::prefixes::IsPrefixOf;
assert!(common_prefix.is_prefix_of(place));
assert!(common_prefix.is_prefix_of(&issued_borrow.place));
let issued_span = self.retrieve_borrow_span(issued_borrow);
let new_closure_span = self.find_closure_span(span, context.loc);
@ -1984,12 +2331,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(_, _, _, BorrowKind::Unique, _, _) => self.tcx
(BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx
.cannot_reborrow_already_uniquely_borrowed(
span,
&desc_place,
"it",
"",
lft,
issued_span,
"",
end_issued_loan_span,
Origin::Mir,
),
(BorrowKind::Mut, _, lft, BorrowKind::Unique, _, _) => self.tcx
.cannot_reborrow_already_uniquely_borrowed(
span,
&desc_place,
"",
lft,
issued_span,
"",
end_issued_loan_span,
@ -2373,8 +2732,10 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
xform_move_outs(&mut self.move_outs);
xform_ever_inits(&mut self.ever_inits);
}
}
fn summary(&self) -> String {
impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str("borrows in effect: [");
@ -2451,7 +2812,7 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
});
s.push_str("]");
return s;
fmt::Display::fmt(&s, fmt)
}
}

View File

@ -9,11 +9,12 @@
// except according to those terms.
use rustc::hir::def_id::DefId;
use rustc::mir::Mir;
use rustc::mir::{ClosureRegionRequirements, Mir};
use rustc::infer::InferCtxt;
use rustc::ty::{self, RegionKind, RegionVid};
use rustc::util::nodemap::FxHashMap;
use std::collections::BTreeSet;
use std::io;
use transform::MirSource;
use transform::type_check;
use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
@ -22,6 +23,7 @@ use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::MoveData;
use util as mir_util;
use util::pretty::{self, ALIGN};
use self::mir_util::PassWhere;
mod constraint_generation;
@ -35,20 +37,26 @@ use self::region_infer::RegionInferenceContext;
mod renumber;
/// Rewrites the regions in the MIR to use NLL variables, also
/// scraping out the set of free regions (e.g., region parameters)
/// scraping out the set of universal regions (e.g., region parameters)
/// declared on the function. That set will need to be given to
/// `compute_regions`.
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
mir: &mut Mir<'tcx>,
) -> UniversalRegions<'tcx> {
// Compute named region information.
let universal_regions = universal_regions::universal_regions(infcx, def_id);
debug!("replace_regions_in_mir(def_id={:?})", def_id);
// Replace all regions with fresh inference variables.
// Compute named region information. This also renumbers the inputs/outputs.
let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
// Replace all remaining regions with fresh inference variables.
renumber::renumber_mir(infcx, &universal_regions, mir);
let source = MirSource::item(def_id);
mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
universal_regions
}
@ -63,7 +71,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
param_env: ty::ParamEnv<'gcx>,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> RegionInferenceContext<'tcx> {
) -> (
RegionInferenceContext<'tcx>,
Option<ClosureRegionRequirements>,
) {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
@ -71,13 +82,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
// Create the region inference context, taking ownership of the region inference
// data that was contained in `infcx`.
let var_origins = infcx.take_region_var_origins();
let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
subtype_constraint_generation::generate(
&mut regioncx,
&universal_regions,
mir,
constraint_sets,
);
let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
// Compute what is live where.
let liveness = &LivenessResults {
@ -110,13 +116,24 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
);
// Solve the region constraints.
regioncx.solve(infcx, &mir);
let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests.
dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, &regioncx);
// write unit-tests, as well as helping with debugging.
dump_mir_results(
infcx,
liveness,
MirSource::item(def_id),
&mir,
&regioncx,
&closure_region_requirements,
);
regioncx
// We also have a `#[rustc_nll]` annotation that causes us to dump
// information
dump_annotation(infcx, &mir, def_id, &regioncx, &closure_region_requirements);
(regioncx, closure_region_requirements)
}
struct LivenessResults {
@ -130,6 +147,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
source: MirSource,
mir: &Mir<'tcx>,
regioncx: &RegionInferenceContext,
closure_region_requirements: &Option<ClosureRegionRequirements>,
) {
if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
return;
@ -164,9 +182,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
match pass_where {
// Before the CFG, dump out the values for each region variable.
PassWhere::BeforeCFG => for region in regioncx.regions() {
writeln!(out, "| {:?}: {}", region, regioncx.region_value_str(region))?;
},
PassWhere::BeforeCFG => {
regioncx.dump_mir(out)?;
if let Some(closure_region_requirements) = closure_region_requirements {
writeln!(out, "|")?;
writeln!(out, "| Free Region Constraints")?;
for_each_region_constraint(closure_region_requirements, &mut |msg| {
writeln!(out, "| {}", msg)
})?;
}
}
// Before each basic block, dump out the values
// that are live on entry to the basic block.
@ -180,19 +206,96 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
&regular_liveness_per_location[&location],
&drop_liveness_per_location[&location],
);
writeln!(out, " | Live variables at {:?}: {}", location, s)?;
writeln!(
out,
"{:ALIGN$} | Live variables on entry to {:?}: {}",
"",
location,
s,
ALIGN = ALIGN
)?;
}
PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
}
Ok(())
});
// Also dump the inference graph constraints as a graphviz file.
let _: io::Result<()> = do catch {
let mut file =
pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
regioncx.dump_graphviz(&mut file)
};
}
fn dump_annotation<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
mir_def_id: DefId,
regioncx: &RegionInferenceContext,
closure_region_requirements: &Option<ClosureRegionRequirements>,
) {
let tcx = infcx.tcx;
let base_def_id = tcx.closure_base_def_id(mir_def_id);
if !tcx.has_attr(base_def_id, "rustc_regions") {
return;
}
// When the enclosing function is tagged with `#[rustc_regions]`,
// we dump out various bits of state as warnings. This is useful
// for verifying that the compiler is behaving as expected. These
// warnings focus on the closure region requirements -- for
// viewing the intraprocedural state, the -Zdump-mir output is
// better.
if let Some(closure_region_requirements) = closure_region_requirements {
let mut err = tcx.sess
.diagnostic()
.span_note_diag(mir.span, "External requirements");
regioncx.annotate(&mut err);
err.note(&format!(
"number of external vids: {}",
closure_region_requirements.num_external_vids
));
// Dump the region constraints we are imposing *between* those
// newly created variables.
for_each_region_constraint(closure_region_requirements, &mut |msg| {
err.note(msg);
Ok(())
}).unwrap();
err.emit();
} else {
let mut err = tcx.sess
.diagnostic()
.span_note_diag(mir.span, "No external requirements");
regioncx.annotate(&mut err);
err.emit();
}
}
fn for_each_region_constraint(
closure_region_requirements: &ClosureRegionRequirements,
with_msg: &mut FnMut(&str) -> io::Result<()>,
) -> io::Result<()> {
for req in &closure_region_requirements.outlives_requirements {
with_msg(&format!(
"where {:?}: {:?}",
req.free_region,
req.outlived_free_region,
))?;
}
Ok(())
}
/// Right now, we piggy back on the `ReVar` to store our NLL inference
/// regions. These are indexed with `RegionVid`. This method will
/// assert that the region is a `ReVar` and extract its interal index.
/// This is reasonable because in our MIR we replace all free regions
/// This is reasonable because in our MIR we replace all universal regions
/// with inference variables.
pub trait ToRegionVid {
fn to_region_vid(&self) -> RegionVid;

View File

@ -0,0 +1,48 @@
// 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.
//! As part of the NLL unit tests, you can annotate a function with
//! `#[rustc_regions]`, and we will emit information about the region
//! inference context and -- in particular -- the external constraints
//! that this region imposes on others. The methods in this file
//! handle the part about dumping the inference context internal
//! state.
use rustc::ty;
use rustc_errors::DiagnosticBuilder;
use super::RegionInferenceContext;
impl<'gcx, 'tcx> RegionInferenceContext<'tcx> {
/// Write out our state into the `.mir` files.
pub(crate) fn annotate(&self, err: &mut DiagnosticBuilder<'_>) {
match self.universal_regions.defining_ty.sty {
ty::TyClosure(def_id, substs) => {
err.note(&format!(
"defining type: {:?} with closure substs {:#?}",
def_id,
&substs.substs[..]
));
}
ty::TyFnDef(def_id, substs) => {
err.note(&format!(
"defining type: {:?} with substs {:#?}",
def_id,
&substs[..]
));
}
_ => {
err.note(&format!(
"defining type: {:?}",
self.universal_regions.defining_ty
));
}
}
}
}

View File

@ -0,0 +1,100 @@
// 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.
//! As part of generating the regions, if you enable `-Zdump-mir=nll`,
//! we will generate an annotated copy of the MIR that includes the
//! state of region inference. This code handles emitting the region
//! context internal state.
use std::io::{self, Write};
use super::{Constraint, RegionInferenceContext};
// Room for "'_#NNNNr" before things get misaligned.
// Easy enough to fix if this ever doesn't seem like
// enough.
const REGION_WIDTH: usize = 8;
impl<'tcx> RegionInferenceContext<'tcx> {
/// Write out our state into the `.mir` files.
pub(crate) fn dump_mir(&self, out: &mut Write) -> io::Result<()> {
writeln!(out, "| Free Region Mapping")?;
for region in self.regions() {
if self.definitions[region].is_universal {
let classification = self.universal_regions.region_classification(region).unwrap();
let outlived_by = self.universal_regions.regions_outlived_by(region);
writeln!(
out,
"| {r:rw$} | {c:cw$} | {ob}",
r = format!("{:?}", region),
rw = REGION_WIDTH,
c = format!("{:?}", classification),
cw = 8, // "External" at most
ob = format!("{:?}", outlived_by)
)?;
}
}
writeln!(out, "|")?;
writeln!(out, "| Inferred Region Values")?;
for region in self.regions() {
writeln!(
out,
"| {r:rw$} | {v}",
r = format!("{:?}", region),
rw = REGION_WIDTH,
v = self.region_value_str(region),
)?;
}
writeln!(out, "|")?;
writeln!(out, "| Inference Constraints")?;
self.for_each_constraint(&mut |msg| writeln!(out, "| {}", msg))?;
Ok(())
}
/// Debugging aid: Invokes the `with_msg` callback repeatedly with
/// our internal region constraints. These are dumped into the
/// -Zdump-mir file so that we can figure out why the region
/// inference resulted in the values that it did when debugging.
fn for_each_constraint(
&self,
with_msg: &mut FnMut(&str) -> io::Result<()>,
) -> io::Result<()> {
for region in self.definitions.indices() {
let value = self.region_value_str_from_matrix(&self.liveness_constraints, region);
if value != "{}" {
with_msg(&format!("{:?} live at {}", region, value))?;
}
}
let mut constraints: Vec<_> = self.constraints.iter().collect();
constraints.sort();
for constraint in &constraints {
let Constraint {
sup,
sub,
point,
span,
} = constraint;
with_msg(&format!(
"{:?}: {:?} @ {:?} due to {:?}",
sup,
sub,
point,
span
))?;
}
Ok(())
}
}

View File

@ -0,0 +1,71 @@
// Copyright 2014 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.
//! This module provides linkage between RegionInferenceContext and
//! libgraphviz traits, specialized to attaching borrowck analysis
//! data to rendered labels.
use dot::{self, IntoCow};
use rustc_data_structures::indexed_vec::Idx;
use std::borrow::Cow;
use std::io::{self, Write};
use super::*;
impl<'tcx> RegionInferenceContext<'tcx> {
/// Write out the region constraint graph.
pub(crate) fn dump_graphviz(&self, mut w: &mut Write) -> io::Result<()> {
dot::render(self, &mut w)
}
}
impl<'this, 'tcx> dot::Labeller<'this> for RegionInferenceContext<'tcx> {
type Node = RegionVid;
type Edge = Constraint;
fn graph_id(&'this self) -> dot::Id<'this> {
dot::Id::new(format!("RegionInferenceContext")).unwrap()
}
fn node_id(&'this self, n: &RegionVid) -> dot::Id<'this> {
dot::Id::new(format!("r{}", n.index())).unwrap()
}
fn node_shape(&'this self, _node: &RegionVid) -> Option<dot::LabelText<'this>> {
Some(dot::LabelText::LabelStr(Cow::Borrowed("box")))
}
fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> {
dot::LabelText::LabelStr(format!("{:?}", n).into_cow())
}
fn edge_label(&'this self, e: &Constraint) -> dot::LabelText<'this> {
dot::LabelText::LabelStr(format!("{:?}", e.point).into_cow())
}
}
impl<'this, 'tcx> dot::GraphWalk<'this> for RegionInferenceContext<'tcx> {
type Node = RegionVid;
type Edge = Constraint;
fn nodes(&'this self) -> dot::Nodes<'this, RegionVid> {
let vids: Vec<RegionVid> = self.definitions.indices().collect();
vids.into_cow()
}
fn edges(&'this self) -> dot::Edges<'this, Constraint> {
(&self.constraints[..]).into_cow()
}
// Render `a: b` as `a <- b`, indicating the flow
// of data during inference.
fn source(&'this self, edge: &Constraint) -> RegionVid {
edge.sub
}
fn target(&'this self, edge: &Constraint) -> RegionVid {
edge.sup
}
}

View File

@ -9,12 +9,13 @@
// except according to those terms.
use super::universal_regions::UniversalRegions;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::RegionVariableOrigin;
use rustc::infer::SubregionOrigin;
use rustc::infer::region_constraints::VarOrigins;
use rustc::infer::outlives::free_region_map::FreeRegionMap;
use rustc::mir::{Location, Mir};
use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
@ -24,6 +25,10 @@ use std::collections::BTreeMap;
use std::fmt;
use syntax_pos::Span;
mod annotation;
mod dump_mir;
mod graphviz;
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionVid`). The
@ -52,12 +57,9 @@ pub struct RegionInferenceContext<'tcx> {
/// the free regions.)
point_indices: BTreeMap<Location, usize>,
/// Number of universally quantified regions. This is used to
/// determine the meaning of the bits in `inferred_values` and
/// friends.
num_universal_regions: usize,
free_region_map: &'tcx FreeRegionMap<'tcx>,
/// Information about the universally quantified regions in scope
/// on this function and their (known) relations to one another.
universal_regions: UniversalRegions<'tcx>,
}
struct RegionDefinition<'tcx> {
@ -67,9 +69,15 @@ struct RegionDefinition<'tcx> {
/// late-bound-regions).
origin: RegionVariableOrigin,
/// If this is a free-region, then this is `Some(X)` where `X` is
/// the name of the region.
name: Option<ty::Region<'tcx>>,
/// True if this is a universally quantified region. This means a
/// lifetime parameter that appears in the function signature (or,
/// in the case of a closure, in the closure environment, which of
/// course is also in the function signature).
is_universal: bool,
/// If this is 'static or an early-bound region, then this is
/// `Some(X)` where `X` is the name of the region.
external_name: Option<ty::Region<'tcx>>,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -98,11 +106,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// regions defined in `universal_regions`.
pub fn new(
var_origins: VarOrigins,
universal_regions: &UniversalRegions<'tcx>,
universal_regions: UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
) -> Self {
let num_region_variables = var_origins.len();
let num_universal_regions = universal_regions.indices.len();
let num_universal_regions = universal_regions.len();
let mut num_points = 0;
let mut point_indices = BTreeMap::new();
@ -133,11 +141,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
inferred_values: None,
constraints: Vec::new(),
point_indices,
num_universal_regions,
free_region_map: universal_regions.free_region_map,
universal_regions,
};
result.init_universal_regions(universal_regions);
result.init_universal_regions();
result
}
@ -159,25 +166,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// R1 = { CFG, R0, R1 } // 'b
///
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
/// and (b) any free regions that it outlives, which in this case
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
/// hence contains R0 and R1.
fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) {
let UniversalRegions {
indices,
free_region_map: _,
} = universal_regions;
/// and (b) any universally quantified regions that it outlives,
/// which in this case is just itself. R1 (`'b`) in contrast also
/// outlives `'a` and hence contains R0 and R1.
fn init_universal_regions(&mut self) {
// Update the names (if any)
for (external_name, variable) in self.universal_regions.named_universal_regions() {
self.definitions[variable].external_name = Some(external_name);
}
// For each universally quantified region X:
for (free_region, &variable) in indices {
for variable in self.universal_regions.universal_regions() {
// These should be free-region variables.
assert!(match self.definitions[variable].origin {
RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
_ => false,
});
// Initialize the name and a few other details.
self.definitions[variable].name = Some(free_region);
self.definitions[variable].is_universal = true;
// Add all nodes in the CFG to liveness constraints
for (_location, point_index) in &self.point_indices {
@ -196,6 +202,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.definitions.indices()
}
/// Given a universal region in scope on the MIR, returns the
/// corresponding index.
///
/// (Panics if `r` is not a registered universal region.)
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
self.universal_regions.to_region_vid(r)
}
/// Returns true if the region `r` contains the point `p`.
///
/// Panics if called before `solve()` executes,
@ -237,19 +251,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.as_ref()
.expect("region values not yet inferred");
self.region_value_str_from_matrix(inferred_values, r)
}
fn region_value_str_from_matrix(&self,
matrix: &BitMatrix,
r: RegionVid) -> String {
let mut result = String::new();
result.push_str("{");
let mut sep = "";
for &point in self.point_indices.keys() {
if self.region_contains_point_in_matrix(inferred_values, r, point) {
if self.region_contains_point_in_matrix(matrix, r, point) {
result.push_str(&format!("{}{:?}", sep, point));
sep = ", ";
}
}
for fr in (0..self.num_universal_regions).map(RegionVid::new) {
if self.region_contains_region_in_matrix(inferred_values, r, fr) {
for fr in (0..self.universal_regions.len()).map(RegionVid::new) {
if self.region_contains_region_in_matrix(matrix, r, fr) {
result.push_str(&format!("{}{:?}", sep, fr));
sep = ", ";
}
@ -289,8 +309,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// Perform region inference.
pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
pub(super) fn solve(
&mut self,
infcx: &InferCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
mir_def_id: DefId,
) -> Option<ClosureRegionRequirements> {
assert!(self.inferred_values.is_none(), "values already inferred");
let tcx = infcx.tcx;
// Find the minimal regions that can solve the constraints. This is infallible.
self.propagate_constraints(mir);
@ -310,57 +336,135 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// The universal regions are always found in a prefix of the
// full list.
let free_region_definitions = self.definitions
let universal_definitions = self.definitions
.iter_enumerated()
.take_while(|(_, fr_definition)| fr_definition.name.is_some());
.take_while(|(_, fr_definition)| fr_definition.is_universal);
for (fr, fr_definition) in free_region_definitions {
self.check_free_region(infcx, fr, fr_definition);
// Go through each of the universal regions `fr` and check that
// they did not grow too large, accumulating any requirements
// for our caller into the `outlives_requirements` vector.
let mut outlives_requirements = vec![];
for (fr, _) in universal_definitions {
self.check_universal_region(infcx, fr, &mut outlives_requirements);
}
// If this is not a closure, then there is no caller to which we can
// "pass the buck". So if there are any outlives-requirements that were
// not satisfied, we just have to report a hard error here.
if !tcx.is_closure(mir_def_id) {
for outlives_requirement in outlives_requirements {
self.report_error(
infcx,
outlives_requirement.free_region,
outlives_requirement.outlived_free_region,
outlives_requirement.blame_span,
);
}
return None;
}
let num_external_vids = self.universal_regions.num_global_and_external_regions();
Some(ClosureRegionRequirements {
num_external_vids,
outlives_requirements,
})
}
fn check_free_region(
/// Check the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
/// fr`, we want to check that `fr: X`. If not, that's either an
/// error, or something we have to propagate to our creator.
///
/// Things that are to be propagated are accumulated into the
/// `outlives_requirements` vector.
fn check_universal_region(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
longer_fr: RegionVid,
longer_definition: &RegionDefinition<'tcx>,
outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
let longer_name = longer_definition.name.unwrap();
let longer_value = inferred_values.iter(longer_fr.index());
// Find every region `shorter` such that `longer: shorter`
// (because `longer` includes `end(shorter)`).
for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
let shorter_fr = RegionVid::new(shorter_fr);
debug!("check_universal_region(fr={:?})", longer_fr);
// `fr` includes `end(fr)`, that's not especially
// interesting.
if longer_fr == shorter_fr {
// Find every region `o` such that `fr: o`
// (because `fr` includes `end(o)`).
let shorter_frs = longer_value
.take_while(|&i| i < self.universal_regions.len())
.map(RegionVid::new);
for shorter_fr in shorter_frs {
// If it is known that `fr: o`, carry on.
if self.universal_regions.outlives(longer_fr, shorter_fr) {
continue;
}
let shorter_definition = &self.definitions[shorter_fr];
let shorter_name = shorter_definition.name.unwrap();
debug!(
"check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
longer_fr,
shorter_fr,
);
// Check that `o <= fr`. If not, report an error.
if !self.free_region_map
.sub_free_regions(shorter_name, longer_name)
{
// FIXME: worst error msg ever
let blame_span = self.blame_span(longer_fr, shorter_fr);
infcx.tcx.sess.span_err(
blame_span,
&format!(
"free region `{}` does not outlive `{}`",
longer_name,
shorter_name
),
let blame_span = self.blame_span(longer_fr, shorter_fr);
// Shrink `fr` until we find a non-local region (if we do).
// We'll call that `fr-` -- it's ever so slightly smaller than `fr`.
if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) {
debug!("check_universal_region: fr_minus={:?}", fr_minus);
// Grow `shorter_fr` until we find a non-local
// regon. (We always will.) We'll call that
// `shorter_fr+` -- it's ever so slightly larger than
// `fr`.
let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr);
debug!(
"check_universal_region: shorter_fr_plus={:?}",
shorter_fr_plus
);
// Push the constraint `fr-: shorter_fr+`
outlives_requirements.push(ClosureOutlivesRequirement {
free_region: fr_minus,
outlived_free_region: shorter_fr_plus,
blame_span: blame_span,
});
return;
}
// If we could not shrink `fr` to something smaller that
// the external users care about, then we can't pass the
// buck; just report an error.
self.report_error(infcx, longer_fr, shorter_fr, blame_span);
}
}
fn report_error(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
fr: RegionVid,
outlived_fr: RegionVid,
blame_span: Span,
) {
// Obviously uncool error reporting.
let fr_string = match self.definitions[fr].external_name {
Some(r) => format!("free region `{}`", r),
None => format!("free region `{:?}`", fr),
};
let outlived_fr_string = match self.definitions[outlived_fr].external_name {
Some(r) => format!("free region `{}`", r),
None => format!("free region `{:?}`", outlived_fr),
};
infcx.tcx.sess.span_err(
blame_span,
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
);
}
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
@ -421,8 +525,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
stack.push(start_point);
while let Some(p) = stack.pop() {
debug!(" copy: p={:?}", p);
if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
debug!(" not in from-region");
continue;
@ -464,7 +566,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// and make sure they are included in the `to_region`.
let universal_region_indices = inferred_values
.iter(from_region.index())
.take_while(|&i| i < self.num_universal_regions)
.take_while(|&i| i < self.universal_regions.len())
.collect::<Vec<_>>();
for fr in &universal_region_indices {
changed |= inferred_values.add(to_region.index(), *fr);
@ -535,7 +637,11 @@ impl<'tcx> RegionDefinition<'tcx> {
// Create a new region definition. Note that, for free
// regions, these fields get updated later in
// `init_universal_regions`.
Self { origin, name: None }
Self {
origin,
is_universal: false,
external_name: None,
}
}
}
@ -551,3 +657,70 @@ impl fmt::Debug for Constraint {
)
}
}
pub trait ClosureRegionRequirementsExt {
fn apply_requirements<'tcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
location: Location,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>,
);
}
impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
/// Given an instance T of the closure type, this method
/// instantiates the "extra" requirements that we computed for the
/// closure into the inference context. This has the effect of
/// adding new subregion obligations to existing variables.
///
/// As described on `ClosureRegionRequirements`, the extra
/// requirements are expressed in terms of regionvids that index
/// into the free regions that appear on the closure type. So, to
/// do this, we first copy those regions out from the type T into
/// a vector. Then we can just index into that vector to extract
/// out the corresponding region from T and apply the
/// requirements.
fn apply_requirements<'tcx>(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
location: Location,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>,
) {
let tcx = infcx.tcx;
debug!(
"apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})",
location,
closure_def_id,
closure_substs
);
// Get Tu.
let user_closure_ty = tcx.mk_closure(closure_def_id, closure_substs);
debug!("apply_requirements: user_closure_ty={:?}", user_closure_ty);
// Extract the values of the free regions in `user_closure_ty`
// into a vector. These are the regions that we will be
// relating to one another.
let closure_mapping =
UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids);
debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
// Create the predicates.
for outlives_requirement in &self.outlives_requirements {
let region = closure_mapping[outlives_requirement.free_region];
let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
debug!(
"apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}",
region,
outlived_region,
outlives_requirement
);
// FIXME, this origin is not entirely suitable.
let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span);
infcx.sub_regions(origin, outlived_region, region);
}
}
}

View File

@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::subst::Substs;
use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
use rustc::mir::RETURN_PLACE;
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
@ -25,25 +26,24 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
universal_regions: &UniversalRegions<'tcx>,
mir: &mut Mir<'tcx>,
) {
// Create inference variables for each of the free regions
// declared on the function signature.
let free_region_inference_vars = (0..universal_regions.indices.len())
.map(RegionVid::new)
.map(|vid_expected| {
let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
assert_eq!(vid_expected, r.to_region_vid());
r
})
.collect();
debug!("renumber_mir()");
debug!("renumber_mir: universal_regions={:#?}", universal_regions);
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
// Update the return type and types of the arguments based on the
// `universal_regions` computation.
debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
for (&input_ty, local) in universal_regions
.input_tys
.iter()
.zip((1..).map(Local::new))
{
debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
mir.local_decls[local].ty = input_ty;
}
let mut visitor = NLLVisitor {
infcx,
universal_regions,
free_region_inference_vars,
arg_count: mir.arg_count,
};
visitor.visit_mir(mir);
@ -51,8 +51,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
arg_count: usize,
}
@ -74,20 +72,17 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
})
}
/// Renumbers the regions appearing in `value`, but those regions
/// are expected to be free regions from the function signature.
fn renumber_universal_regions<T>(&mut self, value: &T) -> T
/// Checks that all the regions appearing in `value` have already
/// been renumbered. `FreeRegions` code should have done this.
fn assert_free_regions_are_renumbered<T>(&self, value: &T)
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_universal_regions(value={:?})", value);
debug!("assert_free_regions_are_renumbered(value={:?})", value);
self.infcx
.tcx
.fold_regions(value, &mut false, |region, _depth| {
let index = self.universal_regions.indices[&region];
self.free_region_inference_vars[index]
})
self.infcx.tcx.for_each_free_region(value, |region| {
region.to_region_vid(); // will panic if `region` is not renumbered
});
}
fn is_argument_or_return_slot(&self, local: Local) -> bool {
@ -110,12 +105,12 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
ty_context
);
let old_ty = *ty;
*ty = if is_arg {
self.renumber_universal_regions(&old_ty)
if is_arg {
self.assert_free_regions_are_renumbered(ty);
} else {
self.renumber_regions(ty_context, &old_ty)
};
*ty = self.renumber_regions(ty_context, ty);
}
debug!("visit_ty: ty={:?}", ty);
}
@ -138,6 +133,11 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
debug!("visit_region: region={:?}", region);
}
fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, location: Location) {
let ty_context = TyContext::Location(location);
*constant = self.renumber_regions(ty_context, &*constant);
}
fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
debug!(
"visit_closure_substs(substs={:?}, location={:?})",

View File

@ -15,7 +15,6 @@ use rustc::ty;
use transform::type_check::MirTypeckRegionConstraints;
use transform::type_check::OutlivesSet;
use super::universal_regions::UniversalRegions;
use super::region_infer::RegionInferenceContext;
/// When the MIR type-checker executes, it validates all the types in
@ -25,20 +24,17 @@ use super::region_infer::RegionInferenceContext;
/// them into the NLL `RegionInferenceContext`.
pub(super) fn generate<'tcx>(
regioncx: &mut RegionInferenceContext<'tcx>,
universal_regions: &UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
) {
SubtypeConstraintGenerator {
regioncx,
universal_regions,
mir,
}.generate(constraints);
}
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
regioncx: &'cx mut RegionInferenceContext<'tcx>,
universal_regions: &'cx UniversalRegions<'tcx>,
mir: &'cx Mir<'tcx>,
}
@ -106,7 +102,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
if let ty::ReVar(vid) = r {
*vid
} else {
self.universal_regions.indices[&r]
self.regioncx.to_region_vid(r)
}
}
}

View File

@ -22,69 +22,681 @@
//! The code in this file doesn't *do anything* with those results; it
//! just returns them for other code to use.
use rustc::hir::HirId;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::infer::outlives::free_region_map::FreeRegionMap;
use rustc::ty::{self, RegionVid};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::outlives::bounds::{self, OutlivesBound};
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use std::iter;
use syntax::ast;
use super::ToRegionVid;
#[derive(Debug)]
pub struct UniversalRegions<'tcx> {
/// Given a universally quantified region defined on this function
/// (either early- or late-bound), this maps it to its internal
/// region index. When the region context is created, the first N
/// variables will be created based on these indices.
pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
indices: UniversalRegionIndices<'tcx>,
/// The map from the typeck tables telling us how to relate universal regions.
pub free_region_map: &'tcx FreeRegionMap<'tcx>,
/// The vid assigned to `'static`
pub fr_static: RegionVid,
/// We create region variables such that they are ordered by their
/// `RegionClassification`. The first block are globals, then
/// externals, then locals. So things from:
/// - `FIRST_GLOBAL_INDEX..first_extern_index` are global;
/// - `first_extern_index..first_local_index` are external; and
/// - first_local_index..num_universals` are local.
first_extern_index: usize,
/// See `first_extern_index`.
first_local_index: usize,
/// The total number of universal region variables instantiated.
num_universals: usize,
/// The "defining" type for this function, with all universal
/// regions instantiated. For a closure or generator, this is the
/// closure type, but for a top-level function it's the `TyFnDef`.
pub defining_ty: Ty<'tcx>,
/// The return type of this function, with all regions replaced
/// by their universal `RegionVid` equivalents.
pub output_ty: Ty<'tcx>,
/// The fully liberated input types of this function, with all
/// regions replaced by their universal `RegionVid` equivalents.
pub input_tys: &'tcx [Ty<'tcx>],
/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
/// be true. These encode relationships like `T: 'a` that are
/// added via implicit bounds.
///
/// Each region here is guaranteed to be a key in the `indices`
/// map. We use the "original" regions (i.e., the keys from the
/// map, and not the values) because the code in
/// `process_registered_region_obligations` has some special-cased
/// logic expecting to see (e.g.) `ReStatic`, and if we supplied
/// our special inference variable there, we would mess that up.
pub region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
relations: UniversalRegionRelations,
}
pub fn universal_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
item_def_id: DefId,
) -> UniversalRegions<'tcx> {
debug!("universal_regions(item_def_id={:?})", item_def_id);
#[derive(Debug)]
struct UniversalRegionIndices<'tcx> {
/// For those regions that may appear in the parameter environment
/// ('static and early-bound regions), we maintain a map from the
/// `ty::Region` to the internal `RegionVid` we are using. This is
/// used because trait matching and type-checking will feed us
/// region constraints that reference those regions and we need to
/// be able to map them our internal `RegionVid`. This is
/// basically equivalent to a `Substs`, except that it also
/// contains an entry for `ReStatic` -- it might be nice to just
/// use a substs, and then handle `ReStatic` another way.
indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
}
let mut indices = FxHashMap();
#[derive(Debug)]
struct UniversalRegionRelations {
/// Stores the outlives relations that are known to hold from the
/// implied bounds, in-scope where clauses, and that sort of
/// thing.
outlives: TransitiveRelation<RegionVid>,
// `'static` is always free.
insert_free_region(&mut indices, infcx.tcx.types.re_static);
/// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
/// and we store that here. This is useful when figuring out how
/// to express some local region in terms of external regions our
/// caller will understand.
inverse_outlives: TransitiveRelation<RegionVid>,
}
// Extract the early regions.
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
for item_subst in item_substs {
if let Some(region) = item_subst.as_region() {
insert_free_region(&mut indices, region);
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum RegionClassification {
/// A **global** region is one that can be named from
/// anywhere. There is only one, `'static`.
Global,
/// An **external** region is only relevant for closures. In that
/// case, it refers to regions that are free in the closure type
/// -- basically, something bound in the surrounding context.
///
/// Consider this example:
///
/// ```
/// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) {
/// let closure = for<'x> |x: &'x u32| { .. };
/// ^^^^^^^ pretend this were legal syntax
/// for declaring a late-bound region in
/// a closure signature
/// }
/// ```
///
/// Here, the lifetimes `'a` and `'b` would be **external** to the
/// closure.
///
/// If we are not analyzing a closure, there are no external
/// lifetimes.
External,
/// A **local** lifetime is one about which we know the full set
/// of relevant constraints (that is, relationships to other named
/// regions). For a closure, this includes any region bound in
/// the closure's signature. For a fn item, this includes all
/// regions other than global ones.
///
/// Continuing with the example from `External`, if we were
/// analyzing the closure, then `'x` would be local (and `'a` and
/// `'b` are external). If we are analyzing the function item
/// `foo`, then `'a` and `'b` are local (and `'x` is not in
/// scope).
Local,
}
const FIRST_GLOBAL_INDEX: usize = 0;
impl<'tcx> UniversalRegions<'tcx> {
/// Creates a new and fully initialized `UniversalRegions` that
/// contains indices for all the free regions found in the given
/// MIR -- that is, all the regions that appear in the function's
/// signature. This will also compute the relationships that are
/// known between those regions.
pub fn new(
infcx: &InferCtxt<'_, '_, 'tcx>,
mir_def_id: DefId,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
let tcx = infcx.tcx;
let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).unwrap();
let mir_hir_id = tcx.hir.node_to_hir_id(mir_node_id);
UniversalRegionsBuilder {
infcx,
mir_def_id,
mir_node_id,
mir_hir_id,
param_env,
region_bound_pairs: vec![],
relations: UniversalRegionRelations {
outlives: TransitiveRelation::new(),
inverse_outlives: TransitiveRelation::new(),
},
}.build()
}
/// Given a reference to a closure type, extracts all the values
/// from its free regions and returns a vector with them. This is
/// used when the closure's creator checks that the
/// `ClosureRegionRequirements` are met. The requirements from
/// `ClosureRegionRequirements` are expressed in terms of
/// `RegionVid` entries that map into the returned vector `V`: so
/// if the `ClosureRegionRequirements` contains something like
/// `'1: '2`, then the caller would impose the constraint that
/// `V[1]: V[2]`.
pub fn closure_mapping(
infcx: &InferCtxt<'_, '_, 'tcx>,
closure_ty: Ty<'tcx>,
expected_num_vars: usize,
) -> IndexVec<RegionVid, ty::Region<'tcx>> {
let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
region_mapping.push(infcx.tcx.types.re_static);
infcx.tcx.for_each_free_region(&closure_ty, |fr| {
region_mapping.push(fr);
});
assert_eq!(
region_mapping.len(),
expected_num_vars,
"index vec had unexpected number of variables"
);
region_mapping
}
/// True if `r` is a member of this set of universal regions.
pub fn is_universal_region(&self, r: RegionVid) -> bool {
(FIRST_GLOBAL_INDEX..self.num_universals).contains(r.index())
}
/// Classifies `r` as a universal region, returning `None` if this
/// is not a member of this set of universal regions.
pub fn region_classification(&self, r: RegionVid) -> Option<RegionClassification> {
let index = r.index();
if (FIRST_GLOBAL_INDEX..self.first_extern_index).contains(index) {
Some(RegionClassification::Global)
} else if (self.first_extern_index..self.first_local_index).contains(index) {
Some(RegionClassification::External)
} else if (self.first_local_index..self.num_universals).contains(index) {
Some(RegionClassification::Local)
} else {
None
}
}
// Extract the late-bound regions. Use the liberated fn sigs,
// where the late-bound regions will have been converted into free
// regions, and add them to the map.
let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
let tables = infcx.tcx.typeck_tables_of(item_def_id);
let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
infcx
.tcx
.for_each_free_region(&fn_sig.inputs_and_output, |region| {
if let ty::ReFree(_) = *region {
insert_free_region(&mut indices, region);
/// Returns an iterator over all the RegionVids corresponding to
/// universally quantified free regions.
pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
(FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
}
/// True if `r` is classied as a global region.
pub fn is_global_free_region(&self, r: RegionVid) -> bool {
self.region_classification(r) == Some(RegionClassification::Global)
}
/// True if `r` is classied as an external region.
pub fn is_extern_free_region(&self, r: RegionVid) -> bool {
self.region_classification(r) == Some(RegionClassification::External)
}
/// True if `r` is classied as an local region.
pub fn is_local_free_region(&self, r: RegionVid) -> bool {
self.region_classification(r) == Some(RegionClassification::Local)
}
/// Returns the number of universal regions created in any category.
pub fn len(&self) -> usize {
self.num_universals
}
/// Finds an "upper bound" for `fr` that is not local. In other
/// words, returns the smallest (*) known region `fr1` that (a)
/// outlives `fr` and (b) is not local. This cannot fail, because
/// we will always find `'static` at worst.
///
/// (*) If there are multiple competing choices, we pick the "postdominating"
/// one. See `TransitiveRelation::postdom_upper_bound` for details.
pub fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
debug!("non_local_upper_bound(fr={:?})", fr);
self.non_local_bound(&self.relations.inverse_outlives, fr)
.unwrap_or(self.fr_static)
}
/// Finds a "lower bound" for `fr` that is not local. In other
/// words, returns the largest (*) known region `fr1` that (a) is
/// outlived by `fr` and (b) is not local. This cannot fail,
/// because we will always find `'static` at worst.
///
/// (*) If there are multiple competing choices, we pick the "postdominating"
/// one. See `TransitiveRelation::postdom_upper_bound` for details.
pub fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
debug!("non_local_lower_bound(fr={:?})", fr);
self.non_local_bound(&self.relations.outlives, fr)
}
/// Returns the number of global plus external universal regions.
/// For closures, these are the regions that appear free in the
/// closure type (versus those bound in the closure
/// signature). They are therefore the regions between which the
/// closure may impose constraints that its creator must verify.
pub fn num_global_and_external_regions(&self) -> usize {
self.first_local_index
}
/// Helper for `non_local_upper_bound` and
/// `non_local_lower_bound`. Repeatedly invokes `postdom_parent`
/// until we find something that is not local. Returns None if we
/// never do so.
fn non_local_bound(
&self,
relation: &TransitiveRelation<RegionVid>,
fr0: RegionVid,
) -> Option<RegionVid> {
let mut external_parents = vec![];
let mut queue = vec![&fr0];
// Keep expanding `fr` into its parents until we reach
// non-local regions.
while let Some(fr) = queue.pop() {
if !self.is_local_free_region(*fr) {
external_parents.push(fr);
continue;
}
});
debug!("universal_regions: indices={:#?}", indices);
queue.extend(relation.parents(fr));
}
UniversalRegions { indices, free_region_map: &tables.free_region_map }
debug!("non_local_bound: external_parents={:?}", external_parents);
// In case we find more than one, reduce to one for
// convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = relation
.mutual_immediate_postdominator(external_parents)
.cloned();
debug!("non_local_bound: post_dom={:?}", post_dom);
post_dom.and_then(|post_dom| {
// If the mutual immediate postdom is not local, then
// there is no non-local result we can return.
if !self.is_local_free_region(post_dom) {
Some(post_dom)
} else {
None
}
})
}
/// True if fr1 is known to outlive fr2.
///
/// This will only ever be true for universally quantified regions.
pub fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
self.relations.outlives.contains(&fr1, &fr2)
}
/// Returns a vector of free regions `x` such that `fr1: x` is
/// known to hold.
pub fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
self.relations.outlives.reachable_from(&fr1)
}
/// Get an iterator over all the early-bound regions that have names.
pub fn named_universal_regions<'s>(
&'s self,
) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
self.indices.indices.iter().map(|(&r, &v)| (r, v))
}
/// See `UniversalRegionIndices::to_region_vid`.
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
self.indices.to_region_vid(r)
}
}
fn insert_free_region<'tcx>(
universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
region: ty::Region<'tcx>,
) {
let next = RegionVid::new(universal_regions.len());
universal_regions.entry(region).or_insert(next);
struct UniversalRegionsBuilder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
mir_def_id: DefId,
mir_hir_id: HirId,
mir_node_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
relations: UniversalRegionRelations,
}
const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion;
impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
fn build(mut self) -> UniversalRegions<'tcx> {
debug!("build(mir_def_id={:?})", self.mir_def_id);
let param_env = self.param_env;
debug!("build: param_env={:?}", param_env);
assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
// Create the "global" region that is always free in all contexts: 'static.
let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
// We've now added all the global regions. The next ones we
// add will be external.
let first_extern_index = self.infcx.num_region_vars();
let defining_ty = self.defining_ty();
debug!("build: defining_ty={:?}", defining_ty);
let indices = self.compute_indices(fr_static, defining_ty);
debug!("build: indices={:?}", indices);
let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
// "Liberate" the late-bound regions. These correspond to
// "local" free regions.
let first_local_index = self.infcx.num_region_vars();
let inputs_and_output = self.infcx
.replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output);
let num_universals = self.infcx.num_region_vars();
// Insert the facts we know from the predicates. Why? Why not.
self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
// Add the implied bounds from inputs and outputs.
for ty in inputs_and_output {
debug!("build: input_or_output={:?}", ty);
self.add_implied_bounds(&indices, ty);
}
// Finally, outlives is reflexive, and static outlives every
// other free region.
for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
debug!("build: relating free region {:?} to itself and to 'static", fr);
self.relations.relate_universal_regions(fr, fr);
self.relations.relate_universal_regions(fr_static, fr);
}
let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
// we should not have created any more variables
assert_eq!(self.infcx.num_region_vars(), num_universals);
debug!("build: global regions = {}..{}",
FIRST_GLOBAL_INDEX,
first_extern_index);
debug!("build: extern regions = {}..{}",
first_extern_index,
first_local_index);
debug!("build: local regions = {}..{}",
first_local_index,
num_universals);
UniversalRegions {
indices,
fr_static,
first_extern_index,
first_local_index,
num_universals,
defining_ty,
output_ty,
input_tys,
region_bound_pairs: self.region_bound_pairs,
relations: self.relations,
}
}
fn defining_ty(&self) -> ty::Ty<'tcx> {
let tcx = self.infcx.tcx;
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
let defining_ty = if self.mir_def_id == closure_base_def_id {
tcx.type_of(closure_base_def_id)
} else {
let tables = tcx.typeck_tables_of(self.mir_def_id);
tables.node_id_to_type(self.mir_hir_id)
};
self.infcx
.replace_free_regions_with_nll_infer_vars(FR, &defining_ty)
}
fn compute_indices(
&self,
fr_static: RegionVid,
defining_ty: Ty<'tcx>,
) -> UniversalRegionIndices<'tcx> {
let tcx = self.infcx.tcx;
let gcx = tcx.global_tcx();
let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id);
let fr_substs = match defining_ty.sty {
ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => {
// In the case of closures, we rely on the fact that
// the first N elements in the ClosureSubsts are
// inherited from the `closure_base_def_id`.
// Therefore, when we zip together (below) with
// `identity_substs`, we will get only those regions
// that correspond to early-bound regions declared on
// the `closure_base_def_id`.
assert!(substs.substs.len() >= identity_substs.len());
substs.substs
}
ty::TyFnDef(_, substs) => substs,
_ => bug!(),
};
let global_mapping = iter::once((gcx.types.re_static, fr_static));
let subst_mapping = identity_substs
.regions()
.zip(fr_substs.regions().map(|r| r.to_region_vid()));
UniversalRegionIndices {
indices: global_mapping.chain(subst_mapping).collect(),
}
}
fn compute_inputs_and_output(
&self,
indices: &UniversalRegionIndices<'tcx>,
defining_ty: Ty<'tcx>,
) -> ty::Binder<&'tcx ty::Slice<Ty<'tcx>>> {
let tcx = self.infcx.tcx;
match defining_ty.sty {
ty::TyClosure(def_id, substs) => {
assert_eq!(self.mir_def_id, def_id);
let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx);
let inputs_and_output = closure_sig.inputs_and_output();
let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap();
ty::Binder::fuse(
closure_ty,
inputs_and_output,
|closure_ty, inputs_and_output| {
// The "inputs" of the closure in the
// signature appear as a tuple. The MIR side
// flattens this tuple.
let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
let inputs = match tuplized_inputs[0].sty {
ty::TyTuple(inputs, _) => inputs,
_ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
};
tcx.mk_type_list(
iter::once(closure_ty)
.chain(inputs.iter().cloned())
.chain(iter::once(output)),
)
},
)
}
ty::TyGenerator(def_id, substs, ..) => {
assert_eq!(self.mir_def_id, def_id);
let output = substs.generator_return_ty(def_id, tcx);
let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]);
ty::Binder::dummy(inputs_and_output)
}
ty::TyFnDef(def_id, _) => {
let sig = tcx.fn_sig(def_id);
let sig = indices.fold_to_region_vids(tcx, &sig);
return sig.inputs_and_output();
}
_ => span_bug!(
tcx.def_span(self.mir_def_id),
"unexpected defining type: {:?}",
defining_ty
),
}
}
/// Update the type of a single local, which should represent
/// either the return type of the MIR or one of its arguments. At
/// the same time, compute and add any implied bounds that come
/// from this local.
///
/// Assumes that `universal_regions` indices map is fully constructed.
fn add_implied_bounds(&mut self, indices: &UniversalRegionIndices<'tcx>, ty: Ty<'tcx>) {
debug!("add_implied_bounds(ty={:?})", ty);
let span = self.infcx.tcx.def_span(self.mir_def_id);
let bounds = self.infcx
.implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span);
self.add_outlives_bounds(indices, bounds);
}
/// Registers the `OutlivesBound` items from `outlives_bounds` in
/// the outlives relation as well as the region-bound pairs
/// listing.
fn add_outlives_bounds<I>(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I)
where
I: IntoIterator<Item = OutlivesBound<'tcx>>,
{
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds(bound={:?})", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r1, r2) => {
// The bound says that `r1 <= r2`; we store `r2: r1`.
let r1 = indices.to_region_vid(r1);
let r2 = indices.to_region_vid(r2);
self.relations.relate_universal_regions(r2, r1);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Param(param_b)));
}
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
.push((r_a, GenericKind::Projection(projection_b)));
}
}
}
}
}
impl UniversalRegionRelations {
/// Records in the `outlives_relation` (and
/// `inverse_outlives_relation`) that `fr_a: fr_b`.
fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
debug!(
"relate_universal_regions: fr_a={:?} outlives fr_b={:?}",
fr_a,
fr_b
);
self.outlives.add(fr_a, fr_b);
self.inverse_outlives.add(fr_b, fr_a);
}
}
pub(crate) trait InferCtxtExt<'tcx> {
fn replace_free_regions_with_nll_infer_vars<T>(
&self,
origin: NLLRegionVariableOrigin,
value: &T,
) -> T
where
T: TypeFoldable<'tcx>;
fn replace_bound_regions_with_nll_infer_vars<T>(
&self,
origin: NLLRegionVariableOrigin,
value: &ty::Binder<T>,
) -> T
where
T: TypeFoldable<'tcx>;
}
impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> {
fn replace_free_regions_with_nll_infer_vars<T>(
&self,
origin: NLLRegionVariableOrigin,
value: &T,
) -> T
where
T: TypeFoldable<'tcx>,
{
self.tcx.fold_regions(
value,
&mut false,
|_region, _depth| self.next_nll_region_var(origin),
)
}
fn replace_bound_regions_with_nll_infer_vars<T>(
&self,
origin: NLLRegionVariableOrigin,
value: &ty::Binder<T>,
) -> T
where
T: TypeFoldable<'tcx>,
{
let (value, _map) = self.tcx
.replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin));
value
}
}
impl<'tcx> UniversalRegionIndices<'tcx> {
/// Converts `r` into a local inference variable: `r` can either
/// by a `ReVar` (i.e., already a reference to an inference
/// variable) or it can be `'static` or some early-bound
/// region. This is useful when taking the results from
/// type-checking and trait-matching, which may sometimes
/// reference those regions from the `ParamEnv`. It is also used
/// during initialization. Relies on the `indices` map having been
/// fully initialized.
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
match r {
ty::ReEarlyBound(..) | ty::ReStatic => *self.indices.get(&r).unwrap(),
ty::ReVar(..) => r.to_region_vid(),
_ => bug!("cannot convert `{:?}` to a region vid", r),
}
}
/// Replace all free regions in `value` with region vids, as
/// returned by `to_region_vid`.
pub fn fold_to_region_vids<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: &T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(
value,
&mut false,
|region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))),
)
}
}

View File

@ -51,6 +51,17 @@ impl<'tcx> CFG<'tcx> {
source_info: SourceInfo,
region_scope: region::Scope) {
if tcx.sess.emit_end_regions() {
if let region::ScopeData::CallSite(_) = region_scope.data() {
// The CallSite scope (aka the root scope) is sort of weird, in that it is
// supposed to "separate" the "interior" and "exterior" of a closure. Being
// that, it is not really a part of the region hierarchy, but for some
// reason it *is* considered a part of it.
//
// It should die a hopefully painful death with NLL, so let's leave this hack
// for now so that nobody can complain about soundness.
return
}
self.push(block, Statement {
source_info,
kind: StatementKind::EndRegion(region_scope),

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::region;
use rustc::mir::{self, Location, Mir};
use rustc::mir::visit::Visitor;
use rustc::ty::{self, Region, TyCtxt};
@ -27,6 +30,7 @@ use borrow_check::nll::ToRegionVid;
use syntax_pos::Span;
use std::fmt;
use std::rc::Rc;
// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
// uniquely identified in the MIR by the `Location` of the assigment
@ -34,9 +38,12 @@ use std::fmt;
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
scope_tree: Rc<region::ScopeTree>,
root_scope: Option<region::Scope>,
borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
}
@ -69,22 +76,32 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
def_id: DefId,
body_id: Option<hir::BodyId>)
-> Self {
let scope_tree = tcx.region_scope_tree(def_id);
let root_scope = body_id.map(|body_id| {
region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
});
let mut visitor = GatherBorrows {
tcx,
mir,
idx_vec: IndexVec::new(),
location_map: FxHashMap(),
region_map: FxHashMap(),
local_map: FxHashMap(),
region_span_map: FxHashMap()
};
visitor.visit_mir(mir);
return Borrows { tcx: tcx,
mir: mir,
borrows: visitor.idx_vec,
scope_tree,
root_scope,
location_map: visitor.location_map,
region_map: visitor.region_map,
local_map: visitor.local_map,
region_span_map: visitor.region_span_map,
nonlexical_regioncx };
@ -94,6 +111,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>,
}
@ -101,6 +119,14 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
fn visit_rvalue(&mut self,
rvalue: &mir::Rvalue<'tcx>,
location: mir::Location) {
fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> {
loop { match p {
mir::Place::Projection(pi) => p = &pi.base,
mir::Place::Static(_) => return None,
mir::Place::Local(l) => return Some(*l)
}}
}
if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
if is_unsafe_place(self.tcx, self.mir, place) { return; }
@ -109,8 +135,14 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
};
let idx = self.idx_vec.push(borrow);
self.location_map.insert(location, idx);
let borrows = self.region_map.entry(region).or_insert(FxHashSet());
borrows.insert(idx);
if let Some(local) = root_local(place) {
let borrows = self.local_map.entry(local).or_insert(FxHashSet());
borrows.insert(idx);
}
}
}
@ -199,7 +231,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
mir::StatementKind::EndRegion(region_scope) => {
if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) {
assert!(self.nonlexical_regioncx.is_none());
for idx in borrow_indexes { sets.kill(&idx); }
sets.kill_all(borrow_indexes);
} else {
// (if there is no entry, then there are no borrows to be tracked)
}
@ -224,10 +256,19 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
}
}
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
//
// FIXME: expand this to variables that are assigned over.
if let Some(borrow_indexes) = self.local_map.get(&local) {
sets.kill_all(borrow_indexes);
}
}
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::StorageDead(..) |
mir::StatementKind::Validate(..) |
mir::StatementKind::Nop => {}
@ -253,8 +294,17 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
// like unwind paths, we do not always emit `EndRegion` statements, so we
// add some kills here as a "backup" and to avoid spurious error messages.
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
if let ReScope(..) = borrow_data.region {
sets.kill(&borrow_index);
if let ReScope(scope) = borrow_data.region {
// Check that the scope is not actually a scope from a function that is
// a parent of our closure. Note that the CallSite scope itself is
// *outside* of the closure, for some weird reason.
if let Some(root_scope) = self.root_scope {
if *scope != root_scope &&
self.scope_tree.is_subscope_of(*scope, root_scope)
{
sets.kill(&borrow_index);
}
}
}
}
}

View File

@ -14,7 +14,6 @@
use rustc::ty::TyCtxt;
use rustc::mir::{self, Mir, Location};
use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
use rustc_data_structures::bitslice::{BitwiseOperator};
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::Idx;
@ -504,7 +503,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
let stmt = &mir[location.block].statements[location.statement_index];
let loc_map = &move_data.loc_map;
let path_map = &move_data.path_map;
let bits_per_block = self.bits_per_block();
match stmt.kind {
// this analysis only tries to find moves explicitly
@ -515,21 +513,15 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
_ => {
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
stmt, location, &loc_map[location]);
for move_index in &loc_map[location] {
// Every path deinitialized by a *particular move*
// has corresponding bit, "gen'ed" (i.e. set)
// here, in dataflow vector
zero_to_one(sets.gen_set.words_mut(), *move_index);
}
// Every path deinitialized by a *particular move*
// has corresponding bit, "gen'ed" (i.e. set)
// here, in dataflow vector
sets.gen_all_and_assert_dead(&loc_map[location]);
}
}
for_location_inits(tcx, mir, move_data, location,
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
}
);
|mpi| sets.kill_all(&path_map[mpi]));
}
fn terminator_effect(&self,
@ -543,18 +535,10 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
term, location, &loc_map[location]);
let bits_per_block = self.bits_per_block();
for move_index in &loc_map[location] {
assert!(move_index.index() < bits_per_block);
zero_to_one(sets.gen_set.words_mut(), *move_index);
}
sets.gen_all_and_assert_dead(&loc_map[location]);
for_location_inits(tcx, mir, move_data, location,
|mpi| for moi in &path_map[mpi] {
assert!(moi.index() < bits_per_block);
sets.kill_set.add(&moi);
}
);
|mpi| sets.kill_all(&path_map[mpi]));
}
fn propagate_call_return(&self,
@ -585,11 +569,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
}
fn start_block_effect(&self, sets: &mut BlockSets<InitIndex>) {
let bits_per_block = self.bits_per_block();
for init_index in (0..self.mir.arg_count).map(InitIndex::new) {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(&init_index);
}
sets.gen_all((0..self.mir.arg_count).map(InitIndex::new));
}
fn statement_effect(&self,
sets: &mut BlockSets<InitIndex>,
@ -599,26 +579,39 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
let init_path_map = &move_data.init_path_map;
let init_loc_map = &move_data.init_loc_map;
let rev_lookup = &move_data.rev_lookup;
let bits_per_block = self.bits_per_block();
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
stmt, location, &init_loc_map[location]);
for init_index in &init_loc_map[location] {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(init_index);
}
sets.gen_all(&init_loc_map[location]);
match stmt.kind {
mir::StatementKind::StorageDead(local) => {
// End inits for StorageDead, so that an immutable variable can
// be reinitialized on the next iteration of the loop.
mir::StatementKind::StorageDead(local) |
mir::StatementKind::StorageLive(local) => {
// End inits for StorageDead and StorageLive, so that an immutable
// variable can be reinitialized on the next iteration of the loop.
//
// FIXME(#46525): We *need* to do this for StorageLive as well as
// StorageDead, because lifetimes of match bindings with guards are
// weird - i.e. this code
//
// ```
// fn main() {
// match 0 {
// a | a
// if { println!("a={}", a); false } => {}
// _ => {}
// }
// }
// ```
//
// runs the guard twice, using the same binding for `a`, and only
// storagedeads after everything ends, so if we don't regard the
// storagelive as killing storage, we would have a multiple assignment
// to immutable data error.
if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) {
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
stmt, location, &init_path_map[mpi]);
for ii in &init_path_map[mpi] {
assert!(ii.index() < bits_per_block);
sets.kill_set.add(&ii);
}
stmt, location, &init_path_map[mpi]);
sets.kill_all(&init_path_map[mpi]);
}
}
_ => {}
@ -634,13 +627,11 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
let init_loc_map = &move_data.init_loc_map;
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
term, location, &init_loc_map[location]);
let bits_per_block = self.bits_per_block();
for init_index in &init_loc_map[location] {
if move_data.inits[*init_index].kind != InitKind::NonPanicPathOnly {
assert!(init_index.index() < bits_per_block);
sets.gen_set.add(init_index);
}
}
sets.gen_all(
init_loc_map[location].iter().filter(|init_index| {
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
})
);
}
fn propagate_call_return(&self,
@ -663,11 +654,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
}
}
fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
let retval = bitvec.set_bit(move_index.index());
assert!(retval);
}
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {

View File

@ -18,6 +18,7 @@ use rustc::ty::{self, TyCtxt};
use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
use rustc::session::Session;
use std::borrow::Borrow;
use std::fmt::{self, Debug};
use std::io;
use std::mem;
@ -492,10 +493,39 @@ impl<'a, E:Idx> BlockSets<'a, E> {
self.gen_set.add(e);
self.kill_set.remove(e);
}
fn gen_all<I>(&mut self, i: I)
where I: IntoIterator,
I::Item: Borrow<E>
{
for j in i {
self.gen(j.borrow());
}
}
fn gen_all_and_assert_dead<I>(&mut self, i: I)
where I: IntoIterator,
I::Item: Borrow<E>
{
for j in i {
let j = j.borrow();
let retval = self.gen_set.add(j);
self.kill_set.remove(j);
assert!(retval);
}
}
fn kill(&mut self, e: &E) {
self.gen_set.remove(e);
self.kill_set.add(e);
}
fn kill_all<I>(&mut self, i: I)
where I: IntoIterator,
I::Item: Borrow<E>
{
for j in i {
self.kill(j.borrow());
}
}
}
impl<E:Idx> AllSets<E> {

View File

@ -41,6 +41,9 @@ use transform::{MirPass, MirSource};
use super::promote_consts::{self, Candidate, TempState};
bitflags! {
// Borrows of temporaries can be promoted only if
// they have none of these qualifications, with
// the exception of `STATIC_REF` (in statics only).
struct Qualif: u8 {
// Constant containing interior mutability (UnsafeCell).
const MUTABLE_INTERIOR = 1 << 0;
@ -65,10 +68,6 @@ bitflags! {
// promote_consts decided they weren't simple enough.
const NOT_PROMOTABLE = 1 << 6;
// Borrows of temporaries can be promoted only
// if they have none of the above qualifications.
const NEVER_PROMOTE = 0b111_1111;
// Const items can only have MUTABLE_INTERIOR
// and NOT_PROMOTABLE without producing an error.
const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
@ -197,7 +196,17 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
self.add(original);
}
/// Check if an Place with the current qualifications could
/// Check if a Local with the current qualifications is promotable.
fn can_promote(&mut self) -> bool {
// References to statics are allowed, but only in other statics.
if self.mode == Mode::Static || self.mode == Mode::StaticMut {
(self.qualif - Qualif::STATIC_REF).is_empty()
} else {
self.qualif.is_empty()
}
}
/// Check if a Place with the current qualifications could
/// be consumed, by either an operand or a Deref projection.
fn try_consume(&mut self) -> bool {
if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
@ -633,7 +642,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
// We might have a candidate for promotion.
let candidate = Candidate::Ref(location);
if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
if self.can_promote() {
// We can only promote direct borrows of temps.
if let Place::Local(local) = *place {
if self.mir.local_kind(local) == LocalKind::Temp {
@ -745,7 +754,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
this.visit_operand(arg, location);
if is_shuffle && i == 2 && this.mode == Mode::Fn {
let candidate = Candidate::ShuffleIndices(bb);
if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
if this.can_promote() {
this.promotion_candidates.push(candidate);
} else {
span_err!(this.tcx.sess, this.span, E0526,

View File

@ -11,6 +11,7 @@
//! This pass type-checks the MIR to ensure it is not broken.
#![allow(unreachable_code)]
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::traits::{self, FulfillmentContext};
@ -110,6 +111,7 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
self.sanitize_constant(constant, location);
self.sanitize_type(constant, constant.ty);
}
@ -159,6 +161,52 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
}
}
/// Checks that the constant's `ty` field matches up with what
/// would be expected from its literal.
fn sanitize_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
debug!(
"sanitize_constant(constant={:?}, location={:?})",
constant,
location
);
let expected_ty = match constant.literal {
Literal::Value { value } => value.ty,
Literal::Promoted { .. } => {
// FIXME -- promoted MIR return types reference
// various "free regions" (e.g., scopes and things)
// that they ought not to do. We have to figure out
// how best to handle that -- probably we want treat
// promoted MIR much like closures, renumbering all
// their free regions and propagating constraints
// upwards. We have the same acyclic guarantees, so
// that should be possible. But for now, ignore them.
//
// let promoted_mir = &self.mir.promoted[index];
// promoted_mir.return_ty()
return;
}
};
debug!("sanitize_constant: expected_ty={:?}", expected_ty);
if let Err(terr) = self.cx
.eq_types(expected_ty, constant.ty, location.at_self())
{
span_mirbug!(
self,
constant,
"constant {:?} should have type {:?} but has {:?} ({:?})",
constant,
expected_ty,
constant.ty,
terr,
);
}
}
/// Checks that the types internal to the `place` match up with
/// what would be expected.
fn sanitize_place(
&mut self,
place: &Place<'tcx>,
@ -1088,14 +1136,45 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
operands: &[Operand<'tcx>],
location: Location,
) {
let tcx = self.tcx();
match aggregate_kind {
// tuple rvalue field type is always the type of the op. Nothing to check here.
AggregateKind::Tuple => return,
// For closures, we have some **extra requirements** we
// have to check. In particular, in their upvars and
// signatures, closures often reference various regions
// from the surrounding function -- we call those the
// closure's free regions. When we borrow-check (and hence
// region-check) closures, we may find that the closure
// requires certain relationships between those free
// regions. However, because those free regions refer to
// portions of the CFG of their caller, the closure is not
// in a position to verify those relationships. In that
// case, the requirements get "propagated" to us, and so
// we have to solve them here where we instantiate the
// closure.
//
// Despite the opacity of the previous parapgrah, this is
// actually relatively easy to understand in terms of the
// desugaring. A closure gets desugared to a struct, and
// these extra requirements are basically like where
// clauses on the struct.
AggregateKind::Closure(def_id, substs) => {
if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
closure_region_requirements.apply_requirements(
self.infcx,
location,
*def_id,
*substs,
);
}
}
_ => {}
}
let tcx = self.tcx();
for (i, operand) in operands.iter().enumerate() {
let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
Ok(field_ty) => field_ty,

View File

@ -11,7 +11,8 @@
use rustc::hir;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::mir::*;
use rustc::ty::TyCtxt;
use rustc::mir::visit::Visitor;
use rustc::ty::{self, TyCtxt};
use rustc::ty::item_path;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
@ -56,9 +57,19 @@ pub enum PassWhere {
/// where `<filter>` takes the following forms:
///
/// - `all` -- dump MIR for all fns, all passes, all everything
/// - `substring1&substring2,...` -- `&`-separated list of substrings
/// that can appear in the pass-name or the `item_path_str` for the given
/// node-id. If any one of the substrings match, the data is dumped out.
/// - a filter defined by a set of substrings combined with `&` and `|`
/// (`&` has higher precedence). At least one of the `|`-separated groups
/// must match; an `|`-separated group matches if all of its `&`-separated
/// substrings are matched.
///
/// Example:
///
/// - `nll` == match if `nll` appears in the name
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` appears in the name.
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
/// or `typeck` and `bar` both appear in the name.
pub fn dump_mir<'a, 'gcx, 'tcx, F>(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
pass_num: Option<&Display>,
@ -103,8 +114,10 @@ pub fn dump_enabled<'a, 'gcx, 'tcx>(
// see notes on #41697 below
tcx.item_path_str(source.def_id)
});
filters.split("&").any(|filter| {
filter == "all" || pass_name.contains(filter) || node_path.contains(filter)
filters.split("|").any(|or_filter| {
or_filter.split("&").all(|and_filter| {
and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
})
})
}
@ -125,14 +138,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
F: FnMut(PassWhere, &mut Write) -> io::Result<()>,
{
let _: io::Result<()> = do catch {
let mut file = create_dump_file(
tcx,
"mir",
pass_num,
pass_name,
disambiguator,
source,
)?;
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
writeln!(file, "// MIR for `{}`", node_path)?;
writeln!(file, "// source = {:?}", source)?;
writeln!(file, "// pass_name = {}", pass_name)?;
@ -148,15 +154,9 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>(
};
if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
let _: io::Result<()> = do catch {
let mut file = create_dump_file(
tcx,
"dot",
pass_num,
pass_name,
disambiguator,
source,
)?;
let _: io::Result<()> = do catch {
let mut file =
create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?;
write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?;
Ok(())
};
@ -297,10 +297,10 @@ where
}
/// Write out a human-readable textual representation for the given basic block.
pub fn write_basic_block<F>(
tcx: TyCtxt,
pub fn write_basic_block<'cx, 'gcx, 'tcx, F>(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
block: BasicBlock,
mir: &Mir,
mir: &Mir<'tcx>,
extra_data: &mut F,
w: &mut Write,
) -> io::Result<()>
@ -330,6 +330,11 @@ where
comment(tcx, statement.source_info),
A = ALIGN,
)?;
write_extra(tcx, w, |visitor| {
visitor.visit_statement(current_location.block, statement, current_location);
})?;
extra_data(PassWhere::AfterLocation(current_location), w)?;
current_location.statement_index += 1;
@ -346,11 +351,94 @@ where
comment(tcx, data.terminator().source_info),
A = ALIGN,
)?;
write_extra(tcx, w, |visitor| {
visitor.visit_terminator(current_location.block, data.terminator(), current_location);
})?;
extra_data(PassWhere::AfterLocation(current_location), w)?;
writeln!(w, "{}}}", INDENT)
}
/// After we print the main statement, we sometimes dump extra
/// information. There's often a lot of little things "nuzzled up" in
/// a statement.
fn write_extra<'cx, 'gcx, 'tcx, F>(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
write: &mut Write,
mut visit_op: F,
) -> io::Result<()>
where
F: FnMut(&mut ExtraComments<'cx, 'gcx, 'tcx>),
{
let mut extra_comments = ExtraComments {
_tcx: tcx,
comments: vec![],
};
visit_op(&mut extra_comments);
for comment in extra_comments.comments {
writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
}
Ok(())
}
struct ExtraComments<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
_tcx: TyCtxt<'cx, 'gcx, 'tcx>, // don't need it now, but bet we will soon
comments: Vec<String>,
}
impl<'cx, 'gcx, 'tcx> ExtraComments<'cx, 'gcx, 'tcx> {
fn push(&mut self, lines: &str) {
for line in lines.split("\n") {
self.comments.push(line.to_string());
}
}
}
impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ExtraComments<'cx, 'gcx, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
let Constant { span, ty, literal } = constant;
self.push(&format!("mir::Constant"));
self.push(&format!("└ span: {:?}", span));
self.push(&format!("└ ty: {:?}", ty));
self.push(&format!("└ literal: {:?}", literal));
}
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
self.super_const(constant);
let ty::Const { ty, val } = constant;
self.push(&format!("ty::Const"));
self.push(&format!("└ ty: {:?}", ty));
self.push(&format!("└ val: {:?}", val));
}
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location);
match rvalue {
Rvalue::Aggregate(kind, _) => match **kind {
AggregateKind::Closure(def_id, substs) => {
self.push(&format!("closure"));
self.push(&format!("└ def_id: {:?}", def_id));
self.push(&format!("└ substs: {:#?}", substs));
}
AggregateKind::Generator(def_id, substs, interior) => {
self.push(&format!("generator"));
self.push(&format!("└ def_id: {:?}", def_id));
self.push(&format!("└ substs: {:#?}", substs));
self.push(&format!("└ interior: {:?}", interior));
}
_ => {}
},
_ => {}
}
}
}
fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
format!(
"scope {} at {}",

View File

@ -21,6 +21,7 @@ use rustc::session::Session;
use syntax::ast::*;
use syntax::attr;
use syntax::codemap::Spanned;
use syntax::parse::token;
use syntax::symbol::keywords;
use syntax::visit::{self, Visitor};
use syntax_pos::Span;
@ -35,8 +36,16 @@ impl<'a> AstValidator<'a> {
&self.session.parse_sess.span_diagnostic
}
fn check_lifetime(&self, lifetime: &Lifetime) {
let valid_names = [keywords::StaticLifetime.name(), keywords::Invalid.name()];
if !valid_names.contains(&lifetime.ident.name) &&
token::Ident(lifetime.ident.without_first_quote()).is_reserved_ident() {
self.err_handler().span_err(lifetime.span, "lifetimes cannot use keyword names");
}
}
fn check_label(&self, label: Ident, span: Span) {
if label.name == keywords::StaticLifetime.name() || label.name == "'_" {
if token::Ident(label.without_first_quote()).is_reserved_ident() || label.name == "'_" {
self.err_handler().span_err(span, &format!("invalid label name `{}`", label.name));
}
}
@ -200,6 +209,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_use_tree(self, use_tree, id);
}
fn visit_lifetime(&mut self, lifetime: &'a Lifetime) {
self.check_lifetime(lifetime);
visit::walk_lifetime(self, lifetime);
}
fn visit_item(&mut self, item: &'a Item) {
match item.node {
ItemKind::Impl(.., Some(..), _, ref impl_items) => {

View File

@ -21,6 +21,7 @@ use rustc::ty;
use rustc::lint::builtin::PUB_USE_OF_PRIVATE_EXTERN_CRATE;
use rustc::hir::def_id::DefId;
use rustc::hir::def::*;
use rustc::session::DiagnosticMessageId;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use syntax::ast::{Ident, Name, SpannedIdent, NodeId};
@ -72,7 +73,7 @@ impl<'a> ImportDirective<'a> {
}
}
#[derive(Clone, Default)]
#[derive(Clone, Default, Debug)]
/// Records information about the resolution of a name in a namespace of a module.
pub struct NameResolution<'a> {
/// The single imports that define the name in the namespace.
@ -867,12 +868,59 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
}
match binding.kind {
NameBindingKind::Import { binding: orig_binding, .. } => {
NameBindingKind::Import { binding: orig_binding, directive, .. } => {
if ns == TypeNS && orig_binding.is_variant() &&
!orig_binding.vis.is_at_least(binding.vis, &*self) {
let msg = format!("variant `{}` is private, and cannot be reexported, \
consider declaring its enum as `pub`", ident);
self.session.span_err(binding.span, &msg);
!orig_binding.vis.is_at_least(binding.vis, &*self) {
let msg = match directive.subclass {
ImportDirectiveSubclass::SingleImport { .. } => {
format!("variant `{}` is private and cannot be reexported",
ident)
},
ImportDirectiveSubclass::GlobImport { .. } => {
let msg = "enum is private and its variants \
cannot be reexported".to_owned();
let error_id = (DiagnosticMessageId::ErrorId(0), // no code?!
Some(binding.span),
msg.clone());
let fresh = self.session.one_time_diagnostics
.borrow_mut().insert(error_id);
if !fresh {
continue;
}
msg
},
ref s @ _ => bug!("unexpected import subclass {:?}", s)
};
let mut err = self.session.struct_span_err(binding.span, &msg);
let imported_module = directive.imported_module.get()
.expect("module should exist");
let resolutions = imported_module.parent.expect("parent should exist")
.resolutions.borrow();
let enum_path_segment_index = directive.module_path.len() - 1;
let enum_ident = directive.module_path[enum_path_segment_index].node;
let enum_resolution = resolutions.get(&(enum_ident, TypeNS))
.expect("resolution should exist");
let enum_span = enum_resolution.borrow()
.binding.expect("binding should exist")
.span;
let enum_def_span = self.session.codemap().def_span(enum_span);
let enum_def_snippet = self.session.codemap()
.span_to_snippet(enum_def_span).expect("snippet should exist");
// potentially need to strip extant `crate`/`pub(path)` for suggestion
let after_vis_index = enum_def_snippet.find("enum")
.expect("`enum` keyword should exist in snippet");
let suggestion = format!("pub {}",
&enum_def_snippet[after_vis_index..]);
self.session
.diag_span_suggestion_once(&mut err,
DiagnosticMessageId::ErrorId(0),
enum_def_span,
"consider making the enum public",
suggestion);
err.emit();
}
}
NameBindingKind::Ambiguity { b1, b2, .. }

View File

@ -120,10 +120,7 @@ impl<'a> ArchiveBuilder<'a> {
if let Some(ref a) = self.src_archive {
return a.as_ref()
}
let src = match self.config.src {
Some(ref src) => src,
None => return None,
};
let src = self.config.src.as_ref()?;
self.src_archive = Some(ArchiveRO::open(src).ok());
self.src_archive.as_ref().unwrap().as_ref()
}

View File

@ -29,7 +29,7 @@ use value::Value;
use rustc::traits;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{HasDataLayout, LayoutOf};
use rustc::ty::subst::{Kind, Subst, Substs};
use rustc::ty::subst::{Kind, Substs};
use rustc::hir;
use libc::{c_uint, c_char};
@ -393,7 +393,7 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
ty::TyFnPtr(_) => ty.fn_sig(ccx.tcx()),
ty::TyClosure(def_id, substs) => {
let tcx = ccx.tcx();
let sig = tcx.fn_sig(def_id).subst(tcx, substs.substs);
let sig = substs.closure_sig(def_id, tcx);
let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
sig.map_bound(|sig| tcx.mk_fn_sig(

View File

@ -74,7 +74,7 @@ pub fn addr_of_mut(ccx: &CrateContext,
});
llvm::LLVMSetInitializer(gv, cv);
set_global_alignment(ccx, gv, align);
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::InternalLinkage);
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
SetUnnamedAddr(gv, true);
gv
}

View File

@ -20,7 +20,7 @@ use rustc::mir::tcx::PlaceTy;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::layout::{self, LayoutOf, Size};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::{Kind, Substs, Subst};
use rustc::ty::subst::{Kind, Substs};
use rustc_apfloat::{ieee, Float, Status};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use base;
@ -658,8 +658,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
.find(|it| it.kind == ty::AssociatedKind::Method)
.unwrap().def_id;
// Now create its substs [Closure, Tuple]
let input = tcx.fn_sig(def_id)
.subst(tcx, substs.substs).input(0);
let input = substs.closure_sig(def_id, tcx).input(0);
let input = tcx.erase_late_bound_regions_and_normalize(&input);
let substs = tcx.mk_substs([operand.ty, input]
.iter().cloned().map(Kind::from));

View File

@ -359,10 +359,10 @@ impl<'a, 'tcx> PlaceRef<'tcx> {
/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn trans_set_discr(&self, bcx: &Builder<'a, 'tcx>, variant_index: usize) {
if self.layout.for_variant(bcx.ccx, variant_index).abi == layout::Abi::Uninhabited {
return;
}
match self.layout.variants {
if self.layout.for_variant(bcx.ccx, variant_index).abi == layout::Abi::Uninhabited {
return;
}
match self.layout.variants {
layout::Variants::Single { index } => {
assert_eq!(index, variant_index);
}

View File

@ -12,7 +12,7 @@ use rustc::hir::def_id::DefId;
use rustc::middle::lang_items::DropInPlaceFnLangItem;
use rustc::traits;
use rustc::ty::adjustment::CustomCoerceUnsized;
use rustc::ty::subst::{Kind, Subst};
use rustc::ty::subst::Kind;
use rustc::ty::{self, Ty, TyCtxt};
pub use rustc::ty::Instance;
@ -34,7 +34,7 @@ fn fn_once_adapter_instance<'a, 'tcx>(
let self_ty = tcx.mk_closure_from_closure_substs(
closure_did, substs);
let sig = tcx.fn_sig(closure_did).subst(tcx, substs.substs);
let sig = substs.closure_sig(closure_did, tcx);
let sig = tcx.erase_late_bound_regions_and_normalize(&sig);
assert_eq!(sig.inputs().len(), 1);
let substs = tcx.mk_substs([

View File

@ -79,10 +79,8 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(false, NoPreference) {
(AutoderefKind::Builtin, mt.ty)
} else {
match self.overloaded_deref_ty(self.cur_ty) {
Some(ty) => (AutoderefKind::Overloaded, ty),
_ => return None,
}
let ty = self.overloaded_deref_ty(self.cur_ty)?;
(AutoderefKind::Overloaded, ty)
};
if new_ty.references_error() {
@ -108,10 +106,7 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
// <cur_ty as Deref>
let trait_ref = TraitRef {
def_id: match tcx.lang_items().deref_trait() {
Some(f) => f,
None => return None,
},
def_id: tcx.lang_items().deref_trait()?,
substs: tcx.mk_substs_trait(self.cur_ty, &[]),
};

View File

@ -16,7 +16,6 @@ use hir::def::Def;
use hir::def_id::{DefId, LOCAL_CRATE};
use rustc::{infer, traits};
use rustc::ty::{self, TyCtxt, TypeFoldable, LvaluePreference, Ty};
use rustc::ty::subst::Subst;
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
use syntax::abi;
use syntax::symbol::Symbol;
@ -109,7 +108,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// haven't yet decided on whether the closure is fn vs
// fnmut vs fnonce. If so, we have to defer further processing.
if self.closure_kind(def_id, substs).is_none() {
let closure_ty = self.fn_sig(def_id).subst(self.tcx, substs.substs);
let closure_ty = self.closure_sig(def_id, substs);
let fn_sig = self.replace_late_bound_regions_with_fresh_var(call_expr.span,
infer::FnCall,
&closure_ty)

View File

@ -74,7 +74,6 @@ use rustc::ty::{self, LvaluePreference, TypeAndMut,
use rustc::ty::fold::TypeFoldable;
use rustc::ty::error::TypeError;
use rustc::ty::relate::RelateResult;
use rustc::ty::subst::Subst;
use errors::DiagnosticBuilder;
use syntax::abi;
use syntax::feature_gate;
@ -670,7 +669,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
// `fn(arg0,arg1,...) -> _`
let sig = self.fn_sig(def_id_a).subst(self.tcx, substs_a.substs);
let sig = self.closure_sig(def_id_a, substs_a);
let converted_sig = sig.map_bound(|s| {
let params_iter = match s.inputs()[0].sty {
ty::TyTuple(params, _) => {

View File

@ -4194,7 +4194,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// type of the lvalue it is referencing, and not some
// supertype thereof.
let init_ty = self.check_expr_with_lvalue_pref(init, LvaluePreference::from_mutbl(m));
self.demand_eqtype(init.span, init_ty, local_ty);
self.demand_eqtype(init.span, local_ty, init_ty);
init_ty
} else {
self.check_expr_coercable_to_type(init, local_ty)

View File

@ -12,9 +12,11 @@ use namespace::Namespace;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::traits;
use rustc::traits::{self, IntercrateMode};
use rustc::ty::TyCtxt;
use lint;
pub fn crate_inherent_impls_overlap_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
crate_num: CrateNum) {
assert_eq!(crate_num, LOCAL_CRATE);
@ -28,7 +30,8 @@ struct InherentOverlapChecker<'a, 'tcx: 'a> {
impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
fn check_for_common_items_in_impls(&self, impl1: DefId, impl2: DefId,
overlap: traits::OverlapResult) {
overlap: traits::OverlapResult,
used_to_be_allowed: bool) {
let name_and_namespace = |def_id| {
let item = self.tcx.associated_item(def_id);
@ -43,11 +46,21 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
for &item2 in &impl_items2[..] {
if (name, namespace) == name_and_namespace(item2) {
let mut err = struct_span_err!(self.tcx.sess,
self.tcx.span_of_impl(item1).unwrap(),
E0592,
"duplicate definitions with name `{}`",
name);
let node_id = self.tcx.hir.as_local_node_id(impl1);
let mut err = if used_to_be_allowed && node_id.is_some() {
self.tcx.struct_span_lint_node(
lint::builtin::INCOHERENT_FUNDAMENTAL_IMPLS,
node_id.unwrap(),
self.tcx.span_of_impl(item1).unwrap(),
&format!("duplicate definitions with name `{}` (E0592)", name)
)
} else {
struct_span_err!(self.tcx.sess,
self.tcx.span_of_impl(item1).unwrap(),
E0592,
"duplicate definitions with name `{}`",
name)
};
err.span_label(self.tcx.span_of_impl(item1).unwrap(),
format!("duplicate definitions for `{}`", name));
@ -69,12 +82,30 @@ impl<'a, 'tcx> InherentOverlapChecker<'a, 'tcx> {
for (i, &impl1_def_id) in impls.iter().enumerate() {
for &impl2_def_id in &impls[(i + 1)..] {
self.tcx.infer_ctxt().enter(|infcx| {
let used_to_be_allowed = self.tcx.infer_ctxt().enter(|infcx| {
if let Some(overlap) =
traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id) {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap)
traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id,
IntercrateMode::Issue43355)
{
self.check_for_common_items_in_impls(
impl1_def_id, impl2_def_id, overlap, false);
false
} else {
true
}
});
if used_to_be_allowed {
self.tcx.infer_ctxt().enter(|infcx| {
if let Some(overlap) =
traits::overlapping_impls(&infcx, impl1_def_id, impl2_def_id,
IntercrateMode::Fixed)
{
self.check_for_common_items_in_impls(
impl1_def_id, impl2_def_id, overlap, true);
}
});
}
}
}
}
@ -100,4 +131,3 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentOverlapChecker<'a, 'tcx> {
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
}
}

View File

@ -1268,15 +1268,23 @@ fn fn_sig<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
))
}
NodeExpr(&hir::Expr { node: hir::ExprClosure(..), hir_id, .. }) => {
let tables = tcx.typeck_tables_of(def_id);
match tables.node_id_to_type(hir_id).sty {
ty::TyClosure(closure_def_id, closure_substs) => {
assert_eq!(def_id, closure_def_id);
return closure_substs.closure_sig(closure_def_id, tcx);
}
ref t => bug!("closure with non-closure type: {:?}", t),
}
NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => {
// Closure signatures are not like other function
// signatures and cannot be accessed through `fn_sig`. For
// example, a closure signature excludes the `self`
// argument. In any case they are embedded within the
// closure type as part of the `ClosureSubsts`.
//
// To get
// the signature of a closure, you should use the
// `closure_sig` method on the `ClosureSubsts`:
//
// closure_substs.closure_sig(def_id, tcx)
//
// or, inside of an inference context, you can use
//
// infcx.closure_sig(def_id, closure_substs)
bug!("to get the signature of a closure, use `closure_sig()` not `fn_sig()`");
}
x => {

View File

@ -269,6 +269,7 @@ pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
lang_items.char_impl(),
lang_items.str_impl(),
lang_items.slice_impl(),
lang_items.slice_u8_impl(),
lang_items.const_ptr_impl(),
lang_items.mut_ptr_impl(),
];

View File

@ -1802,6 +1802,7 @@ pub enum PrimitiveType {
RawPointer,
Reference,
Fn,
Never,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Copy, Debug)]
@ -1843,6 +1844,7 @@ impl Type {
RawPointer(..) => Some(PrimitiveType::RawPointer),
BorrowedRef { type_: box Generic(..), .. } => Some(PrimitiveType::Reference),
BareFunction(..) => Some(PrimitiveType::Fn),
Never => Some(PrimitiveType::Never),
_ => None,
}
}
@ -1891,6 +1893,7 @@ impl GetDefId for Type {
Primitive(PrimitiveType::Tuple).def_id()
},
BareFunction(..) => Primitive(PrimitiveType::Fn).def_id(),
Never => Primitive(PrimitiveType::Never).def_id(),
Slice(..) => Primitive(PrimitiveType::Slice).def_id(),
Array(..) => Primitive(PrimitiveType::Array).def_id(),
RawPointer(..) => Primitive(PrimitiveType::RawPointer).def_id(),
@ -1927,6 +1930,7 @@ impl PrimitiveType {
"pointer" => Some(PrimitiveType::RawPointer),
"reference" => Some(PrimitiveType::Reference),
"fn" => Some(PrimitiveType::Fn),
"never" => Some(PrimitiveType::Never),
_ => None,
}
}
@ -1958,6 +1962,7 @@ impl PrimitiveType {
RawPointer => "pointer",
Reference => "reference",
Fn => "fn",
Never => "never",
}
}
@ -2521,7 +2526,7 @@ pub struct Span {
}
impl Span {
fn empty() -> Span {
pub fn empty() -> Span {
Span {
filename: "".to_string(),
loline: 0, locol: 0,
@ -2892,6 +2897,7 @@ fn build_deref_target_impls(cx: &DocContext,
RawPointer => tcx.lang_items().const_ptr_impl(),
Reference => None,
Fn => None,
Never => None,
};
if let Some(did) = did {
if !did.is_local() {

View File

@ -425,15 +425,13 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
Some(&(ref fqp, shortty)) => {
(fqp, shortty, repeat("../").take(loc.len()).collect())
}
None => match cache.external_paths.get(&did) {
Some(&(ref fqp, shortty)) => {
(fqp, shortty, match cache.extern_locations[&did.krate] {
(.., render::Remote(ref s)) => s.to_string(),
(.., render::Local) => repeat("../").take(loc.len()).collect(),
(.., render::Unknown) => return None,
})
}
None => return None,
None => {
let &(ref fqp, shortty) = cache.external_paths.get(&did)?;
(fqp, shortty, match cache.extern_locations[&did.krate] {
(.., render::Remote(ref s)) => s.to_string(),
(.., render::Local) => repeat("../").take(loc.len()).collect(),
(.., render::Unknown) => return None,
})
}
};
for component in &fqp[..fqp.len() - 1] {
@ -638,7 +636,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter, use_absolute: bool) -> fmt:
fmt::Display::fmt(t, f)?;
primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
}
clean::Never => f.write_str("!"),
clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
clean::RawPointer(m, ref t) => {
match **t {
clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {

View File

@ -65,6 +65,7 @@ r##"<!DOCTYPE html>
{before_content}
<nav class="sidebar">
<div class="sidebar-menu">&#9776;</div>
{logo}
{sidebar}
</nav>

View File

@ -421,9 +421,19 @@ impl ToJson for IndexItemFunctionType {
thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> =
RefCell::new(Vec::new()));
thread_local!(static USED_ID_MAP: RefCell<FxHashMap<String, usize>> =
thread_local!(pub static USED_ID_MAP: RefCell<FxHashMap<String, usize>> =
RefCell::new(init_ids()));
pub fn render_text<F: FnMut(RenderType) -> String>(mut render: F) -> (String, String) {
// Save the state of USED_ID_MAP so it only gets updated once even
// though we're rendering twice.
let orig_used_id_map = USED_ID_MAP.with(|map| map.borrow().clone());
let hoedown_output = render(RenderType::Hoedown);
USED_ID_MAP.with(|map| *map.borrow_mut() = orig_used_id_map);
let pulldown_output = render(RenderType::Pulldown);
(hoedown_output, pulldown_output)
}
fn init_ids() -> FxHashMap<String, usize> {
[
"main",
@ -699,7 +709,10 @@ fn print_message(msg: &str, intro_msg: &mut bool, span: &Span, text: &str) {
println!("{}", msg);
}
fn render_difference(diff: &html_diff::Difference, intro_msg: &mut bool, span: &Span, text: &str) {
pub fn render_difference(diff: &html_diff::Difference,
intro_msg: &mut bool,
span: &Span,
text: &str) {
match *diff {
html_diff::Difference::NodeType { ref elem, ref opposite_elem } => {
print_message(&format!(" {} Types differ: expected: `{}`, found: `{}`",
@ -1659,11 +1672,8 @@ impl<'a> Item<'a> {
let mut path = String::new();
let (krate, path) = if self.item.def_id.is_local() {
let path = PathBuf::from(&self.item.source.filename);
if let Some(path) = self.cx.shared.local_sources.get(&path) {
(&self.cx.shared.layout.krate, path)
} else {
return None;
}
let path = self.cx.shared.local_sources.get(&path)?;
(&self.cx.shared.layout.krate, path)
} else {
// Macros from other libraries get special filenames which we can
// safely ignore.
@ -1853,12 +1863,7 @@ fn render_markdown(w: &mut fmt::Formatter,
prefix: &str,
scx: &SharedContext)
-> fmt::Result {
// Save the state of USED_ID_MAP so it only gets updated once even
// though we're rendering twice.
let orig_used_id_map = USED_ID_MAP.with(|map| map.borrow().clone());
let hoedown_output = format!("{}", Markdown(md_text, RenderType::Hoedown));
USED_ID_MAP.with(|map| *map.borrow_mut() = orig_used_id_map);
let pulldown_output = format!("{}", Markdown(md_text, RenderType::Pulldown));
let (hoedown_output, pulldown_output) = render_text(|ty| format!("{}", Markdown(md_text, ty)));
let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output);
differences.retain(|s| {
match *s {
@ -3542,6 +3547,7 @@ impl<'a> fmt::Display for Sidebar<'a> {
let cx = self.cx;
let it = self.item;
let parentlen = cx.current.len() - if it.is_mod() {1} else {0};
let mut should_close = false;
if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union()
|| it.is_enum() || it.is_mod() || it.is_typedef()
@ -3575,6 +3581,8 @@ impl<'a> fmt::Display for Sidebar<'a> {
}
}
write!(fmt, "<div class=\"sidebar-elems\">")?;
should_close = true;
match it.inner {
clean::StructItem(ref s) => sidebar_struct(fmt, it, s)?,
clean::TraitItem(ref t) => sidebar_trait(fmt, it, t)?,
@ -3625,6 +3633,10 @@ impl<'a> fmt::Display for Sidebar<'a> {
write!(fmt, "<script defer src=\"{path}sidebar-items.js\"></script>",
path = relpath)?;
}
if should_close {
// Closes sidebar-elems div.
write!(fmt, "</div>")?;
}
Ok(())
}

View File

@ -106,6 +106,30 @@
return (elem.offsetParent === null)
}
function showSidebar() {
var elems = document.getElementsByClassName("sidebar-elems")[0];
if (elems) {
elems.style.display = "block";
}
var sidebar = document.getElementsByClassName('sidebar')[0];
sidebar.style.position = 'fixed';
sidebar.style.width = '100%';
sidebar.style.marginLeft = '0';
document.getElementsByTagName("body")[0].style.marginTop = '45px';
}
function hideSidebar() {
var elems = document.getElementsByClassName("sidebar-elems")[0];
if (elems) {
elems.style.display = "";
}
var sidebar = document.getElementsByClassName('sidebar')[0];
sidebar.style.position = '';
sidebar.style.width = '';
sidebar.style.marginLeft = '';
document.getElementsByTagName("body")[0].style.marginTop = '';
}
// used for special search precedence
var TY_PRIMITIVE = itemTypes.indexOf("primitive");
@ -119,8 +143,7 @@
map(function(s) {
var pair = s.split("=");
params[decodeURIComponent(pair[0])] =
typeof pair[1] === "undefined" ?
null : decodeURIComponent(pair[1]);
typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
});
return params;
}
@ -131,6 +154,8 @@
}
function highlightSourceLines(ev) {
// If we're in mobile mode, we should add the sidebar in any case.
hideSidebar();
var search = document.getElementById("search");
var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
if (match) {
@ -1460,7 +1485,7 @@
// delayed sidebar rendering.
function initSidebarItems(items) {
var sidebar = document.getElementsByClassName('sidebar')[0];
var sidebar = document.getElementsByClassName('sidebar-elems')[0];
var current = window.sidebarCurrent;
function block(shortty, longty) {
@ -1822,6 +1847,30 @@
}
};
}
var params = getQueryStringParams();
if (params && params.search) {
addClass(document.getElementById("main"), "hidden");
var search = document.getElementById("search");
removeClass(search, "hidden");
search.innerHTML = '<h3 style="text-align: center;">Loading search results...</h3>';
}
var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
if (sidebar_menu) {
sidebar_menu.onclick = function() {
var sidebar = document.getElementsByClassName('sidebar')[0];
if (sidebar.style.position === "fixed") {
hideSidebar();
} else {
showSidebar();
}
};
}
window.onresize = function() {
hideSidebar();
};
}());
// Sets the focus on the search bar at the top of the page

View File

@ -179,7 +179,6 @@ nav.sub {
top: 0;
height: 100vh;
overflow: auto;
z-index: 1;
}
.sidebar .current {
@ -209,7 +208,7 @@ nav.sub {
.sidebar .version {
font-size: 15px;
text-align: center;
border-bottom: #DDDDDD 1px solid;
border-bottom: 1px solid;
overflow-wrap: break-word;
word-wrap: break-word; /* deprecated */
word-break: break-word; /* Chrome, non-standard */
@ -248,8 +247,8 @@ nav.sub {
}
.sidebar-title {
border-top: 1px solid #777;
border-bottom: 1px solid #777;
border-top: 1px solid;
border-bottom: 1px solid;
text-align: center;
font-size: 17px;
margin-bottom: 5px;
@ -264,6 +263,10 @@ nav.sub {
width: 100%;
}
.sidebar-menu {
display: none;
}
.content {
padding: 15px 0;
}
@ -273,9 +276,19 @@ nav.sub {
overflow: auto;
padding-left: 0;
}
#search {
margin-left: 230px;
position: relative;
}
#results {
position: absolute;
right: 0;
left: 0;
overflow: auto;
}
.content pre.line-numbers {
float: left;
border: none;
@ -355,7 +368,6 @@ h4 > code, h3 > code, .invisible > code {
}
.invisible {
background: rgba(0, 0, 0, 0);
width: 100%;
display: inline-block;
}
@ -435,7 +447,6 @@ h4 > code, h3 > code, .invisible > code {
.content .fn .where,
.content .where.fmt-newline {
display: block;
color: #4E4C4C;
font-size: 0.8em;
}
@ -529,7 +540,6 @@ a {
}
.search-input:focus {
border-color: #66afe9;
border-radius: 2px;
border: 0;
outline: 0;
@ -551,7 +561,8 @@ a {
.content .search-results td:first-child a { padding-right: 10px; }
tr.result span.primitive::after {
content: ' (primitive type)'; font-style: italic; color: black;
content: ' (primitive type)';
font-style: italic;
}
body.blur > :not(#help) {
@ -688,7 +699,6 @@ a.test-arrow:hover{
font-weight: 300;
position: absolute;
left: -23px;
color: #999;
top: 0;
}
@ -814,7 +824,7 @@ span.since {
position: static;
}
.sidebar .location {
.sidebar > .location {
float: right;
margin: 0px;
margin-top: 2px;
@ -834,16 +844,33 @@ span.since {
margin-top: 5px;
margin-bottom: 5px;
float: left;
margin-left: 50px;
}
.sidebar-menu {
position: absolute;
font-size: 2rem;
cursor: pointer;
margin-top: 2px;
display: block;
}
.sidebar-elems {
background-color: #F1F1F1;
position: fixed;
z-index: 1;
left: 0;
top: 45px;
bottom: 0;
overflow-y: auto;
border-right: 1px solid #000;
display: none;
}
nav.sub {
margin: 0 auto;
}
.sidebar .block {
display: none;
}
.content {
margin-left: 0px;
}
@ -895,8 +922,6 @@ span.since {
.tooltip .tooltiptext {
width: 120px;
display: none;
background-color: black;
color: #fff;
text-align: center;
padding: 5px 3px;
border-radius: 6px;
@ -918,13 +943,10 @@ span.since {
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
.important-traits .tooltip .tooltiptext {
background-color: white;
color: black;
border: 1px solid #000;
border: 1px solid;
}
pre.rust {
@ -944,22 +966,21 @@ pre.rust {
float: left;
width: 33.3%;
text-align: center;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid;
font-size: 18px;
cursor: pointer;
}
#titles > div.selected {
border-bottom: 3px solid #0078ee;
border-bottom: 3px solid;
}
#titles > div:hover {
border-bottom: 3px solid #0089ff;
border-bottom: 3px solid;
}
#titles > div > div.count {
display: inline-block;
color: #888;
font-size: 16px;
}
@ -974,11 +995,18 @@ h4 > .important-traits {
top: 2px;
}
@media (max-width: 700px) {
h4 > .important-traits {
position: absolute;
left: -22px;
top: 24px;
}
}
.modal {
position: fixed;
width: 100vw;
height: 100vh;
background-color: rgba(0,0,0,0.3);
z-index: 10000;
top: 0;
left: 0;
@ -988,13 +1016,12 @@ h4 > .important-traits {
display: block;
max-width: 60%;
min-width: 200px;
background-color: #eee;
padding: 8px;
top: 40%;
position: absolute;
left: 50%;
transform: translate(-50%, -40%);
border: 1px solid #999;
border: 1px solid;
border-radius: 4px;
border-top-right-radius: 0;
}
@ -1021,35 +1048,24 @@ h3.important {
right: -25px;
top: -1px;
font-size: 18px;
background-color: #eee;
width: 25px;
padding-right: 2px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
text-align: center;
border: 1px solid #999;
border: 1px solid;
border-right: 0;
cursor: pointer;
}
.modal-content > .close:hover {
background-color: #ff1f1f;
color: white;
}
.modal-content > .whiter {
height: 25px;
position: absolute;
width: 3px;
background-color: #eee;
right: -2px;
top: 0px;
}
.modal-content > .close:hover + .whiter {
background-color: #ff1f1f;
}
#main > div.important-traits {
position: absolute;
left: -24px;
@ -1060,4 +1076,4 @@ h3.important {
position: absolute;
left: -42px;
margin-top: 2px;
}
}

View File

@ -31,6 +31,10 @@ h2, h3:not(.impl):not(.method):not(.type):not(.tymethod), h4:not(.method):not(.t
background-color: white;
}
.invisible {
background: rgba(0, 0, 0, 0);
}
.docblock code, .docblock-short code {
background-color: #F5F5F5;
}
@ -56,6 +60,15 @@ pre {
color: #333;
}
.sidebar .version {
border-bottom-color: #DDD;
}
.sidebar-title {
border-top-color: #777;
border-bottom-color: #777;
}
.block a:hover {
background: #F5F5F5;
}
@ -89,6 +102,12 @@ pre {
background: #FDFFD3;
}
.content .method .where,
.content .fn .where,
.content .where.fmt-newline {
color: #4E4C4C;
}
.content .highlighted {
color: #000 !important;
background-color: #ccc;
@ -152,12 +171,20 @@ a.test-arrow {
color: #f5f5f5;
}
.collapse-toggle {
color: #999;
}
.search-input {
color: #555;
box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent;
background-color: white;
}
.search-input:focus {
border-color: #66afe9;
}
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
.stab.portability { background: #C4ECFF; border-color: #7BA5DB; }
@ -176,6 +203,10 @@ a.test-arrow {
color: grey;
}
tr.result span.primitive::after {
color: black;
}
.line-numbers :target { background-color: transparent; }
/* Code highlighting */
@ -241,3 +272,61 @@ pre.ignore:hover, .information:hover + pre.ignore {
.search-failed > a {
color: #0089ff;
}
.tooltip .tooltiptext {
background-color: black;
color: #fff;
}
.tooltip .tooltiptext::after {
border-color: transparent black transparent transparent;
}
.important-traits .tooltip .tooltiptext {
background-color: white;
color: black;
border-color: black;
}
#titles > div {
border-bottom-color: #ccc;
}
#titles > div.selected {
border-bottom-color: #0078ee;
}
#titles > div:hover {
border-bottom-color: #0089ff;
}
#titles > div > div.count {
color: #888;
}
.modal {
background-color: rgba(0,0,0,0.3);
}
.modal-content {
background-color: #eee;
border-color: #999;
}
.modal-content > .close {
background-color: #eee;
border-color: #999;
}
.modal-content > .close:hover {
background-color: #ff1f1f;
color: white;
}
.modal-content > .whiter {
background-color: #eee;
}
.modal-content > .close:hover + .whiter {
background-color: #ff1f1f;
}

View File

@ -19,10 +19,15 @@ use rustc::session::search_paths::SearchPaths;
use rustc::session::config::Externs;
use syntax::codemap::DUMMY_SP;
use clean::Span;
use externalfiles::{ExternalHtml, LoadStringError, load_string};
use html::render::reset_ids;
use html_diff;
use html::render::{render_text, reset_ids};
use html::escape::Escape;
use html::render::render_difference;
use html::markdown;
use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, old_find_testable_code};
use html::markdown::RenderType;
@ -52,6 +57,10 @@ fn extract_leading_metadata<'a>(s: &'a str) -> (Vec<&'a str>, &'a str) {
pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
external_html: &ExternalHtml, include_toc: bool,
render_type: RenderType) -> isize {
// Span used for markdown hoedown/pulldown differences.
let mut span = Span::empty();
span.filename = input.to_owned();
let input_p = Path::new(input);
output.push(input_p.file_stem().unwrap());
output.set_extension("html");
@ -89,12 +98,36 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
reset_ids(false);
let rendered = if include_toc {
format!("{}", MarkdownWithToc(text, render_type))
let (hoedown_output, pulldown_output) = if include_toc {
// Save the state of USED_ID_MAP so it only gets updated once even
// though we're rendering twice.
render_text(|ty| format!("{}", MarkdownWithToc(text, ty)))
} else {
format!("{}", Markdown(text, render_type))
// Save the state of USED_ID_MAP so it only gets updated once even
// though we're rendering twice.
render_text(|ty| format!("{}", Markdown(text, ty)))
};
let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output);
differences.retain(|s| {
match *s {
html_diff::Difference::NodeText { ref elem_text,
ref opposite_elem_text,
.. }
if elem_text.split_whitespace().eq(opposite_elem_text.split_whitespace()) => {
false
}
_ => true,
}
});
if !differences.is_empty() {
let mut intro_msg = false;
for diff in differences {
render_difference(&diff, &mut intro_msg, &span, text);
}
}
let err = write!(
&mut out,
r#"<!DOCTYPE html>
@ -126,16 +159,16 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
css = css,
in_header = external_html.in_header,
before_content = external_html.before_content,
text = rendered,
text = if render_type == RenderType::Pulldown { pulldown_output } else { hoedown_output },
after_content = external_html.after_content,
);
);
match err {
Err(e) => {
eprintln!("rustdoc: cannot write to `{}`: {}", output.display(), e);
6
}
Ok(_) => 0
Ok(_) => 0,
}
}

View File

@ -1052,10 +1052,7 @@ impl Json {
pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Json>{
let mut target = self;
for key in keys {
match target.find(*key) {
Some(t) => { target = t; },
None => return None
}
target = target.find(*key)?;
}
Some(target)
}

View File

@ -1152,13 +1152,9 @@ impl<'a, T, S> Iterator for Intersection<'a, T, S>
fn next(&mut self) -> Option<&'a T> {
loop {
match self.iter.next() {
None => return None,
Some(elt) => {
if self.other.contains(elt) {
return Some(elt);
}
}
let elt = self.iter.next()?;
if self.other.contains(elt) {
return Some(elt);
}
}
}
@ -1202,13 +1198,9 @@ impl<'a, T, S> Iterator for Difference<'a, T, S>
fn next(&mut self) -> Option<&'a T> {
loop {
match self.iter.next() {
None => return None,
Some(elt) => {
if !self.other.contains(elt) {
return Some(elt);
}
}
let elt = self.iter.next()?;
if !self.other.contains(elt) {
return Some(elt);
}
}
}

View File

@ -211,6 +211,115 @@ pub struct DirBuilder {
recursive: bool,
}
/// Read the entire contents of a file into a bytes vector.
///
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
/// with fewer imports and without an intermediate variable.
///
/// [`File::open`]: struct.File.html#method.open
/// [`read_to_end`]: ../io/trait.Read.html#method.read_to_end
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`ErrorKind::Interrupted`].
///
/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
/// use std::net::SocketAddr;
///
/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut bytes = Vec::new();
File::open(path)?.read_to_end(&mut bytes)?;
Ok(bytes)
}
/// Read the entire contents of a file into a string.
///
/// This is a convenience function for using [`File::open`] and [`read_to_string`]
/// with fewer imports and without an intermediate variable.
///
/// [`File::open`]: struct.File.html#method.open
/// [`read_to_string`]: ../io/trait.Read.html#method.read_to_string
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`ErrorKind::Interrupted`],
/// or if the contents of the file are not valid UTF-8.
///
/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
/// use std::net::SocketAddr;
///
/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
/// let foo: SocketAddr = fs::read_string("address.txt")?.parse()?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn read_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
let mut string = String::new();
File::open(path)?.read_to_string(&mut string)?;
Ok(string)
}
/// Write a slice as the entire contents of a file.
///
/// This function will create a file if it does not exist,
/// and will entirely replace its contents if it does.
///
/// This is a convenience function for using [`File::create`] and [`write_all`]
/// with fewer imports.
///
/// [`File::create`]: struct.File.html#method.create
/// [`write_all`]: ../io/trait.Write.html#method.write_all
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
///
/// # fn foo() -> std::io::Result<()> {
/// fs::write("foo.txt", b"Lorem ipsum")?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
File::create(path)?.write_all(contents.as_ref())
}
impl File {
/// Attempts to open a file in read-only mode.
///
@ -1912,7 +2021,9 @@ mod tests {
) }
#[cfg(unix)]
macro_rules! error { ($e:expr, $s:expr) => (
macro_rules! error { ($e:expr, $s:expr) => ( error_contains!($e, $s) ) }
macro_rules! error_contains { ($e:expr, $s:expr) => (
match $e {
Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
Err(ref err) => assert!(err.to_string().contains($s),
@ -2921,6 +3032,27 @@ mod tests {
assert!(v == &bytes[..]);
}
#[test]
fn write_then_read() {
let mut bytes = [0; 1024];
StdRng::new().unwrap().fill_bytes(&mut bytes);
let tmpdir = tmpdir();
check!(fs::write(&tmpdir.join("test"), &bytes[..]));
let v = check!(fs::read(&tmpdir.join("test")));
assert!(v == &bytes[..]);
check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
error_contains!(fs::read_string(&tmpdir.join("not-utf8")),
"stream did not contain valid UTF-8");
let s = "𐁁𐀓𐀠𐀴𐀍";
check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
let string = check!(fs::read_string(&tmpdir.join("utf8")));
assert_eq!(string, s);
}
#[test]
fn file_try_clone() {
let tmpdir = tmpdir();

View File

@ -2019,10 +2019,9 @@ impl<R: Read> Iterator for Chars<R> {
type Item = result::Result<char, CharsError>;
fn next(&mut self) -> Option<result::Result<char, CharsError>> {
let first_byte = match read_one_byte(&mut self.inner) {
None => return None,
Some(Ok(b)) => b,
Some(Err(e)) => return Some(Err(CharsError::Other(e))),
let first_byte = match read_one_byte(&mut self.inner)? {
Ok(b) => b,
Err(e) => return Some(Err(CharsError::Other(e))),
};
let width = core_str::utf8_char_width(first_byte);
if width == 1 { return Some(Ok(first_byte as char)) }

View File

@ -260,6 +260,7 @@
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
#![feature(fs_read_write)]
#![feature(fixed_size_array)]
#![feature(float_from_str_radix)]
#![feature(fn_traits)]

View File

@ -282,9 +282,34 @@ pub mod builtin {
/// Unconditionally causes compilation to fail with the given error message when encountered.
///
/// For more information, see the [RFC].
/// This macro should be used when a crate uses a conditional compilation strategy to provide
/// better error messages for errornous conditions.
///
/// [RFC]: https://github.com/rust-lang/rfcs/blob/master/text/1695-add-error-macro.md
/// # Examples
///
/// Two such examples are macros and `#[cfg]` environments.
///
/// Emit better compiler error if a macro is passed invalid values.
///
/// ```compile_fail
/// macro_rules! give_me_foo_or_bar {
/// (foo) => {};
/// (bar) => {};
/// ($x:ident) => {
/// compile_error!("This macro only accepts `foo` or `bar`");
/// }
/// }
///
/// give_me_foo_or_bar!(neither);
/// // ^ will fail at compile time with message "This macro only accepts `foo` or `bar`"
/// ```
///
/// Emit compiler error if one of a number of features isn't available.
///
/// ```compile_fail
/// #[cfg(not(any(feature = "foo", feature = "bar")))]
/// compile_error!("Either feature \"foo\" or \"bar\" must be enabled for this crate.")
/// ```
#[stable(feature = "compile_error_macro", since = "1.20.0")]
#[macro_export]
macro_rules! compile_error { ($msg:expr) => ({ /* compiler built-in */ }) }
@ -606,7 +631,7 @@ pub mod builtin {
#[macro_export]
macro_rules! module_path { () => ({ /* compiler built-in */ }) }
/// Boolean evaluation of configuration flags.
/// Boolean evaluation of configuration flags, at compile-time.
///
/// In addition to the `#[cfg]` attribute, this macro is provided to allow
/// boolean expression evaluation of configuration flags. This frequently

View File

@ -170,11 +170,7 @@ impl<'a> Parser<'a> {
return None;
}
let octet = self.read_number(10, 3, 0x100).map(|n| n as u8);
match octet {
Some(d) => bs[i] = d,
None => return None,
};
bs[i] = self.read_number(10, 3, 0x100).map(|n| n as u8)?;
i += 1;
}
Some(Ipv4Addr::new(bs[0], bs[1], bs[2], bs[3]))

View File

@ -67,6 +67,134 @@
#[stable(feature = "rust1", since = "1.0.0")]
mod prim_bool { }
#[doc(primitive = "never")]
//
/// The `!` type, also called "never".
///
/// `!` represents the type of computations which never resolve to any value at all. For example,
/// the [`exit`] function `fn exit(code: i32) -> !` exits the process without ever returning, and
/// so returns `!`.
///
/// `break`, `continue` and `return` expressions also have type `!`. For example we are allowed to
/// write:
///
/// ```
/// #![feature(never_type)]
/// # fn foo() -> u32 {
/// let x: ! = {
/// return 123
/// };
/// # }
/// ```
///
/// Although the `let` is pointless here, it illustrates the meaning of `!`. Since `x` is never
/// assigned a value (because `return` returns from the entire function), `x` can be given type
/// `!`. We could also replace `return 123` with a `panic!` or a never-ending `loop` and this code
/// would still be valid.
///
/// A more realistic usage of `!` is in this code:
///
/// ```
/// # fn get_a_number() -> Option<u32> { None }
/// # loop {
/// let num: u32 = match get_a_number() {
/// Some(num) => num,
/// None => break,
/// };
/// # }
/// ```
///
/// Both match arms must produce values of type [`u32`], but since `break` never produces a value
/// at all we know it can never produce a value which isn't a [`u32`]. This illustrates another
/// behaviour of the `!` type - expressions with type `!` will coerce into any other type.
///
/// [`u32`]: primitive.str.html
/// [`exit`]: process/fn.exit.html
///
/// # `!` and generics
///
/// The main place you'll see `!` used explicitly is in generic code. Consider the [`FromStr`]
/// trait:
///
/// ```
/// trait FromStr: Sized {
/// type Err;
/// fn from_str(s: &str) -> Result<Self, Self::Err>;
/// }
/// ```
///
/// When implementing this trait for [`String`] we need to pick a type for [`Err`]. And since
/// converting a string into a string will never result in an error, the appropriate type is `!`.
/// (Currently the type actually used is an enum with no variants, though this is only because `!`
/// was added to Rust at a later date and it may change in the future). With an [`Err`] type of
/// `!`, if we have to call [`String::from_str`] for some reason the result will be a
/// [`Result<String, !>`] which we can unpack like this:
///
/// ```ignore (string-from-str-error-type-is-not-never-yet)
/// // NOTE: This does not work today!
/// let Ok(s) = String::from_str("hello");
/// ```
///
/// Since the [`Err`] variant contains a `!`, it can never occur. So we can exhaustively match on
/// [`Result<T, !>`] by just taking the [`Ok`] variant. This illustrates another behaviour of `!` -
/// it can be used to "delete" certain enum variants from generic types like `Result`.
///
/// [`String::from_str`]: str/trait.FromStr.html#tymethod.from_str
/// [`Result<String, !>`]: result/enum.Result.html
/// [`Result<T, !>`]: result/enum.Result.html
/// [`Ok`]: result/enum.Result.html#variant.Ok
/// [`String`]: string/struct.String.html
/// [`Err`]: result/enum.Result.html#variant.Err
/// [`FromStr`]: str/trait.FromStr.html
///
/// # `!` and traits
///
/// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
/// which doesn't `panic!`. As is turns out, most traits can have an `impl` for `!`. Take [`Debug`]
/// for example:
///
/// ```
/// # #![feature(never_type)]
/// # use std::fmt;
/// # trait Debug {
/// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result;
/// # }
/// impl Debug for ! {
/// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// *self
/// }
/// }
/// ```
///
/// Once again we're using `!`'s ability to coerce into any other type, in this case
/// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be
/// called (because there is no value of type `!` for it to be called with). Writing `*self`
/// essentially tells the compiler "We know that this code can never be run, so just treat the
/// entire function body has having type [`fmt::Result`]". This pattern can be used a lot when
/// implementing traits for `!`. Generally, any trait which only has methods which take a `self`
/// parameter should have such as impl.
///
/// On the other hand, one trait which would not be appropriate to implement is [`Default`]:
///
/// ```
/// trait Default {
/// fn default() -> Self;
/// }
/// ```
///
/// Since `!` has no values, it has no default value either. It's true that we could write an
/// `impl` for this which simply panics, but the same is true for any type (we could `impl
/// Default` for (eg.) [`File`] by just making [`default()`] panic.)
///
/// [`fmt::Result`]: fmt/type.Result.html
/// [`File`]: fs/struct.File.html
/// [`Debug`]: fmt/trait.Debug.html
/// [`Default`]: default/trait.Default.html
/// [`default()`]: default/trait.Default.html#tymethod.default
///
#[unstable(feature = "never_type_impls", issue = "35121")]
mod prim_never { }
#[doc(primitive = "char")]
//
/// A character type.

Some files were not shown because too many files have changed in this diff Show More