mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-05 11:33:04 +00:00
Merge remote-tracking branch 'origin/master' into miri
This commit is contained in:
commit
7e5583b7f8
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -7,3 +7,4 @@
|
||||
src/etc/installer/gfx/* binary
|
||||
*.woff binary
|
||||
src/vendor/** -text
|
||||
Cargo.lock -merge
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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()))
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1776,14 +1776,20 @@ impl<I: Iterator> Iterator for Peekable<I> {
|
||||
|
||||
#[inline]
|
||||
fn nth(&mut self, n: usize) -> Option<I::Item> {
|
||||
// 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() {
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn last(mut self) -> Option<I::Item> {
|
||||
|
@ -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!`].
|
||||
///
|
||||
|
@ -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
|
||||
///
|
||||
/// ```
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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)),
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
@ -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
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
/// 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);
|
||||
return closure_sig_ty.fn_sig(self.tcx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tcx.fn_sig(def_id)
|
||||
closure_sig_ty.fn_sig(self.tcx)
|
||||
}
|
||||
|
||||
/// Normalizes associated types in `value`, potentially returning
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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],
|
||||
|
@ -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 {
|
||||
|
@ -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,42 +350,42 @@ 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)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we exit above loop, never found a local type.
|
||||
debug!("orphan_check_trait_ref: no local type");
|
||||
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 => {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 `{}`{} \
|
||||
format!("upstream crates may add new impl of trait `{}`{} \
|
||||
in future versions",
|
||||
trait_desc, self_desc));
|
||||
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,11 +1087,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if !self.is_knowable(stack) {
|
||||
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.is_empty() {
|
||||
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();
|
||||
@ -1090,8 +1104,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let cause = if !coherence::trait_ref_is_local_or_fundamental(self.tcx(),
|
||||
trait_ref) {
|
||||
let cause = if let Conflict::Upstream = conflict {
|
||||
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
|
||||
} else {
|
||||
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
|
||||
@ -1100,6 +1113,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
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)
|
||||
|
@ -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,
|
||||
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,
|
||||
"conflicting implementations of trait `{}`{}:",
|
||||
overlap.trait_desc,
|
||||
overlap.self_desc.clone().map_or(String::new(),
|
||||
|ty| {
|
||||
format!(" for type `{}`", ty)
|
||||
}));
|
||||
"{}",
|
||||
msg)
|
||||
};
|
||||
|
||||
match tcx.span_of_impl(overlap.with_impl) {
|
||||
Ok(span) => {
|
||||
|
@ -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,30 +105,19 @@ 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 tcx = tcx.global_tcx();
|
||||
let (le, ge) = tcx.infer_ctxt().enter(|infcx| {
|
||||
let overlap = traits::overlapping_impls(&infcx,
|
||||
possible_sibling,
|
||||
impl_def_id);
|
||||
if let Some(overlap) = overlap {
|
||||
if tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
|
||||
return Ok((false, false));
|
||||
}
|
||||
|
||||
let le = tcx.specializes((impl_def_id, possible_sibling));
|
||||
let ge = tcx.specializes((possible_sibling, impl_def_id));
|
||||
|
||||
if le == ge {
|
||||
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();
|
||||
Err(OverlapError {
|
||||
OverlapError {
|
||||
with_impl: possible_sibling,
|
||||
trait_desc: trait_ref.to_string(),
|
||||
// only report the Self type if it has at least
|
||||
@ -140,7 +129,25 @@ impl<'a, 'gcx, 'tcx> Children {
|
||||
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,
|
||||
traits::IntercrateMode::Issue43355);
|
||||
if let Some(overlap) = overlap {
|
||||
if tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) {
|
||||
return Ok((false, false));
|
||||
}
|
||||
|
||||
let le = tcx.specializes((impl_def_id, possible_sibling));
|
||||
let ge = tcx.specializes((possible_sibling, impl_def_id));
|
||||
|
||||
if le == ge {
|
||||
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.
|
||||
|
@ -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([
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -47,10 +47,8 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
hasher.finish()
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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, ®ioncx);
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
dump_mir_results(
|
||||
infcx,
|
||||
liveness,
|
||||
MirSource::item(def_id),
|
||||
&mir,
|
||||
®ioncx,
|
||||
&closure_region_requirements,
|
||||
);
|
||||
|
||||
regioncx
|
||||
// We also have a `#[rustc_nll]` annotation that causes us to dump
|
||||
// information
|
||||
dump_annotation(infcx, &mir, def_id, ®ioncx, &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>(
|
||||
®ular_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;
|
||||
|
48
src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
Normal file
48
src/librustc_mir/borrow_check/nll/region_infer/annotation.rs
Normal 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
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
Normal file
100
src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
71
src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
Normal file
71
src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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,56 +336,134 @@ 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);
|
||||
}
|
||||
|
||||
fn check_free_region(
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
// 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!(
|
||||
"free region `{}` does not outlive `{}`",
|
||||
longer_name,
|
||||
shorter_name
|
||||
),
|
||||
&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
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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[®ion];
|
||||
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={:?})",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
/// 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);
|
||||
});
|
||||
|
||||
debug!("universal_regions: indices={:#?}", indices);
|
||||
assert_eq!(
|
||||
region_mapping.len(),
|
||||
expected_num_vars,
|
||||
"index vec had unexpected number of variables"
|
||||
);
|
||||
|
||||
UniversalRegions { indices, free_region_map: &tables.free_region_map }
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
queue.extend(relation.parents(fr));
|
||||
}
|
||||
|
||||
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))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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,11 +294,20 @@ 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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mir::TerminatorKind::SwitchInt {..} |
|
||||
mir::TerminatorKind::Drop {..} |
|
||||
mir::TerminatorKind::DropAndReplace {..} |
|
||||
|
@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
|
@ -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,13 +1136,44 @@ 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) {
|
||||
|
@ -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)?;
|
||||
@ -149,14 +155,8 @@ 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 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 {}",
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
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, .. }
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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([
|
||||
|
@ -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, &[]),
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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, _) => {
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
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);
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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(),
|
||||
];
|
||||
|
@ -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() {
|
||||
|
@ -425,16 +425,14 @@ 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)) => {
|
||||
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,
|
||||
})
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
};
|
||||
for component in &fqp[..fqp.len() - 1] {
|
||||
url.push_str(component);
|
||||
@ -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, ..} => {
|
||||
|
@ -65,6 +65,7 @@ r##"<!DOCTYPE html>
|
||||
{before_content}
|
||||
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-menu">☰</div>
|
||||
{logo}
|
||||
{sidebar}
|
||||
</nav>
|
||||
|
@ -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) {
|
||||
let path = self.cx.shared.local_sources.get(&path)?;
|
||||
(&self.cx.shared.layout.krate, path)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} 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(())
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,7 +159,7 @@ 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,
|
||||
);
|
||||
|
||||
@ -135,7 +168,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches,
|
||||
eprintln!("rustdoc: cannot write to `{}`: {}", output.display(), e);
|
||||
6
|
||||
}
|
||||
Ok(_) => 0
|
||||
Ok(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -1152,16 +1152,12 @@ 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) => {
|
||||
let elt = self.iter.next()?;
|
||||
if self.other.contains(elt) {
|
||||
return Some(elt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (_, upper) = self.iter.size_hint();
|
||||
@ -1202,16 +1198,12 @@ 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) => {
|
||||
let elt = self.iter.next()?;
|
||||
if !self.other.contains(elt) {
|
||||
return Some(elt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (_, upper) = self.iter.size_hint();
|
||||
|
134
src/libstd/fs.rs
134
src/libstd/fs.rs
@ -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();
|
||||
|
@ -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)) }
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -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]))
|
||||
|
@ -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.
|
||||
|
@ -255,10 +255,7 @@ pub mod guard {
|
||||
|
||||
pub unsafe fn init() -> Option<usize> {
|
||||
let psize = os::page_size();
|
||||
let mut stackaddr = match get_stack_start() {
|
||||
Some(addr) => addr,
|
||||
None => return None,
|
||||
};
|
||||
let mut stackaddr = get_stack_start()?;
|
||||
|
||||
// Ensure stackaddr is page aligned! A parent process might
|
||||
// have reset RLIMIT_STACK to be non-page aligned. The
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user