Merge branch 'master' into drop

This commit is contained in:
Michael Bradshaw 2018-09-29 19:51:09 -07:00
commit 43cc32fbb2
3017 changed files with 7141 additions and 3254 deletions

View file

@ -1,3 +1,16 @@
Version 1.29.1 (2018-09-25)
===========================
Security Notes
--------------
- The standard library's `str::repeat` function contained an out of bounds write
caused by an integer overflow. This has been fixed by deterministically
panicking when an overflow happens.
Thank you to Scott McMurray for responsibily disclosing this vulnerability to
us.
Version 1.29.0 (2018-09-13)
==========================

View file

@ -344,6 +344,7 @@ dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2372,6 +2373,7 @@ dependencies = [
"rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc 0.0.0",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_codegen_utils 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_target 0.0.0",
"rustc_typeck 0.0.0",

View file

@ -0,0 +1,167 @@
# `emit-stack-sizes`
The tracking issue for this feature is: [#54192]
[#54192]: https://github.com/rust-lang/rust/issues/54192
------------------------
The rustc flag `-Z emit-stack-sizes` makes LLVM emit stack size metadata.
> **NOTE**: This LLVM feature only supports the ELF object format as of LLVM
> 8.0. Using this flag with targets that use other object formats (e.g. macOS
> and Windows) will result in it being ignored.
Consider this crate:
```
#![crate_type = "lib"]
use std::ptr;
pub fn foo() {
// this function doesn't use the stack
}
pub fn bar() {
let xs = [0u32; 2];
// force LLVM to allocate `xs` on the stack
unsafe { ptr::read_volatile(&xs.as_ptr()); }
}
```
Using the `-Z emit-stack-sizes` flag produces extra linker sections in the
output *object file*.
``` console
$ rustc -C opt-level=3 --emit=obj foo.rs
$ size -A foo.o
foo.o :
section size addr
.text 0 0
.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0
.text._ZN3foo3bar17h1acb594305f70c2eE 22 0
.note.GNU-stack 0 0
.eh_frame 72 0
Total 95
$ rustc -C opt-level=3 --emit=obj -Z emit-stack-sizes foo.rs
$ size -A foo.o
foo.o :
section size addr
.text 0 0
.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0
.stack_sizes 9 0
.text._ZN3foo3bar17h1acb594305f70c2eE 22 0
.stack_sizes 9 0
.note.GNU-stack 0 0
.eh_frame 72 0
Total 113
```
As of LLVM 7.0 the data will be written into a section named `.stack_sizes` and
the format is "an array of pairs of function symbol values (pointer size) and
stack sizes (unsigned LEB128)".
``` console
$ objdump -d foo.o
foo.o: file format elf64-x86-64
Disassembly of section .text._ZN3foo3foo17he211d7b4a3a0c16eE:
0000000000000000 <_ZN3foo3foo17he211d7b4a3a0c16eE>:
0: c3 retq
Disassembly of section .text._ZN3foo3bar17h1acb594305f70c2eE:
0000000000000000 <_ZN3foo3bar17h1acb594305f70c2eE>:
0: 48 83 ec 10 sub $0x10,%rsp
4: 48 8d 44 24 08 lea 0x8(%rsp),%rax
9: 48 89 04 24 mov %rax,(%rsp)
d: 48 8b 04 24 mov (%rsp),%rax
11: 48 83 c4 10 add $0x10,%rsp
15: c3 retq
$ objdump -s -j .stack_sizes foo.o
foo.o: file format elf64-x86-64
Contents of section .stack_sizes:
0000 00000000 00000000 00 .........
Contents of section .stack_sizes:
0000 00000000 00000000 10 .........
```
It's important to note that linkers will discard this linker section by default.
To preserve the section you can use a linker script like the one shown below.
``` text
/* file: keep-stack-sizes.x */
SECTIONS
{
/* `INFO` makes the section not allocatable so it won't be loaded into memory */
.stack_sizes (INFO) :
{
KEEP(*(.stack_sizes));
}
}
```
The linker script must be passed to the linker using a rustc flag like `-C
link-arg`.
```
// file: src/main.rs
use std::ptr;
#[inline(never)]
fn main() {
let xs = [0u32; 2];
// force LLVM to allocate `xs` on the stack
unsafe { ptr::read_volatile(&xs.as_ptr()); }
}
```
``` console
$ RUSTFLAGS="-Z emit-stack-sizes" cargo build --release
$ size -A target/release/hello | grep stack_sizes || echo section was not found
section was not found
$ RUSTFLAGS="-Z emit-stack-sizes" cargo rustc --release -- \
-C link-arg=-Wl,-Tkeep-stack-sizes.x \
-C link-arg=-N
$ size -A target/release/hello | grep stack_sizes
.stack_sizes 90 176272
$ # non-allocatable section (flags don't contain the "A" (alloc) flag)
$ readelf -S target/release/hello
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
(..)
[1031] .stack_sizes PROGBITS 000000000002b090 0002b0f0
000000000000005a 0000000000000000 L 5 0 1
$ objdump -s -j .stack_sizes target/release/hello
target/release/hello: file format elf64-x86-64
Contents of section .stack_sizes:
2b090 c0040000 00000000 08f00400 00000000 ................
2b0a0 00080005 00000000 00000810 05000000 ................
2b0b0 00000000 20050000 00000000 10400500 .... ........@..
2b0c0 00000000 00087005 00000000 00000080 ......p.........
2b0d0 05000000 00000000 90050000 00000000 ................
2b0e0 00a00500 00000000 0000 ..........
```
> Author note: I'm not entirely sure why, in *this* case, `-N` is required in
> addition to `-Tkeep-stack-sizes.x`. For example, it's not required when
> producing statically linked files for the ARM Cortex-M architecture.

View file

@ -322,11 +322,8 @@ class RustStdBTreeSetPrinter(object):
def children(self):
(length, data_ptr) = \
rustpp.extract_length_and_ptr_from_std_btreeset(self.__val)
leaf_node = GdbValue(data_ptr.get_wrapped_value().dereference())
maybe_uninit_keys = leaf_node.get_child_at_index(3)
manually_drop_keys = maybe_uninit_keys.get_child_at_index(1)
keys = manually_drop_keys.get_child_at_index(0)
gdb_ptr = keys.get_wrapped_value()
val = GdbValue(data_ptr.get_wrapped_value().dereference()).get_child_at_index(3)
gdb_ptr = val.get_wrapped_value()
for index in xrange(length):
yield (str(index), gdb_ptr[index])
@ -348,14 +345,9 @@ class RustStdBTreeMapPrinter(object):
def children(self):
(length, data_ptr) = \
rustpp.extract_length_and_ptr_from_std_btreemap(self.__val)
leaf_node = GdbValue(data_ptr.get_wrapped_value().dereference())
maybe_uninit_keys = leaf_node.get_child_at_index(3)
manually_drop_keys = maybe_uninit_keys.get_child_at_index(1)
keys = manually_drop_keys.get_child_at_index(0)
keys = GdbValue(data_ptr.get_wrapped_value().dereference()).get_child_at_index(3)
keys_ptr = keys.get_wrapped_value()
maybe_uninit_vals = leaf_node.get_child_at_index(4)
manually_drop_vals = maybe_uninit_vals.get_child_at_index(1)
vals = manually_drop_vals.get_child_at_index(0)
vals = GdbValue(data_ptr.get_wrapped_value().dereference()).get_child_at_index(4)
vals_ptr = vals.get_wrapped_value()
for index in xrange(length):
yield (str(index), keys_ptr[index])

View file

@ -42,7 +42,7 @@
// This implies that even an empty internal node has at least one edge.
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::mem;
use core::ptr::{self, Unique, NonNull};
use core::slice;
@ -73,7 +73,7 @@ struct LeafNode<K, V> {
/// This node's index into the parent node's `edges` array.
/// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`.
/// This is only guaranteed to be initialized when `parent` is nonnull.
parent_idx: MaybeUninit<u16>,
parent_idx: u16,
/// The number of keys and values this node stores.
///
@ -83,8 +83,8 @@ struct LeafNode<K, V> {
/// The arrays storing the actual data of the node. Only the first `len` elements of each
/// array are initialized and valid.
keys: MaybeUninit<[K; CAPACITY]>,
vals: MaybeUninit<[V; CAPACITY]>,
keys: [K; CAPACITY],
vals: [V; CAPACITY],
}
impl<K, V> LeafNode<K, V> {
@ -94,10 +94,10 @@ impl<K, V> LeafNode<K, V> {
LeafNode {
// As a general policy, we leave fields uninitialized if they can be, as this should
// be both slightly faster and easier to track in Valgrind.
keys: MaybeUninit::uninitialized(),
vals: MaybeUninit::uninitialized(),
keys: mem::uninitialized(),
vals: mem::uninitialized(),
parent: ptr::null(),
parent_idx: MaybeUninit::uninitialized(),
parent_idx: mem::uninitialized(),
len: 0
}
}
@ -115,10 +115,10 @@ unsafe impl Sync for LeafNode<(), ()> {}
// ever take a pointer past the first key.
static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode {
parent: ptr::null(),
parent_idx: MaybeUninit::uninitialized(),
parent_idx: 0,
len: 0,
keys: MaybeUninit::uninitialized(),
vals: MaybeUninit::uninitialized(),
keys: [(); CAPACITY],
vals: [(); CAPACITY],
};
/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden
@ -430,7 +430,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
root: self.root,
_marker: PhantomData
},
idx: unsafe { usize::from(*self.as_leaf().parent_idx.get_ref()) },
idx: self.as_leaf().parent_idx as usize,
_marker: PhantomData
})
} else {
@ -567,7 +567,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
// the node, which is allowed by LLVM.
unsafe {
slice::from_raw_parts(
self.as_leaf().keys.as_ptr() as *const K,
self.as_leaf().keys.as_ptr(),
self.len()
)
}
@ -578,7 +578,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
debug_assert!(!self.is_shared_root());
unsafe {
slice::from_raw_parts(
self.as_leaf().vals.as_ptr() as *const V,
self.as_leaf().vals.as_ptr(),
self.len()
)
}
@ -605,7 +605,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
} else {
unsafe {
slice::from_raw_parts_mut(
self.as_leaf_mut().keys.get_mut() as *mut [K] as *mut K,
&mut self.as_leaf_mut().keys as *mut [K] as *mut K,
self.len()
)
}
@ -616,7 +616,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
debug_assert!(!self.is_shared_root());
unsafe {
slice::from_raw_parts_mut(
self.as_leaf_mut().vals.get_mut() as *mut [V] as *mut V,
&mut self.as_leaf_mut().vals as *mut [V] as *mut V,
self.len()
)
}
@ -1013,7 +1013,7 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
let ptr = self.node.as_internal_mut() as *mut _;
let mut child = self.descend();
child.as_leaf_mut().parent = ptr;
child.as_leaf_mut().parent_idx.set(idx);
child.as_leaf_mut().parent_idx = idx;
}
/// Unsafely asserts to the compiler some static information about whether the underlying
@ -1152,12 +1152,12 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::KV>
ptr::copy_nonoverlapping(
self.node.keys().as_ptr().add(self.idx + 1),
new_node.keys.as_mut_ptr() as *mut K,
new_node.keys.as_mut_ptr(),
new_len
);
ptr::copy_nonoverlapping(
self.node.vals().as_ptr().add(self.idx + 1),
new_node.vals.as_mut_ptr() as *mut V,
new_node.vals.as_mut_ptr(),
new_len
);
@ -1210,12 +1210,12 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
ptr::copy_nonoverlapping(
self.node.keys().as_ptr().add(self.idx + 1),
new_node.data.keys.as_mut_ptr() as *mut K,
new_node.data.keys.as_mut_ptr(),
new_len
);
ptr::copy_nonoverlapping(
self.node.vals().as_ptr().add(self.idx + 1),
new_node.data.vals.as_mut_ptr() as *mut V,
new_node.data.vals.as_mut_ptr(),
new_len
);
ptr::copy_nonoverlapping(

View file

@ -120,7 +120,6 @@
#![feature(rustc_const_unstable)]
#![feature(const_vec_new)]
#![feature(slice_partition_dedup)]
#![feature(maybe_uninit)]
// Allow testing this library

View file

@ -9,7 +9,7 @@
// except according to those terms.
use fmt::{Formatter, Result, LowerExp, UpperExp, Display, Debug};
use mem::MaybeUninit;
use mem;
use num::flt2dec;
// Don't inline this so callers don't use the stack space this function
@ -20,11 +20,11 @@ fn float_to_decimal_common_exact<T>(fmt: &mut Formatter, num: &T,
where T: flt2dec::DecodableFloat
{
unsafe {
let mut buf = MaybeUninit::<[u8; 1024]>::uninitialized(); // enough for f32 and f64
let mut parts = MaybeUninit::<[flt2dec::Part; 4]>::uninitialized();
let mut buf: [u8; 1024] = mem::uninitialized(); // enough for f32 and f64
let mut parts: [flt2dec::Part; 4] = mem::uninitialized();
let formatted = flt2dec::to_exact_fixed_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
false, buf.get_mut(), parts.get_mut());
false, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
}
@ -38,11 +38,10 @@ fn float_to_decimal_common_shortest<T>(fmt: &mut Formatter, num: &T,
{
unsafe {
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninitialized();
let mut parts = MaybeUninit::<[flt2dec::Part; 4]>::uninitialized();
let mut buf: [u8; flt2dec::MAX_SIG_DIGITS] = mem::uninitialized();
let mut parts: [flt2dec::Part; 4] = mem::uninitialized();
let formatted = flt2dec::to_shortest_str(flt2dec::strategy::grisu::format_shortest, *num,
sign, precision, false, buf.get_mut(),
parts.get_mut());
sign, precision, false, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
}
@ -76,11 +75,11 @@ fn float_to_exponential_common_exact<T>(fmt: &mut Formatter, num: &T,
where T: flt2dec::DecodableFloat
{
unsafe {
let mut buf = MaybeUninit::<[u8; 1024]>::uninitialized(); // enough for f32 and f64
let mut parts = MaybeUninit::<[flt2dec::Part; 6]>::uninitialized();
let mut buf: [u8; 1024] = mem::uninitialized(); // enough for f32 and f64
let mut parts: [flt2dec::Part; 6] = mem::uninitialized();
let formatted = flt2dec::to_exact_exp_str(flt2dec::strategy::grisu::format_exact,
*num, sign, precision,
upper, buf.get_mut(), parts.get_mut());
upper, &mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
}
@ -95,11 +94,11 @@ fn float_to_exponential_common_shortest<T>(fmt: &mut Formatter,
{
unsafe {
// enough for f32 and f64
let mut buf = MaybeUninit::<[u8; flt2dec::MAX_SIG_DIGITS]>::uninitialized();
let mut parts = MaybeUninit::<[flt2dec::Part; 6]>::uninitialized();
let mut buf: [u8; flt2dec::MAX_SIG_DIGITS] = mem::uninitialized();
let mut parts: [flt2dec::Part; 6] = mem::uninitialized();
let formatted = flt2dec::to_shortest_exp_str(flt2dec::strategy::grisu::format_shortest,
*num, sign, (0, 0), upper,
buf.get_mut(), parts.get_mut());
&mut buf, &mut parts);
fmt.pad_formatted_parts(&formatted)
}
}

View file

@ -246,8 +246,6 @@ macro_rules! test_v512 { ($item:item) => {}; }
#[allow(unused_macros)]
macro_rules! vector_impl { ($([$f:ident, $($args:tt)*]),*) => { $($f!($($args)*);)* } }
#[path = "../stdsimd/coresimd/mod.rs"]
// replacing uses of mem::{uninitialized,zeroed} with MaybeUninit needs to be in the stdsimd repo
#[allow(deprecated)]
#[allow(missing_docs, missing_debug_implementations, dead_code, unused_imports)]
#[unstable(feature = "stdsimd", issue = "48556")]
#[cfg(not(stage0))] // allow changes to how stdsimd works in stage0

View file

@ -293,21 +293,21 @@ where
}
#[unstable(feature = "pin", issue = "49150")]
impl<'a, P: fmt::Debug> fmt::Debug for Pin<P> {
impl<P: fmt::Debug> fmt::Debug for Pin<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.pointer, f)
}
}
#[unstable(feature = "pin", issue = "49150")]
impl<'a, P: fmt::Display> fmt::Display for Pin<P> {
impl<P: fmt::Display> fmt::Display for Pin<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.pointer, f)
}
}
#[unstable(feature = "pin", issue = "49150")]
impl<'a, P: fmt::Pointer> fmt::Pointer for Pin<P> {
impl<P: fmt::Pointer> fmt::Pointer for Pin<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.pointer, f)
}
@ -319,10 +319,10 @@ impl<'a, P: fmt::Pointer> fmt::Pointer for Pin<P> {
// for other reasons, though, so we just need to take care not to allow such
// impls to land in std.
#[unstable(feature = "pin", issue = "49150")]
impl<'a, P, U> CoerceUnsized<Pin<U>> for Pin<P>
impl<P, U> CoerceUnsized<Pin<U>> for Pin<P>
where
P: CoerceUnsized<U>,
{}
#[unstable(feature = "pin", issue = "49150")]
impl<'a, P> Unpin for Pin<P> {}
impl<P> Unpin for Pin<P> {}

View file

@ -79,7 +79,7 @@ use ops::CoerceUnsized;
use fmt;
use hash;
use marker::{PhantomData, Unsize};
use mem::{self, MaybeUninit};
use mem;
use nonzero::NonZero;
use cmp::Ordering::{self, Less, Equal, Greater};
@ -294,12 +294,16 @@ pub const fn null_mut<T>() -> *mut T { 0 as *mut T }
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn swap<T>(x: *mut T, y: *mut T) {
// Give ourselves some scratch space to work with
let mut tmp = MaybeUninit::<T>::uninitialized();
let mut tmp: T = mem::uninitialized();
// Perform the swap
copy_nonoverlapping(x, tmp.as_mut_ptr(), 1);
copy_nonoverlapping(x, &mut tmp, 1);
copy(y, x, 1); // `x` and `y` may overlap
copy_nonoverlapping(tmp.get_ref(), y, 1);
copy_nonoverlapping(&tmp, y, 1);
// y and t now point to the same thing, but we need to completely forget `tmp`
// because it's no longer relevant.
mem::forget(tmp);
}
/// Swaps `count * size_of::<T>()` bytes between the two regions of memory
@ -386,8 +390,8 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
while i + block_size <= len {
// Create some uninitialized memory as scratch space
// Declaring `t` here avoids aligning the stack when this loop is unused
let mut t = mem::MaybeUninit::<Block>::uninitialized();
let t = t.as_mut_ptr() as *mut u8;
let mut t: Block = mem::uninitialized();
let t = &mut t as *mut _ as *mut u8;
let x = x.add(i);
let y = y.add(i);
@ -401,10 +405,10 @@ unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
if i < len {
// Swap any remaining bytes
let mut t = mem::MaybeUninit::<UnalignedBlock>::uninitialized();
let mut t: UnalignedBlock = mem::uninitialized();
let rem = len - i;
let t = t.as_mut_ptr() as *mut u8;
let t = &mut t as *mut _ as *mut u8;
let x = x.add(i);
let y = y.add(i);
@ -569,9 +573,9 @@ pub unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub unsafe fn read<T>(src: *const T) -> T {
let mut tmp = MaybeUninit::<T>::uninitialized();
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
tmp.into_inner()
let mut tmp: T = mem::uninitialized();
copy_nonoverlapping(src, &mut tmp, 1);
tmp
}
/// Reads the value from `src` without moving it. This leaves the
@ -636,11 +640,11 @@ pub unsafe fn read<T>(src: *const T) -> T {
#[inline]
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
pub unsafe fn read_unaligned<T>(src: *const T) -> T {
let mut tmp = MaybeUninit::<T>::uninitialized();
let mut tmp: T = mem::uninitialized();
copy_nonoverlapping(src as *const u8,
tmp.as_mut_ptr() as *mut u8,
&mut tmp as *mut T as *mut u8,
mem::size_of::<T>());
tmp.into_inner()
tmp
}
/// Overwrites a memory location with the given value without reading or

View file

@ -9,7 +9,7 @@
// except according to those terms.
use cmp;
use mem::{self, MaybeUninit};
use mem;
use ptr;
/// Rotation is much faster if it has access to a little bit of memory. This
@ -26,6 +26,12 @@ union RawArray<T> {
}
impl<T> RawArray<T> {
fn new() -> Self {
unsafe { mem::uninitialized() }
}
fn ptr(&self) -> *mut T {
unsafe { &self.typed as *const T as *mut T }
}
fn cap() -> usize {
if mem::size_of::<T>() == 0 {
usize::max_value()
@ -82,8 +88,8 @@ pub unsafe fn ptr_rotate<T>(mut left: usize, mid: *mut T, mut right: usize) {
}
}
let mut rawarray = MaybeUninit::<RawArray<T>>::uninitialized();
let buf = &mut (*rawarray.as_mut_ptr()).typed as *mut [T; 2] as *mut T;
let rawarray = RawArray::new();
let buf = rawarray.ptr();
let dim = mid.sub(left).add(right);
if left <= right {

View file

@ -17,7 +17,7 @@
//! stable sorting implementation.
use cmp;
use mem::{self, MaybeUninit};
use mem;
use ptr;
/// When dropped, copies from `src` into `dest`.
@ -226,14 +226,14 @@ fn partition_in_blocks<T, F>(v: &mut [T], pivot: &T, is_less: &mut F) -> usize
let mut block_l = BLOCK;
let mut start_l = ptr::null_mut();
let mut end_l = ptr::null_mut();
let mut offsets_l = MaybeUninit::<[u8; BLOCK]>::uninitialized();
let mut offsets_l: [u8; BLOCK] = unsafe { mem::uninitialized() };
// The current block on the right side (from `r.sub(block_r)` to `r`).
let mut r = unsafe { l.add(v.len()) };
let mut block_r = BLOCK;
let mut start_r = ptr::null_mut();
let mut end_r = ptr::null_mut();
let mut offsets_r = MaybeUninit::<[u8; BLOCK]>::uninitialized();
let mut offsets_r: [u8; BLOCK] = unsafe { mem::uninitialized() };
// FIXME: When we get VLAs, try creating one array of length `min(v.len(), 2 * BLOCK)` rather
// than two fixed-size arrays of length `BLOCK`. VLAs might be more cache-efficient.
@ -272,8 +272,8 @@ fn partition_in_blocks<T, F>(v: &mut [T], pivot: &T, is_less: &mut F) -> usize
if start_l == end_l {
// Trace `block_l` elements from the left side.
start_l = offsets_l.as_mut_ptr() as *mut u8;
end_l = offsets_l.as_mut_ptr() as *mut u8;
start_l = offsets_l.as_mut_ptr();
end_l = offsets_l.as_mut_ptr();
let mut elem = l;
for i in 0..block_l {
@ -288,8 +288,8 @@ fn partition_in_blocks<T, F>(v: &mut [T], pivot: &T, is_less: &mut F) -> usize
if start_r == end_r {
// Trace `block_r` elements from the right side.
start_r = offsets_r.as_mut_ptr() as *mut u8;
end_r = offsets_r.as_mut_ptr() as *mut u8;
start_r = offsets_r.as_mut_ptr();
end_r = offsets_r.as_mut_ptr();
let mut elem = r;
for i in 0..block_r {

View file

@ -287,6 +287,31 @@ impl DefPath {
s
}
/// Return filename friendly string of the DefPah with the
/// crate-prefix.
pub fn to_string_friendly<F>(&self, crate_imported_name: F) -> String
where F: FnOnce(CrateNum) -> Symbol
{
let crate_name_str = crate_imported_name(self.krate).as_str();
let mut s = String::with_capacity(crate_name_str.len() + self.data.len() * 16);
write!(s, "::{}", crate_name_str).unwrap();
for component in &self.data {
if component.disambiguator == 0 {
write!(s, "::{}", component.data.as_interned_str()).unwrap();
} else {
write!(s,
"{}[{}]",
component.data.as_interned_str(),
component.disambiguator)
.unwrap();
}
}
s
}
/// Return filename friendly string of the DefPah without
/// the crate-prefix. This method is useful if you don't have
/// a TyCtxt available.

View file

@ -55,23 +55,22 @@
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.
use infer;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use super::region_constraints::GenericKind;
use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use infer::{self, SuppressRegionErrors};
use std::{cmp, fmt};
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use hir;
use hir::Node;
use hir::def_id::DefId;
use hir::Node;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TypeFoldable, TyKind};
use ty::error::TypeError;
use session::config::BorrowckMode;
use std::{cmp, fmt};
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use traits::{ObligationCause, ObligationCauseCode};
use ty::error::TypeError;
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TyKind, TypeFoldable};
mod note;
@ -153,8 +152,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
// We shouldn't encounter an error message with ReClosureBound.
ty::ReCanonical(..) |
ty::ReClosureBound(..) => {
ty::ReCanonical(..) | ty::ReClosureBound(..) => {
bug!("encountered unexpected ReClosureBound: {:?}", region,);
}
};
@ -176,9 +174,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
self.msg_span_from_early_bound_and_free_regions(region)
},
}
ty::ReStatic => ("the static lifetime".to_owned(), None),
_ => bug!("{:?}", region),
}
@ -197,25 +195,28 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Some(Node::Item(it)) => Self::item_scope_tag(&it),
Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
_ => unreachable!()
_ => unreachable!(),
};
let (prefix, span) = match *region {
ty::ReEarlyBound(ref br) => {
let mut sp = cm.def_span(self.hir.span(node));
if let Some(param) = self.hir.get_generics(scope).and_then(|generics| {
generics.get_named(&br.name)
}) {
if let Some(param) = self.hir
.get_generics(scope)
.and_then(|generics| generics.get_named(&br.name))
{
sp = param.span;
}
(format!("the lifetime {} as defined on", br.name), sp)
}
ty::ReFree(ty::FreeRegion {
bound_region: ty::BoundRegion::BrNamed(_, ref name), ..
bound_region: ty::BoundRegion::BrNamed(_, ref name),
..
}) => {
let mut sp = cm.def_span(self.hir.span(node));
if let Some(param) = self.hir.get_generics(scope).and_then(|generics| {
generics.get_named(&name)
}) {
if let Some(param) = self.hir
.get_generics(scope)
.and_then(|generics| generics.get_named(&name))
{
sp = param.span;
}
(format!("the lifetime {} as defined on", name), sp)
@ -278,9 +279,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str {
match item.node {
hir::ImplItemKind::Method(..) => "method body",
hir::ImplItemKind::Const(..) |
hir::ImplItemKind::Existential(..) |
hir::ImplItemKind::Type(..) => "associated item",
hir::ImplItemKind::Const(..)
| hir::ImplItemKind::Existential(..)
| hir::ImplItemKind::Type(..) => "associated item",
}
}
@ -298,20 +299,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
&self,
region_scope_tree: &region::ScopeTree,
errors: &Vec<RegionResolutionError<'tcx>>,
will_later_be_reported_by_nll: bool,
suppress: SuppressRegionErrors,
) {
debug!("report_region_errors(): {} errors to start", errors.len());
debug!(
"report_region_errors(): {} errors to start, suppress = {:?}",
errors.len(),
suppress
);
// If the errors will later be reported by NLL, choose wether to display them or not based
// on the borrowck mode
if will_later_be_reported_by_nll {
match self.tcx.borrowck_mode() {
// If we're on AST or Migrate mode, report AST region errors
BorrowckMode::Ast | BorrowckMode::Migrate => {},
// If we're on MIR or Compare mode, don't report AST region errors as they should
// be reported by NLL
BorrowckMode::Compare | BorrowckMode::Mir => return,
}
if suppress.suppressed() {
return;
}
// try to pre-process the errors, which will group some of them
@ -482,17 +479,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
} else {
err.span_label(arm_span, msg);
}
},
hir::MatchSource::TryDesugar => { // Issue #51632
}
hir::MatchSource::TryDesugar => {
// Issue #51632
if let Ok(try_snippet) = self.tcx.sess.source_map().span_to_snippet(arm_span) {
err.span_suggestion_with_applicability(
arm_span,
"try wrapping with a success variant",
format!("Ok({})", try_snippet),
Applicability::MachineApplicable
Applicability::MachineApplicable,
);
}
},
}
_ => {
let msg = "match arm with an incompatible type";
if self.tcx.sess.source_map().is_multiline(arm_span) {
@ -641,16 +639,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
fn strip_generic_default_params(
&self,
def_id: DefId,
substs: &ty::subst::Substs<'tcx>
substs: &ty::subst::Substs<'tcx>,
) -> &'tcx ty::subst::Substs<'tcx> {
let generics = self.tcx.generics_of(def_id);
let mut num_supplied_defaults = 0;
let mut type_params = generics.params.iter().rev().filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
}).peekable();
let mut type_params = generics
.params
.iter()
.rev()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => None,
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
})
.peekable();
let has_default = {
let has_default = type_params.peek().map(|(_, has_default)| has_default);
*has_default.unwrap_or(&false)
@ -684,10 +687,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
| (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_)))
| (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_)))
| (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Float(_))
| (
&ty::Infer(ty::InferTy::FloatVar(_)),
&ty::Infer(ty::InferTy::FloatVar(_)),
) => true,
| (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_))) => {
true
}
_ => false,
}
}
@ -703,11 +705,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
"&{}{}{}",
r,
if r == "" { "" } else { " " },
if mutbl == hir::MutMutable {
"mut "
} else {
""
}
if mutbl == hir::MutMutable { "mut " } else { "" }
));
s.push_normal(ty.to_string());
}
@ -738,9 +736,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let common_len = cmp::min(len1, len2);
let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
let common_default_params =
remainder1.iter().rev().zip(remainder2.iter().rev())
.filter(|(a, b)| a == b).count();
let common_default_params = remainder1
.iter()
.rev()
.zip(remainder2.iter().rev())
.filter(|(a, b)| a == b)
.count();
let len = sub1.len() - common_default_params;
// Only draw `<...>` if there're lifetime/type arguments.
@ -866,8 +867,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
// When encountering &T != &mut T, highlight only the borrow
(&ty::Ref(r1, ref_ty1, mutbl1),
&ty::Ref(r2, ref_ty2, mutbl2)) if equals(&ref_ty1, &ref_ty2) => {
(&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2))
if equals(&ref_ty1, &ref_ty2) =>
{
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
@ -1068,11 +1070,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) {
self.construct_generic_bound_failure(region_scope_tree,
span,
origin,
bound_kind,
sub)
self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub)
.emit()
}
@ -1083,8 +1081,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
origin: Option<SubregionOrigin<'tcx>>,
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) -> DiagnosticBuilder<'a>
{
) -> DiagnosticBuilder<'a> {
// Attempt to obtain the span of the parameter so we can
// suggest adding an explicit lifetime bound to it.
let type_param_span = match (self.in_progress_tables, bound_kind) {
@ -1161,8 +1158,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let tail = if has_lifetimes { " + " } else { "" };
let suggestion = format!("{}: {}{}", bound_kind, sub, tail);
err.span_suggestion_short_with_applicability(
sp, consider, suggestion,
Applicability::MaybeIncorrect // Issue #41966
sp,
consider,
suggestion,
Applicability::MaybeIncorrect, // Issue #41966
);
} else {
err.help(consider);
@ -1358,12 +1357,10 @@ impl<'tcx> ObligationCause<'tcx> {
match self.code {
CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
MatchExpressionArm { source, .. } => Error0308(match source {
hir::MatchSource::IfLetDesugar { .. } => {
"`if let` arms have incompatible types"
},
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have incompatible types",
hir::MatchSource::TryDesugar => {
"try expression alternatives have incompatible types"
},
}
_ => "match arms have incompatible types",
}),
IfExpression => Error0308("if and else have incompatible types"),

View file

@ -10,23 +10,26 @@
//! The code to do lexical region resolution.
use infer::SubregionOrigin;
use infer::RegionVariableOrigin;
use infer::region_constraints::Constraint;
use infer::region_constraints::GenericKind;
use infer::region_constraints::RegionConstraintData;
use infer::region_constraints::VarInfos;
use infer::region_constraints::VerifyBound;
use infer::RegionVariableOrigin;
use infer::SubregionOrigin;
use middle::free_region::RegionRelations;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{Graph, Direction, NodeIndex, INCOMING, OUTGOING};
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use std::fmt;
use std::u32;
use ty::{self, TyCtxt};
use ty::{Region, RegionVid};
use ty::fold::TypeFoldable;
use ty::{self, Ty, TyCtxt};
use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
use ty::{ReLateBound, ReScope, ReSkolemized, ReVar};
use ty::{Region, RegionVid};
mod graphviz;
@ -108,11 +111,15 @@ struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
}
impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
self.region_rels.tcx
}
fn infer_variable_values(
&mut self,
errors: &mut Vec<RegionResolutionError<'tcx>>,
) -> LexicalRegionResolutions<'tcx> {
let mut var_data = self.construct_var_data(self.region_rels.tcx);
let mut var_data = self.construct_var_data(self.tcx());
// Dorky hack to cause `dump_constraints` to only get called
// if debug mode is enabled:
@ -239,9 +246,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"Expanding value of {:?} from {:?} to {:?}",
b_vid,
cur_region,
lub
b_vid, cur_region, lub
);
*b_data = VarValue::Value(lub);
@ -254,18 +259,17 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
}
}
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
let tcx = self.region_rels.tcx;
let tcx = self.tcx();
match (a, b) {
(&ty::ReCanonical(..), _) |
(_, &ty::ReCanonical(..)) |
(&ty::ReClosureBound(..), _) |
(_, &ty::ReClosureBound(..)) |
(&ReLateBound(..), _) |
(_, &ReLateBound(..)) |
(&ReErased, _) |
(_, &ReErased) => {
(&ty::ReCanonical(..), _)
| (_, &ty::ReCanonical(..))
| (&ty::ReClosureBound(..), _)
| (_, &ty::ReClosureBound(..))
| (&ReLateBound(..), _)
| (_, &ReLateBound(..))
| (&ReErased, _)
| (_, &ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
@ -287,20 +291,20 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
);
}
(&ReEarlyBound(_), &ReScope(s_id)) |
(&ReScope(s_id), &ReEarlyBound(_)) |
(&ReFree(_), &ReScope(s_id)) |
(&ReScope(s_id), &ReFree(_)) => {
(&ReEarlyBound(_), &ReScope(s_id))
| (&ReScope(s_id), &ReEarlyBound(_))
| (&ReFree(_), &ReScope(s_id))
| (&ReScope(s_id), &ReFree(_)) => {
// A "free" region can be interpreted as "some region
// at least as big as fr.scope". So, we can
// reasonably compare free regions and scopes:
let fr_scope = match (a, b) {
(&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels
.region_scope_tree
.early_free_scope(self.region_rels.tcx, br),
.early_free_scope(self.tcx(), br),
(&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels
.region_scope_tree
.free_scope(self.region_rels.tcx, fr),
.free_scope(self.tcx(), fr),
_ => bug!(),
};
let r_id = self.region_rels
@ -332,10 +336,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
tcx.mk_region(ReScope(lub))
}
(&ReEarlyBound(_), &ReEarlyBound(_)) |
(&ReFree(_), &ReEarlyBound(_)) |
(&ReEarlyBound(_), &ReFree(_)) |
(&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b),
(&ReEarlyBound(_), &ReEarlyBound(_))
| (&ReFree(_), &ReEarlyBound(_))
| (&ReEarlyBound(_), &ReFree(_))
| (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b),
// For these types, we cannot define any additional
// relationship:
@ -358,8 +362,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
for (constraint, origin) in &self.data.constraints {
debug!(
"collect_errors: constraint={:?} origin={:?}",
constraint,
origin
constraint, origin
);
match *constraint {
Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => {
@ -374,9 +377,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
origin,
sub,
sup
origin, sub, sup
);
errors.push(RegionResolutionError::ConcreteFailure(
@ -402,10 +403,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
origin,
a_vid,
a_region,
b_region
origin, a_vid, a_region, b_region
);
*a_data = VarValue::ErrorValue;
}
@ -415,7 +413,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
for verify in &self.data.verifys {
debug!("collect_errors: verify={:?}", verify);
let sub = var_data.normalize(verify.region);
let sub = var_data.normalize(self.tcx(), verify.region);
// This was an inference variable which didn't get
// constrained, therefore it can be assume to hold.
@ -423,16 +421,15 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
continue;
}
if self.bound_is_met(&verify.bound, var_data, sub) {
let verify_kind_ty = verify.kind.to_ty(self.tcx());
if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) {
continue;
}
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?} <= {:?}",
verify.origin,
verify.region,
verify.bound
verify.origin, verify.region, verify.bound
);
errors.push(RegionResolutionError::GenericBoundFailure(
@ -580,10 +577,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
sup: {:?}",
origin,
node_idx,
lower_bound.region,
upper_bound.region
origin, node_idx, lower_bound.region, upper_bound.region
);
errors.push(RegionResolutionError::SubSupConflict(
origin,
@ -645,8 +639,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
debug!(
"collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx,
node_idx
orig_node_idx, node_idx
);
process_edges(&self.data, &mut state, graph, node_idx, dir);
@ -721,20 +714,26 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
&self,
bound: &VerifyBound<'tcx>,
var_values: &LexicalRegionResolutions<'tcx>,
generic_ty: Ty<'tcx>,
min: ty::Region<'tcx>,
) -> bool {
match bound {
VerifyBound::AnyRegion(rs) => rs.iter()
.map(|&r| var_values.normalize(r))
.any(|r| self.region_rels.is_subregion_of(min, r)),
VerifyBound::IfEq(k, b) => {
(var_values.normalize(self.region_rels.tcx, *k) == generic_ty)
&& self.bound_is_met(b, var_values, generic_ty, min)
}
VerifyBound::AllRegions(rs) => rs.iter()
.map(|&r| var_values.normalize(r))
.all(|r| self.region_rels.is_subregion_of(min, r)),
VerifyBound::OutlivedBy(r) =>
self.region_rels.is_subregion_of(
min,
var_values.normalize(self.tcx(), r),
),
VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)),
VerifyBound::AnyBound(bs) => bs.iter()
.any(|b| self.bound_is_met(b, var_values, generic_ty, min)),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)),
VerifyBound::AllBounds(bs) => bs.iter()
.all(|b| self.bound_is_met(b, var_values, generic_ty, min)),
}
}
}
@ -745,13 +744,15 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> {
}
}
impl<'tcx> LexicalRegionResolutions<'tcx> {
fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match *r {
ty::ReVar(rid) => self.resolve_var(rid),
fn normalize<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(&value, &mut false, |r, _db| match r {
ty::ReVar(rid) => self.resolve_var(*rid),
_ => r,
}
})
}
fn value(&self, rid: RegionVid) -> &VarValue<'tcx> {

View file

@ -24,6 +24,7 @@ use middle::free_region::RegionRelations;
use middle::lang_items;
use middle::region;
use rustc_data_structures::unify as ut;
use session::config::BorrowckMode;
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::BTreeMap;
use std::fmt;
@ -80,6 +81,38 @@ pub type Bound<T> = Option<T>;
pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result"
pub type FixupResult<T> = Result<T, FixupError>; // "fixup result"
/// A flag that is used to suppress region errors. This is normally
/// false, but sometimes -- when we are doing region checks that the
/// NLL borrow checker will also do -- it might be set to true.
#[derive(Copy, Clone, Default, Debug)]
pub struct SuppressRegionErrors {
suppressed: bool
}
impl SuppressRegionErrors {
pub fn suppressed(self) -> bool {
self.suppressed
}
/// Indicates that the MIR borrowck will repeat these region
/// checks, so we should ignore errors if NLL is (unconditionally)
/// enabled.
pub fn when_nll_is_enabled(tcx: TyCtxt<'_, '_, '_>) -> Self {
match tcx.borrowck_mode() {
// If we're on AST or Migrate mode, report AST region errors
BorrowckMode::Ast | BorrowckMode::Migrate => SuppressRegionErrors {
suppressed: false
},
// If we're on MIR or Compare mode, don't report AST region errors as they should
// be reported by NLL
BorrowckMode::Compare | BorrowckMode::Mir => SuppressRegionErrors {
suppressed: true
},
}
}
}
pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
@ -408,7 +441,7 @@ pub enum FixupError {
pub struct RegionObligation<'tcx> {
pub sub_region: ty::Region<'tcx>,
pub sup_type: Ty<'tcx>,
pub cause: ObligationCause<'tcx>,
pub origin: SubregionOrigin<'tcx>,
}
impl fmt::Display for FixupError {
@ -1039,34 +1072,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
self.resolve_regions_and_report_errors_inner(
region_context,
region_map,
outlives_env,
false,
)
}
/// Like `resolve_regions_and_report_errors`, but skips error
/// reporting if NLL is enabled. This is used for fn bodies where
/// the same error may later be reported by the NLL-based
/// inference.
pub fn resolve_regions_and_report_errors_unless_nll(
&self,
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
) {
self.resolve_regions_and_report_errors_inner(region_context, region_map, outlives_env, true)
}
fn resolve_regions_and_report_errors_inner(
&self,
region_context: DefId,
region_map: &region::ScopeTree,
outlives_env: &OutlivesEnvironment<'tcx>,
will_later_be_reported_by_nll: bool,
suppress: SuppressRegionErrors,
) {
assert!(
self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(),
@ -1098,7 +1104,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// this infcx was in use. This is totally hokey but
// otherwise we have a hard time separating legit region
// errors from silly ones.
self.report_region_errors(region_map, &errors, will_later_be_reported_by_nll);
self.report_region_errors(region_map, &errors, suppress);
}
}

View file

@ -8,13 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::{GenericKind, InferCtxt};
use infer::outlives::free_region_map::FreeRegionMap;
use traits::query::outlives_bounds::{self, OutlivesBound};
use ty::{self, Ty};
use infer::{GenericKind, InferCtxt};
use rustc_data_structures::fx::FxHashMap;
use syntax::ast;
use syntax_pos::Span;
use traits::query::outlives_bounds::{self, OutlivesBound};
use ty::{self, Ty};
/// The `OutlivesEnvironment` collects information about what outlives
/// what in a given type-checking setting. For example, if we have a
@ -39,15 +39,51 @@ use syntax_pos::Span;
pub struct OutlivesEnvironment<'tcx> {
param_env: ty::ParamEnv<'tcx>,
free_region_map: FreeRegionMap<'tcx>,
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
// Contains, for each body B that we are checking (that is, the fn
// item, but also any nested closures), the set of implied region
// bounds that are in scope in that particular body.
//
// Example:
//
// ```
// fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
// bar(x, y, |y: &'b T| { .. } // body B1)
// } // body B0
// ```
//
// Here, for body B0, the list would be `[T: 'a]`, because we
// infer that `T` must outlive `'a` from the implied bounds on the
// fn declaration.
//
// For the body B1, the list would be `[T: 'a, T: 'b]`, because we
// also can see that -- within the closure body! -- `T` must
// outlive `'b`. This is not necessarily true outside the closure
// body, since the closure may never be called.
//
// We collect this map as we descend the tree. We then use the
// results when proving outlives obligations like `T: 'x` later
// (e.g., if `T: 'x` must be proven within the body B1, then we
// know it is true if either `'a: 'x` or `'b: 'x`).
region_bound_pairs_map: FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>>,
// Used to compute `region_bound_pairs_map`: contains the set of
// in-scope region-bound pairs thus far.
region_bound_pairs_accum: RegionBoundPairs<'tcx>,
}
/// "Region-bound pairs" tracks outlives relations that are known to
/// be true, either because of explicit where clauses like `T: 'a` or
/// because of implied bounds.
pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
let mut env = OutlivesEnvironment {
param_env,
free_region_map: FreeRegionMap::new(),
region_bound_pairs: vec![],
region_bound_pairs_map: FxHashMap::default(),
region_bound_pairs_accum: vec![],
};
env.add_outlives_bounds(None, outlives_bounds::explicit_outlives_bounds(param_env));
@ -61,8 +97,8 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
}
/// Borrows current value of the `region_bound_pairs`.
pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] {
&self.region_bound_pairs
pub fn region_bound_pairs_map(&self) -> &FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>> {
&self.region_bound_pairs_map
}
/// Returns ownership of the `free_region_map`.
@ -108,12 +144,12 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
/// similar leaks around givens that seem equally suspicious, to
/// be honest. --nmatsakis
pub fn push_snapshot_pre_closure(&self) -> usize {
self.region_bound_pairs.len()
self.region_bound_pairs_accum.len()
}
/// See `push_snapshot_pre_closure`.
pub fn pop_snapshot_post_closure(&mut self, len: usize) {
self.region_bound_pairs.truncate(len);
self.region_bound_pairs_accum.truncate(len);
}
/// This method adds "implied bounds" into the outlives environment.
@ -149,6 +185,15 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
}
}
/// Save the current set of region-bound pairs under the given `body_id`.
pub fn save_implied_bounds(&mut self, body_id: ast::NodeId) {
let old = self.region_bound_pairs_map.insert(
body_id,
self.region_bound_pairs_accum.clone(),
);
assert!(old.is_none());
}
/// Processes outlives bounds that are known to hold, whether from implied or other sources.
///
/// The `infcx` parameter is optional; if the implied bounds may
@ -167,16 +212,18 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
for outlives_bound in outlives_bounds {
debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) |
OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b))
| OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => {
infcx
.expect("no infcx provided but region vars found")
.add_given(r_a, vid_b);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs
self.region_bound_pairs_accum
.push((r_a, GenericKind::Param(param_b)));
}
OutlivesBound::RegionSubProjection(r_a, projection_b) => {
self.region_bound_pairs
self.region_bound_pairs_accum
.push((r_a, GenericKind::Projection(projection_b)));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {

View file

@ -13,3 +13,4 @@
pub mod env;
pub mod free_region_map;
pub mod obligations;
pub mod verify;

View file

@ -69,13 +69,14 @@
//! might later infer `?U` to something like `&'b u32`, which would
//! imply that `'b: 'a`.
use hir::def_id::DefId;
use infer::outlives::env::RegionBoundPairs;
use infer::outlives::verify::VerifyBoundCx;
use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound};
use rustc_data_structures::fx::FxHashMap;
use syntax::ast;
use traits;
use traits::ObligationCause;
use ty::outlives::Component;
use ty::subst::{Subst, Substs};
use ty::{self, Ty, TyCtxt, TypeFoldable};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable};
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// Registers that the given region obligation must be resolved
@ -98,6 +99,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
.push((body_id, obligation));
}
pub fn register_region_obligation_with_cause(
&self,
sup_type: Ty<'tcx>,
sub_region: Region<'tcx>,
cause: &ObligationCause<'tcx>,
) {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
infer::RelateParamBound(cause.span, sup_type)
});
self.register_region_obligation(
cause.body_id,
RegionObligation {
sup_type,
sub_region,
origin,
},
);
}
/// Trait queries just want to pass back type obligations "as is"
pub fn take_registered_region_obligations(&self) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> {
::std::mem::replace(&mut *self.region_obligations.borrow_mut(), vec![])
@ -138,10 +159,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// processed.
pub fn process_registered_region_obligations(
&self,
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs_map: &FxHashMap<ast::NodeId, RegionBoundPairs<'tcx>>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
body_id: ast::NodeId,
) {
assert!(
!self.in_snapshot.get(),
@ -150,41 +170,39 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
debug!("process_registered_region_obligations()");
// pull out the region obligations with the given `body_id` (leaving the rest)
let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len());
{
let mut r_o = self.region_obligations.borrow_mut();
my_region_obligations.extend(
r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id)
.map(|(_, obligation)| obligation)
);
}
let my_region_obligations = self.take_registered_region_obligations();
let outlives = &mut TypeOutlives::new(
self,
self.tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
);
for RegionObligation {
sup_type,
sub_region,
cause,
} in my_region_obligations
for (
body_id,
RegionObligation {
sup_type,
sub_region,
origin,
},
) in my_region_obligations
{
debug!(
"process_registered_region_obligations: sup_type={:?} sub_region={:?} cause={:?}",
sup_type, sub_region, cause
"process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}",
sup_type, sub_region, origin
);
let origin = SubregionOrigin::from_obligation_cause(&cause, || {
infer::RelateParamBound(cause.span, sup_type)
});
let sup_type = self.resolve_type_vars_if_possible(&sup_type);
outlives.type_must_outlive(origin, sup_type, sub_region);
if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) {
let outlives = &mut TypeOutlives::new(
self,
self.tcx,
&region_bound_pairs,
implicit_region_bound,
param_env,
);
outlives.type_must_outlive(origin, sup_type, sub_region);
} else {
self.tcx.sess.delay_span_bug(
origin.span(),
&format!("no region-bound-pairs for {:?}", body_id),
)
}
}
}
@ -192,7 +210,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
/// registered in advance.
pub fn type_must_outlive(
&self,
region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
@ -225,9 +243,7 @@ where
// of these fields.
delegate: D,
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
verify_bound: VerifyBoundCx<'cx, 'gcx, 'tcx>,
}
pub trait TypeOutlivesDelegate<'tcx> {
@ -254,16 +270,19 @@ where
pub fn new(
delegate: D,
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
Self {
delegate,
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
verify_bound: VerifyBoundCx::new(
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
),
}
}
@ -302,7 +321,8 @@ where
let origin = origin.clone();
match component {
Component::Region(region1) => {
self.delegate.push_sub_region_constraint(origin, region, region1);
self.delegate
.push_sub_region_constraint(origin, region, region1);
}
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, param_ty);
@ -337,8 +357,8 @@ where
region, param_ty, origin
);
let verify_bound = self.param_bound(param_ty);
let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
self.delegate
.push_verify(origin, generic, region, verify_bound);
}
@ -368,19 +388,22 @@ where
// rule might not apply (but another rule might). For now, we err
// on the side of adding too few edges into the graph.
// Compute the bounds we can derive from the environment or trait
// definition. We know that the projection outlives all the
// regions in this list.
let env_bounds = self.projection_declared_bounds(projection_ty);
// Compute the bounds we can derive from the environment. This
// is an "approximate" match -- in some cases, these bounds
// may not apply.
let approx_env_bounds = self.verify_bound
.projection_approx_declared_bounds_from_env(projection_ty);
debug!(
"projection_must_outlive: approx_env_bounds={:?}",
approx_env_bounds
);
debug!("projection_must_outlive: env_bounds={:?}", env_bounds);
// If we know that the projection outlives 'static, then we're
// done here.
if env_bounds.contains(&&ty::ReStatic) {
debug!("projection_must_outlive: 'static as declared bound");
return;
}
// Compute the bounds we can derive from the trait definition.
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> = self.verify_bound
.projection_declared_bounds_from_trait(projection_ty)
.collect();
// If declared bounds list is empty, the only applicable rule is
// OutlivesProjectionComponent. If there are inference variables,
@ -397,7 +420,7 @@ where
// inference variables, we use a verify constraint instead of adding
// edges, which winds up enforcing the same condition.
let needs_infer = projection_ty.needs_infer();
if env_bounds.is_empty() && needs_infer {
if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer {
debug!("projection_must_outlive: no declared bounds");
for component_ty in projection_ty.substs.types() {
@ -405,36 +428,38 @@ where
}
for r in projection_ty.substs.regions() {
self.delegate.push_sub_region_constraint(origin.clone(), region, r);
self.delegate
.push_sub_region_constraint(origin.clone(), region, r);
}
return;
}
// If we find that there is a unique declared bound `'b`, and this bound
// appears in the trait reference, then the best action is to require that `'b:'r`,
// so do that. This is best no matter what rule we use:
// If we found a unique bound `'b` from the trait, and we
// found nothing else from the environment, then the best
// action is to require that `'b: 'r`, so do that.
//
// - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
// the requirement that `'b:'r`
// - OutlivesProjectionComponent: this would require `'b:'r` in addition to
// other conditions
if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
let unique_bound = env_bounds[0];
// This is best no matter what rule we use:
//
// - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r`
// - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r`
// - OutlivesProjectionComponent: this would require `'b:'r`
// in addition to other conditions
if !trait_bounds.is_empty()
&& trait_bounds[1..]
.iter()
.chain(approx_env_bounds.iter().map(|b| &b.1))
.all(|b| *b == trait_bounds[0])
{
let unique_bound = trait_bounds[0];
debug!(
"projection_must_outlive: unique declared bound = {:?}",
"projection_must_outlive: unique trait bound = {:?}",
unique_bound
);
if projection_ty
.substs
.regions()
.any(|r| env_bounds.contains(&r))
{
debug!("projection_must_outlive: unique declared bound appears in trait ref");
self.delegate
.push_sub_region_constraint(origin.clone(), region, unique_bound);
return;
}
debug!("projection_must_outlive: unique declared bound appears in trait ref");
self.delegate
.push_sub_region_constraint(origin.clone(), region, unique_bound);
return;
}
// Fallback to verifying after the fact that there exists a
@ -442,216 +467,11 @@ where
// projection outlive; in some cases, this may add insufficient
// edges into the inference graph, leading to inference failures
// even though a satisfactory solution exists.
let verify_bound = self.projection_bound(env_bounds, projection_ty);
let generic = GenericKind::Projection(projection_ty);
let verify_bound = self.verify_bound.generic_bound(generic);
self.delegate
.push_verify(origin, generic.clone(), region, verify_bound);
}
fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
match ty.sty {
ty::Param(p) => self.param_bound(p),
ty::Projection(data) => {
let declared_bounds = self.projection_declared_bounds(data);
self.projection_bound(declared_bounds, data)
}
_ => self.recursive_type_bound(ty),
}
}
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
debug!("param_bound(param_ty={:?})", param_ty);
let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty));
// Add in the default bound of fn body that applies to all in
// scope type parameters:
param_bounds.extend(self.implicit_region_bound);
VerifyBound::AnyRegion(param_bounds)
}
fn projection_declared_bounds(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
// First assemble bounds from where clauses and traits.
let mut declared_bounds =
self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty));
declared_bounds
.extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty));
declared_bounds
}
fn projection_bound(
&self,
declared_bounds: Vec<ty::Region<'tcx>>,
projection_ty: ty::ProjectionTy<'tcx>,
) -> VerifyBound<'tcx> {
debug!(
"projection_bound(declared_bounds={:?}, projection_ty={:?})",
declared_bounds, projection_ty
);
// see the extensive comment in projection_must_outlive
let ty = self
.tcx
.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let recursive_bound = self.recursive_type_bound(ty);
VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
let mut bounds = ty.walk_shallow().map(|subty| self.type_bound(subty)).collect::<Vec<_>>();
let mut regions = ty.regions();
regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
bounds.push(VerifyBound::AllRegions(regions));
// remove bounds that must hold, since they are not interesting
bounds.retain(|b| !b.must_hold());
if bounds.len() == 1 {
bounds.pop().unwrap()
} else {
VerifyBound::AllBounds(bounds)
}
}
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::Region<'tcx>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
// parameter environments are already elaborated, so we don't
// have to worry about that. Comparing using `==` is a bit
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
let generic_ty = generic.to_ty(tcx);
let c_b = self.param_env.caller_bounds;
let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b);
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
// of known relations from the fn ctxt.
//
// This is crucial because otherwise code like this fails:
//
// fn foo<'a, A>(x: &'a A) { x.bar() }
//
// The problem is that the type of `x` is `&'a A`. To be
// well-formed, then, A must be lower-generic by `'a`, but we
// don't know that this holds from first principles.
for &(r, p) in self.region_bound_pairs {
debug!("generic={:?} p={:?}", generic, p);
if generic == p {
param_bounds.push(r);
}
}
param_bounds
}
/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
/// declared in the trait definition. For example, if the trait were
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// then this function would return `'x`. This is subject to the
/// limitations around higher-ranked bounds described in
/// `region_bounds_declared_on_associated_item`.
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::Region<'tcx>> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id);
for r in &mut bounds {
*r = r.subst(self.tcx, projection_ty.substs);
}
bounds
}
/// Given the def-id of an associated item, returns any region
/// bounds attached to that associated item from the trait definition.
///
/// For example:
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// If we were given the def-id of `Foo::Bar`, we would return
/// `'a`. You could then apply the substitutions from the
/// projection to convert this into your namespace. This also
/// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
/// the trait. In fact, it works by searching for just such a
/// where-clause.
///
/// It will not, however, work for higher-ranked bounds like:
///
/// ```rust
/// trait Foo<'a, 'b>
/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
/// {
/// type Bar;
/// }
/// ```
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> Vec<ty::Region<'tcx>> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
let trait_predicates = tcx.predicates_of(trait_def_id);
let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id);
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
self.collect_outlives_from_predicate_list(
identity_proj,
traits::elaborate_predicates(tcx, trait_predicates.predicates),
)
}
/// Searches through a predicate list for a predicate `T: 'a`.
///
/// Careful: does not elaborate predicates, and just uses `==`
/// when comparing `ty` for equality, so `ty` must be something
/// that does not involve inference variables and where you
/// otherwise want a precise match.
fn collect_outlives_from_predicate_list<I, P>(
&self,
ty: Ty<'tcx>,
predicates: I,
) -> Vec<ty::Region<'tcx>>
where
I: IntoIterator<Item = P>,
P: AsRef<ty::Predicate<'tcx>>,
{
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| p.no_late_bound_regions())
.filter(|p| p.0 == ty)
.map(|p| p.1)
.collect()
}
}
impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, 'tcx> {
@ -674,4 +494,3 @@ impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, '
self.verify_generic_bound(origin, kind, a, bound)
}
}

View file

@ -0,0 +1,329 @@
// Copyright 2012-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.
use hir::def_id::DefId;
use infer::outlives::env::RegionBoundPairs;
use infer::{GenericKind, VerifyBound};
use traits;
use ty::subst::{Subst, Substs};
use ty::{self, Ty, TyCtxt};
use util::captures::Captures;
/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a`
/// obligation into a series of `'a: 'b` constraints and "verifys", as
/// described on the module comment. The final constraints are emitted
/// via a "delegate" of type `D` -- this is usually the `infcx`, which
/// accrues them into the `region_obligations` code, but for NLL we
/// use something else.
pub struct VerifyBoundCx<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> {
pub fn new(
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_bound_pairs: &'cx RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
Self {
tcx,
region_bound_pairs,
implicit_region_bound,
param_env,
}
}
/// Returns a "verify bound" that encodes what we know about
/// `generic` and the regions it outlives.
pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> {
match generic {
GenericKind::Param(param_ty) => self.param_bound(param_ty),
GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty),
}
}
fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
match ty.sty {
ty::Param(p) => self.param_bound(p),
ty::Projection(data) => self.projection_bound(data),
_ => self.recursive_type_bound(ty),
}
}
fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
debug!("param_bound(param_ty={:?})", param_ty);
// Start with anything like `T: 'a` we can scrape from the
// environment
let param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty))
.into_iter()
.map(|outlives| outlives.1);
// Add in the default bound of fn body that applies to all in
// scope type parameters:
let param_bounds = param_bounds.chain(self.implicit_region_bound);
VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect())
}
/// Given a projection like `T::Item`, searches the environment
/// for where-clauses like `T::Item: 'a`. Returns the set of
/// regions `'a` that it finds.
///
/// This is an "approximate" check -- it may not find all
/// applicable bounds, and not all the bounds it returns can be
/// relied upon. In particular, this check ignores region
/// identity. So, for example, if we have `<T as
/// Trait<'0>>::Item` where `'0` is a region variable, and the
/// user has `<T as Trait<'a>>::Item: 'b` in the environment, then
/// the clause from the environment only applies if `'0 = 'a`,
/// which we don't know yet. But we would still include `'b` in
/// this list.
pub fn projection_approx_declared_bounds_from_env(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx);
let erased_projection_ty = self.tcx.erase_regions(&projection_ty);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| {
if let ty::Projection(..) = ty.sty {
let erased_ty = self.tcx.erase_regions(&ty);
erased_ty == erased_projection_ty
} else {
false
}
})
}
/// Searches the where clauses in scope for regions that
/// `projection_ty` is known to outlive. Currently requires an
/// exact match.
pub fn projection_declared_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
self.declared_projection_bounds_from_trait(projection_ty)
}
pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> {
debug!("projection_bound(projection_ty={:?})", projection_ty);
let projection_ty_as_ty =
self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs);
// Search the env for where clauses like `P: 'a`.
let env_bounds = self.projection_approx_declared_bounds_from_env(projection_ty)
.into_iter()
.map(|ty::OutlivesPredicate(ty, r)| {
let vb = VerifyBound::OutlivedBy(r);
if ty == projection_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
vb
} else {
VerifyBound::IfEq(ty, Box::new(vb))
}
});
// Extend with bounds that we can find from the trait.
let trait_bounds = self.projection_declared_bounds_from_trait(projection_ty)
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let ty = self.tcx
.mk_projection(projection_ty.item_def_id, projection_ty.substs);
let recursive_bound = self.recursive_type_bound(ty);
VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound)
}
fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
let mut bounds = ty.walk_shallow()
.map(|subty| self.type_bound(subty))
.collect::<Vec<_>>();
let mut regions = ty.regions();
regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
bounds.push(VerifyBound::AllBounds(
regions
.into_iter()
.map(|r| VerifyBound::OutlivedBy(r))
.collect(),
));
// remove bounds that must hold, since they are not interesting
bounds.retain(|b| !b.must_hold());
if bounds.len() == 1 {
bounds.pop().unwrap()
} else {
VerifyBound::AllBounds(bounds)
}
}
/// Searches the environment for where-clauses like `G: 'a` where
/// `G` is either some type parameter `T` or a projection like
/// `T::Item`. Returns a vector of the `'a` bounds it can find.
///
/// This is a conservative check -- it may not find all applicable
/// bounds, but all the bounds it returns can be relied upon.
fn declared_generic_bounds_from_env(
&self,
generic: GenericKind<'tcx>,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let generic_ty = generic.to_ty(self.tcx);
self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty)
}
fn declared_generic_bounds_from_env_with_compare_fn(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
let tcx = self.tcx;
// To start, collect bounds from user environment. Note that
// parameter environments are already elaborated, so we don't
// have to worry about that. Comparing using `==` is a bit
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
let c_b = self.param_env.caller_bounds;
let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b);
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
// of known relations from the fn ctxt.
//
// This is crucial because otherwise code like this fails:
//
// fn foo<'a, A>(x: &'a A) { x.bar() }
//
// The problem is that the type of `x` is `&'a A`. To be
// well-formed, then, A must be lower-generic by `'a`, but we
// don't know that this holds from first principles.
let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}",
(r, p)
);
let p_ty = p.to_ty(tcx);
if compare_ty(p_ty) {
Some(ty::OutlivesPredicate(p_ty, r))
} else {
None
}
});
param_bounds
.chain(from_region_bound_pairs)
.inspect(|bound| {
debug!(
"declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}",
bound
)
})
.collect()
}
/// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
/// declared in the trait definition. For example, if the trait were
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// then this function would return `'x`. This is subject to the
/// limitations around higher-ranked bounds described in
/// `region_bounds_declared_on_associated_item`.
fn declared_projection_bounds_from_trait(
&self,
projection_ty: ty::ProjectionTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
debug!("projection_bounds(projection_ty={:?})", projection_ty);
let tcx = self.tcx;
self.region_bounds_declared_on_associated_item(projection_ty.item_def_id)
.map(move |r| r.subst(tcx, projection_ty.substs))
}
/// Given the def-id of an associated item, returns any region
/// bounds attached to that associated item from the trait definition.
///
/// For example:
///
/// ```rust
/// trait Foo<'a> {
/// type Bar: 'a;
/// }
/// ```
///
/// If we were given the def-id of `Foo::Bar`, we would return
/// `'a`. You could then apply the substitutions from the
/// projection to convert this into your namespace. This also
/// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
/// the trait. In fact, it works by searching for just such a
/// where-clause.
///
/// It will not, however, work for higher-ranked bounds like:
///
/// ```rust
/// trait Foo<'a, 'b>
/// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
/// {
/// type Bar;
/// }
/// ```
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
fn region_bounds_declared_on_associated_item(
&self,
assoc_item_def_id: DefId,
) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'gcx> {
let tcx = self.tcx;
let assoc_item = tcx.associated_item(assoc_item_def_id);
let trait_def_id = assoc_item.container.assert_trait();
let trait_predicates = tcx.predicates_of(trait_def_id).predicates
.into_iter()
.map(|(p, _)| p)
.collect();
let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id);
let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
self.collect_outlives_from_predicate_list(
move |ty| ty == identity_proj,
traits::elaborate_predicates(tcx, trait_predicates),
).map(|b| b.1)
}
/// Searches through a predicate list for a predicate `T: 'a`.
///
/// Careful: does not elaborate predicates, and just uses `==`
/// when comparing `ty` for equality, so `ty` must be something
/// that does not involve inference variables and where you
/// otherwise want a precise match.
fn collect_outlives_from_predicate_list(
&self,
compare_ty: impl Fn(Ty<'tcx>) -> bool,
predicates: impl IntoIterator<Item = impl AsRef<ty::Predicate<'tcx>>>,
) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> {
predicates
.into_iter()
.filter_map(|p| p.as_ref().to_opt_type_outlives())
.filter_map(|p| p.no_late_bound_regions())
.filter(move |p| compare_ty(p.0))
}
}

View file

@ -155,29 +155,94 @@ pub enum GenericKind<'tcx> {
Projection(ty::ProjectionTy<'tcx>),
}
/// When we introduce a verification step, we wish to test that a
/// particular region (let's call it `'min`) meets some bound.
/// The bound is described the by the following grammar:
EnumTypeFoldableImpl! {
impl<'tcx> TypeFoldable<'tcx> for GenericKind<'tcx> {
(GenericKind::Param)(a),
(GenericKind::Projection)(a),
}
}
/// Describes the things that some `GenericKind` value G is known to
/// outlive. Each variant of `VerifyBound` can be thought of as a
/// function:
///
/// fn(min: Region) -> bool { .. }
///
/// where `true` means that the region `min` meets that `G: min`.
/// (False means nothing.)
///
/// So, for example, if we have the type `T` and we have in scope that
/// `T: 'a` and `T: 'b`, then the verify bound might be:
///
/// fn(min: Region) -> bool {
/// ('a: min) || ('b: min)
/// }
///
/// This is described with a `AnyRegion('a, 'b)` node.
#[derive(Debug, Clone)]
pub enum VerifyBound<'tcx> {
/// B = exists {R} --> some 'r in {R} must outlive 'min
/// Given a kind K and a bound B, expands to a function like the
/// following, where `G` is the generic for which this verify
/// bound was created:
///
/// Put another way, the subject value is known to outlive all
/// regions in {R}, so if any of those outlives 'min, then the
/// bound is met.
AnyRegion(Vec<Region<'tcx>>),
/// B = forall {R} --> all 'r in {R} must outlive 'min
/// fn(min) -> bool {
/// if G == K {
/// B(min)
/// } else {
/// false
/// }
/// }
///
/// Put another way, the subject value is known to outlive some
/// region in {R}, so if all of those outlives 'min, then the bound
/// is met.
AllRegions(Vec<Region<'tcx>>),
/// In other words, if the generic `G` that we are checking is
/// equal to `K`, then check the associated verify bound
/// (otherwise, false).
///
/// This is used when we have something in the environment that
/// may or may not be relevant, depending on the region inference
/// results. For example, we may have `where <T as
/// Trait<'a>>::Item: 'b` in our where clauses. If we are
/// generating the verify-bound for `<T as Trait<'0>>::Item`, then
/// this where-clause is only relevant if `'0` winds up inferred
/// to `'a`.
///
/// So we would compile to a verify-bound like
///
/// IfEq(<T as Trait<'a>>::Item, AnyRegion('a))
///
/// meaning, if the subject G is equal to `<T as Trait<'a>>::Item`
/// (after inference), and `'a: min`, then `G: min`.
IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>),
/// B = exists {B} --> 'min must meet some bound b in {B}
/// Given a region `R`, expands to the function:
///
/// fn(min) -> bool {
/// R: min
/// }
///
/// This is used when we can establish that `G: R` -- therefore,
/// if `R: min`, then by transitivity `G: min`.
OutlivedBy(Region<'tcx>),
/// Given a set of bounds `B`, expands to the function:
///
/// fn(min) -> bool {
/// exists (b in B) { b(min) }
/// }
///
/// In other words, if we meet some bound in `B`, that suffices.
/// This is used when all the bounds in `B` are known to apply to
/// G.
AnyBound(Vec<VerifyBound<'tcx>>),
/// B = forall {B} --> 'min must meet all bounds b in {B}
/// Given a set of bounds `B`, expands to the function:
///
/// fn(min) -> bool {
/// forall (b in B) { b(min) }
/// }
///
/// In other words, if we meet *all* bounds in `B`, that suffices.
/// This is used when *some* bound in `B` is known to suffice, but
/// we don't know which.
AllBounds(Vec<VerifyBound<'tcx>>),
}
@ -882,33 +947,23 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> {
}
impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> {
fn for_each_region(&self, f: &mut dyn FnMut(ty::Region<'tcx>)) {
match self {
&VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs {
f(r);
},
&VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs {
b.for_each_region(f);
},
}
}
pub fn must_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic),
&VerifyBound::AllRegions(ref bs) => bs.is_empty(),
&VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()),
VerifyBound::IfEq(..) => false,
VerifyBound::OutlivedBy(ty::ReStatic) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
}
pub fn cannot_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.is_empty(),
&VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty),
&VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()),
VerifyBound::IfEq(_, b) => b.cannot_hold(),
VerifyBound::OutlivedBy(ty::ReEmpty) => true,
VerifyBound::OutlivedBy(_) => false,
VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()),
VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()),
}
}

View file

@ -13,34 +13,39 @@ use super::*;
#[derive(Debug)]
pub(super) struct TaintSet<'tcx> {
directions: TaintDirections,
regions: FxHashSet<ty::Region<'tcx>>
regions: FxHashSet<ty::Region<'tcx>>,
}
impl<'tcx> TaintSet<'tcx> {
pub(super) fn new(directions: TaintDirections,
initial_region: ty::Region<'tcx>)
-> Self {
pub(super) fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self {
let mut regions = FxHashSet();
regions.insert(initial_region);
TaintSet { directions: directions, regions: regions }
TaintSet {
directions: directions,
regions: regions,
}
}
pub(super) fn fixed_point(&mut self,
tcx: TyCtxt<'_, '_, 'tcx>,
undo_log: &[UndoLogEntry<'tcx>],
verifys: &[Verify<'tcx>]) {
pub(super) fn fixed_point(
&mut self,
tcx: TyCtxt<'_, '_, 'tcx>,
undo_log: &[UndoLogEntry<'tcx>],
verifys: &[Verify<'tcx>],
) {
let mut prev_len = 0;
while prev_len < self.len() {
debug!("tainted: prev_len = {:?} new_len = {:?}",
prev_len, self.len());
debug!(
"tainted: prev_len = {:?} new_len = {:?}",
prev_len,
self.len()
);
prev_len = self.len();
for undo_entry in undo_log {
match undo_entry {
&AddConstraint(Constraint::VarSubVar(a, b)) => {
self.add_edge(tcx.mk_region(ReVar(a)),
tcx.mk_region(ReVar(b)));
self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
}
&AddConstraint(Constraint::RegSubVar(a, b)) => {
self.add_edge(a, tcx.mk_region(ReVar(b)));
@ -55,15 +60,13 @@ impl<'tcx> TaintSet<'tcx> {
self.add_edge(a, tcx.mk_region(ReVar(b)));
}
&AddVerify(i) => {
verifys[i].bound.for_each_region(&mut |b| {
self.add_edge(verifys[i].region, b);
});
span_bug!(
verifys[i].origin.span(),
"we never add verifications while doing higher-ranked things",
)
}
&Purged |
&AddCombination(..) |
&AddVar(..) |
&OpenSnapshot |
&CommitedSnapshot => {}
&Purged | &AddCombination(..) | &AddVar(..) | &OpenSnapshot
| &CommitedSnapshot => {}
}
}
}
@ -77,9 +80,7 @@ impl<'tcx> TaintSet<'tcx> {
self.regions.len()
}
fn add_edge(&mut self,
source: ty::Region<'tcx>,
target: ty::Region<'tcx>) {
fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) {
if self.directions.incoming {
if self.regions.contains(&target) {
self.regions.insert(source);
@ -93,4 +94,3 @@ impl<'tcx> TaintSet<'tcx> {
}
}
}

View file

@ -338,6 +338,12 @@ declare_lint! {
cannot be referred to by absolute paths"
}
declare_lint! {
pub EXPLICIT_OUTLIVES_REQUIREMENTS,
Allow,
"outlives requirements can be inferred"
}
/// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`.
pub mod parser {
declare_lint! {

View file

@ -1631,7 +1631,7 @@ impl<'tcx> Statement<'tcx> {
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum StatementKind<'tcx> {
/// Write the RHS Rvalue to the LHS Place.
Assign(Place<'tcx>, Rvalue<'tcx>),
Assign(Place<'tcx>, Box<Rvalue<'tcx>>),
/// This represents all the reading that a pattern match may do
/// (e.g. inspecting constants and discriminant values), and the
@ -1654,8 +1654,8 @@ pub enum StatementKind<'tcx> {
/// Execute a piece of inline Assembly.
InlineAsm {
asm: Box<InlineAsm>,
outputs: Vec<Place<'tcx>>,
inputs: Vec<Operand<'tcx>>,
outputs: Box<[Place<'tcx>]>,
inputs: Box<[Operand<'tcx>]>,
},
/// Assert the given places to be valid inhabitants of their type. These statements are

View file

@ -1385,6 +1385,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"run the self profiler"),
profile_json: bool = (false, parse_bool, [UNTRACKED],
"output a json file with profiler results"),
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
"emits a section containing stack size metadata"),
}
pub fn default_lib_output() -> CrateType {

View file

@ -16,10 +16,9 @@ use super::*;
use std::collections::hash_map::Entry;
use std::collections::VecDeque;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use infer::region_constraints::{Constraint, RegionConstraintData};
use infer::{InferCtxt, RegionObligation};
use infer::InferCtxt;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use ty::fold::TypeFolder;
use ty::{Region, RegionVid};
@ -227,20 +226,18 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
.iter()
.filter_map(|param| match param.kind {
ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()),
_ => None
_ => None,
})
.collect();
let body_ids: FxHashSet<_> = infcx
let body_id_map: FxHashMap<_, _> = infcx
.region_obligations
.borrow()
.iter()
.map(|&(id, _)| id)
.map(|&(id, _)| (id, vec![]))
.collect();
for id in body_ids {
infcx.process_registered_region_obligations(&[], None, full_env.clone(), id);
}
infcx.process_registered_region_obligations(&body_id_map, None, full_env.clone());
let region_data = infcx
.borrow_region_constraints()
@ -359,8 +356,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
&Err(SelectionError::Unimplemented) => {
if self.is_of_param(pred.skip_binder().trait_ref.substs) {
already_visited.remove(&pred);
self.add_user_pred(&mut user_computed_preds,
ty::Predicate::Trait(pred.clone()));
self.add_user_pred(
&mut user_computed_preds,
ty::Predicate::Trait(pred.clone()),
);
predicates.push_back(pred);
} else {
debug!(
@ -418,8 +417,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// under which a type implements an auto trait. A trait predicate involving
// a HRTB means that the type needs to work with any choice of lifetime,
// not just one specific lifetime (e.g. 'static).
fn add_user_pred<'c>(&self, user_computed_preds: &mut FxHashSet<ty::Predicate<'c>>,
new_pred: ty::Predicate<'c>) {
fn add_user_pred<'c>(
&self,
user_computed_preds: &mut FxHashSet<ty::Predicate<'c>>,
new_pred: ty::Predicate<'c>,
) {
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
match (&new_pred, old_pred) {
@ -431,20 +433,19 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
if !new_substs.types().eq(old_substs.types()) {
// We can't compare lifetimes if the types are different,
// so skip checking old_pred
return true
return true;
}
for (new_region, old_region) in new_substs
.regions()
.zip(old_substs.regions()) {
for (new_region, old_region) in
new_substs.regions().zip(old_substs.regions())
{
match (new_region, old_region) {
// If both predicates have an 'ReLateBound' (a HRTB) in the
// same spot, we do nothing
(
ty::RegionKind::ReLateBound(_, _),
ty::RegionKind::ReLateBound(_, _)
) => {},
ty::RegionKind::ReLateBound(_, _),
) => {}
(ty::RegionKind::ReLateBound(_, _), _) => {
// The new predicate has a HRTB in a spot where the old
@ -458,7 +459,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
// so we return 'false' to remove the old predicate from
// user_computed_preds
return false;
},
}
(_, ty::RegionKind::ReLateBound(_, _)) => {
// This is the opposite situation as the previous arm - the
// old predicate has a HRTB lifetime in a place where the
@ -471,10 +472,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
}
},
}
_ => {}
}
return true
return true;
});
if should_add_new {
@ -513,28 +514,20 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
match constraint {
&Constraint::VarSubVar(r1, r2) => {
{
let deps1 = vid_map
.entry(RegionTarget::RegionVid(r1))
.or_default();
let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(r2));
}
let deps2 = vid_map
.entry(RegionTarget::RegionVid(r2))
.or_default();
let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default();
deps2.smaller.insert(RegionTarget::RegionVid(r1));
}
&Constraint::RegSubVar(region, vid) => {
{
let deps1 = vid_map
.entry(RegionTarget::Region(region))
.or_default();
let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default();
deps1.larger.insert(RegionTarget::RegionVid(vid));
}
let deps2 = vid_map
.entry(RegionTarget::RegionVid(vid))
.or_default();
let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default();
deps2.smaller.insert(RegionTarget::Region(region));
}
&Constraint::VarSubReg(vid, region) => {
@ -542,15 +535,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
&Constraint::RegSubReg(r1, r2) => {
{
let deps1 = vid_map
.entry(RegionTarget::Region(r1))
.or_default();
let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default();
deps1.larger.insert(RegionTarget::Region(r2));
}
let deps2 = vid_map
.entry(RegionTarget::Region(r2))
.or_default();
let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default();
deps2.smaller.insert(RegionTarget::Region(r1));
}
}
@ -683,7 +672,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
}
}
&ty::Predicate::RegionOutlives(ref binder) => {
if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() {
if select
.infcx()
.region_outlives_predicate(&dummy_cause, binder)
.is_err()
{
return false;
}
}
@ -693,23 +686,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
binder.map_bound_ref(|pred| pred.0).no_late_bound_regions(),
) {
(None, Some(t_a)) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: select.infcx().tcx.types.re_static,
cause: dummy_cause.clone(),
},
select.infcx().register_region_obligation_with_cause(
t_a,
select.infcx().tcx.types.re_static,
&dummy_cause,
);
}
(Some(ty::OutlivesPredicate(t_a, r_b)), _) => {
select.infcx().register_region_obligation(
ast::DUMMY_NODE_ID,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: dummy_cause.clone(),
},
select.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&dummy_cause,
);
}
_ => {}

View file

@ -96,10 +96,10 @@ fn with_fresh_ty_vars<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, '
let header = ty::ImplHeader {
impl_def_id,
self_ty: tcx.type_of(impl_def_id),
trait_ref: tcx.impl_trait_ref(impl_def_id),
predicates: tcx.predicates_of(impl_def_id).predicates
}.subst(tcx, impl_substs);
self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
trait_ref: tcx.impl_trait_ref(impl_def_id).subst(tcx, impl_substs),
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
let Normalized { value: mut header, obligations } =
traits::normalize(selcx, param_env, ObligationCause::dummy(), &header);

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use infer::{RegionObligation, InferCtxt};
use infer::InferCtxt;
use mir::interpret::GlobalId;
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate};
use ty::error::ExpectedFound;
@ -372,13 +372,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
Some(t_a) => {
let r_static = self.selcx.tcx().types.re_static;
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_static,
cause: obligation.cause.clone(),
});
self.selcx.infcx().register_region_obligation_with_cause(
t_a,
r_static,
&obligation.cause,
);
}
ProcessResult::Changed(vec![])
}
@ -387,13 +385,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx,
// If there aren't, register the obligation.
Some(ty::OutlivesPredicate(t_a, r_b)) => {
if self.register_region_obligations {
self.selcx.infcx().register_region_obligation(
obligation.cause.body_id,
RegionObligation {
sup_type: t_a,
sub_region: r_b,
cause: obligation.cause.clone()
});
self.selcx.infcx().register_region_obligation_with_cause(
t_a,
r_b,
&obligation.cause,
);
}
ProcessResult::Changed(vec![])
}

View file

@ -20,6 +20,7 @@ pub use self::ObligationCauseCode::*;
use chalk_engine;
use hir;
use hir::def_id::DefId;
use infer::SuppressRegionErrors;
use infer::outlives::env::OutlivesEnvironment;
use middle::region;
use mir::interpret::ConstEvalErr;
@ -715,7 +716,12 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
// cares about declarations like `'a: 'b`.
let outlives_env = OutlivesEnvironment::new(elaborated_env);
infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &outlives_env);
infcx.resolve_regions_and_report_errors(
region_context,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
let predicates = match infcx.fully_resolve(&predicates) {
Ok(predicates) => predicates,
@ -810,11 +816,10 @@ fn substitute_normalize_and_test_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx
key: (DefId, &'tcx Substs<'tcx>))
-> bool
{
use ty::subst::Subst;
debug!("substitute_normalize_and_test_predicates(key={:?})",
key);
let predicates = tcx.predicates_of(key.0).predicates.subst(tcx, key.1);
let predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates;
let result = normalize_and_test_predicates(tcx, predicates);
debug!("substitute_normalize_and_test_predicates(key={:?}) = {:?}",

View file

@ -179,7 +179,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
predicates
.predicates
.into_iter()
.map(|predicate| predicate.subst_supertrait(self, &trait_ref))
.map(|(predicate, _)| predicate.subst_supertrait(self, &trait_ref))
.any(|predicate| {
match predicate {
ty::Predicate::Trait(ref data) => {
@ -311,7 +311,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
if self.predicates_of(method.def_id).predicates.into_iter()
// A trait object can't claim to live more than the concrete type,
// so outlives predicates will always hold.
.filter(|p| p.to_opt_type_outlives().is_none())
.filter(|(p, _)| p.to_opt_type_outlives().is_none())
.collect::<Vec<_>>()
// Do a shallow visit so that `contains_illegal_self_type_reference`
// may apply it's custom visiting.

View file

@ -3401,7 +3401,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
// that order.
let predicates = tcx.predicates_of(def_id);
assert_eq!(predicates.parent, None);
let mut predicates: Vec<_> = predicates.predicates.iter().flat_map(|predicate| {
let mut predicates: Vec<_> = predicates.predicates.iter().flat_map(|(predicate, _)| {
let predicate = normalize_with_depth(self, param_env, cause.clone(), recursion_depth,
&predicate.subst(tcx, substs));
predicate.obligations.into_iter().chain(

View file

@ -428,7 +428,7 @@ fn to_pretty_impl_header(tcx: TyCtxt, impl_def_id: DefId) -> Option<String> {
let mut pretty_predicates = Vec::with_capacity(
predicates.len() + types_without_default_bounds.len());
for p in predicates {
for (p, _) in predicates {
if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(poly_trait_ref.self_ty());

View file

@ -137,7 +137,7 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> {
let mut predicates: Vec<_> =
predicates.predicates
.iter()
.map(|p| p.subst_supertrait(tcx, &data.to_poly_trait_ref()))
.map(|(p, _)| p.subst_supertrait(tcx, &data.to_poly_trait_ref()))
.collect();
debug!("super_predicates: data={:?} predicates={:?}",
@ -311,7 +311,7 @@ impl<'cx, 'gcx, 'tcx> Iterator for SupertraitDefIds<'cx, 'gcx, 'tcx> {
self.stack.extend(
predicates.predicates
.iter()
.filter_map(|p| p.to_opt_poly_trait_ref())
.filter_map(|(p, _)| p.to_opt_poly_trait_ref())
.map(|t| t.def_id())
.filter(|&super_def_id| visited.insert(super_def_id)));
Some(def_id)

View file

@ -109,8 +109,9 @@ pub fn encode_predicates<'tcx, E, C>(encoder: &mut E,
{
predicates.parent.encode(encoder)?;
predicates.predicates.len().encode(encoder)?;
for predicate in &predicates.predicates {
encode_with_shorthand(encoder, predicate, &cache)?
for (predicate, span) in &predicates.predicates {
encode_with_shorthand(encoder, predicate, &cache)?;
span.encode(encoder)?;
}
Ok(())
}
@ -178,7 +179,7 @@ pub fn decode_predicates<'a, 'tcx, D>(decoder: &mut D)
parent: Decodable::decode(decoder)?,
predicates: (0..decoder.read_usize()?).map(|_| {
// Handle shorthands first, if we have an usize > 0x80.
if decoder.positioned_at_shorthand() {
let predicate = if decoder.positioned_at_shorthand() {
let pos = decoder.read_usize()?;
assert!(pos >= SHORTHAND_OFFSET);
let shorthand = pos - SHORTHAND_OFFSET;
@ -186,7 +187,8 @@ pub fn decode_predicates<'a, 'tcx, D>(decoder: &mut D)
decoder.with_position(shorthand, ty::Predicate::decode)
} else {
ty::Predicate::decode(decoder)
}
}?;
Ok((predicate, Decodable::decode(decoder)?))
})
.collect::<Result<Vec<_>, _>>()?,
})

View file

@ -449,7 +449,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
}
}
if sized && fields.iter().any(|f| f.abi.is_uninhabited()) {
if sized && fields.iter().any(|f| f.abi == Abi::Uninhabited) {
abi = Abi::Uninhabited;
}
@ -724,7 +724,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
// See issue #49298 for more details on the need to leave space
// for non-ZST uninhabited data (mostly partial initialization).
let absent = |fields: &[TyLayout]| {
let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
let uninhabited = fields.iter().any(|f| f.abi == Abi::Uninhabited);
let is_zst = fields.iter().all(|f| f.is_zst());
uninhabited && is_zst
};
@ -872,7 +872,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
_ => Abi::Aggregate { sized: true },
};
if st.iter().all(|v| v.abi.is_uninhabited()) {
if st.iter().all(|v| v.abi == Abi::Uninhabited) {
abi = Abi::Uninhabited;
}
@ -900,7 +900,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
let discr_type = def.repr.discr_type();
let bits = Integer::from_attr(tcx, discr_type).size().bits();
for (i, discr) in def.discriminants(tcx).enumerate() {
if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
if variants[i].iter().any(|f| f.abi == Abi::Uninhabited) {
continue;
}
let mut x = discr.val as i128;
@ -1096,7 +1096,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
}
}
if layout_variants.iter().all(|v| v.abi.is_uninhabited()) {
if layout_variants.iter().all(|v| v.abi == Abi::Uninhabited) {
abi = Abi::Uninhabited;
}

View file

@ -982,7 +982,7 @@ impl<'a, 'gcx, 'tcx> Generics {
#[derive(Clone, Default)]
pub struct GenericPredicates<'tcx> {
pub parent: Option<DefId>,
pub predicates: Vec<Predicate<'tcx>>,
pub predicates: Vec<(Predicate<'tcx>, Span)>,
}
impl<'tcx> serialize::UseSpecializedEncodable for GenericPredicates<'tcx> {}
@ -998,7 +998,7 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> {
pub fn instantiate_own(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &Substs<'tcx>)
-> InstantiatedPredicates<'tcx> {
InstantiatedPredicates {
predicates: self.predicates.subst(tcx, substs)
predicates: self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)).collect(),
}
}
@ -1008,7 +1008,9 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> {
if let Some(def_id) = self.parent {
tcx.predicates_of(def_id).instantiate_into(tcx, instantiated, substs);
}
instantiated.predicates.extend(self.predicates.iter().map(|p| p.subst(tcx, substs)))
instantiated.predicates.extend(
self.predicates.iter().map(|(p, _)| p.subst(tcx, substs)),
);
}
pub fn instantiate_identity(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>)
@ -1023,7 +1025,7 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> {
if let Some(def_id) = self.parent {
tcx.predicates_of(def_id).instantiate_identity_into(tcx, instantiated);
}
instantiated.predicates.extend(&self.predicates)
instantiated.predicates.extend(self.predicates.iter().map(|&(p, _)| p))
}
pub fn instantiate_supertrait(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>,
@ -1032,7 +1034,7 @@ impl<'a, 'gcx, 'tcx> GenericPredicates<'tcx> {
{
assert_eq!(self.parent, None);
InstantiatedPredicates {
predicates: self.predicates.iter().map(|pred| {
predicates: self.predicates.iter().map(|(pred, _)| {
pred.subst_supertrait(tcx, poly_trait_ref)
}).collect()
}
@ -2351,7 +2353,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
substs: tcx.mk_substs_trait(ty, &[])
}).to_predicate();
let predicates = tcx.predicates_of(self.did).predicates;
if predicates.into_iter().any(|p| p == sized_predicate) {
if predicates.into_iter().any(|(p, _)| p == sized_predicate) {
vec![]
} else {
vec![ty]

View file

@ -606,6 +606,7 @@ impl<'a, 'tcx, 'x> SpecializedDecoder<interpret::AllocId> for CacheDecoder<'a, '
alloc_decoding_session.decode_alloc_id(self)
}
}
impl<'a, 'tcx, 'x> SpecializedDecoder<Span> for CacheDecoder<'a, 'tcx, 'x> {
fn specialized_decode(&mut self) -> Result<Span, Self::Error> {
let tag: u8 = Decodable::decode(self)?;

View file

@ -720,6 +720,16 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec<T> {
}
}
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>().into_boxed_slice()
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.iter().any(|t| t.visit_with(visitor))
}
}
impl<'tcx, T:TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder<T> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
self.map_bound_ref(|ty| ty.fold_with(folder))

View file

@ -30,7 +30,7 @@ pub fn codegen_inline_asm(
ia: &hir::InlineAsm,
outputs: Vec<PlaceRef<'ll, 'tcx>>,
mut inputs: Vec<&'ll Value>
) {
) -> bool {
let mut ext_constraints = vec![];
let mut output_types = vec![];
@ -97,6 +97,10 @@ pub fn codegen_inline_asm(
ia.alignstack,
dialect
);
if r.is_none() {
return false;
}
let r = r.unwrap();
// Again, based on how many outputs we have
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
@ -117,6 +121,8 @@ pub fn codegen_inline_asm(
llvm::LLVMSetMetadata(r, kind,
llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1));
}
return true;
}
pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,

View file

@ -47,7 +47,8 @@ use std::str;
use syntax::attr;
pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target,
invalid_output_for_target, out_filename, check_file_is_writeable};
invalid_output_for_target, out_filename, check_file_is_writeable,
filename_for_metadata};
// The third parameter is for env vars, used on windows to set up the
// path for MSVC to find its DLLs, and gcc to find its bundled
@ -218,15 +219,6 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
false
}
fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf {
let out_filename = outputs.single_output_file.clone()
.unwrap_or(outputs
.out_directory
.join(&format!("lib{}{}.rmeta", crate_name, sess.opts.cg.extra_filename)));
check_file_is_writeable(&out_filename, sess);
out_filename
}
pub(crate) fn each_linked_rlib(sess: &Session,
info: &CrateInfo,
f: &mut dyn FnMut(CrateNum, &Path)) -> Result<(), String> {

View file

@ -196,6 +196,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
let features = CString::new(features).unwrap();
let is_pie_binary = !find_features && is_pie_binary(sess);
let trap_unreachable = sess.target.target.options.trap_unreachable;
let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes;
let asm_comments = sess.asm_comments();
@ -213,6 +214,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
trap_unreachable,
singlethread,
asm_comments,
emit_stack_size_section,
)
};

View file

@ -737,7 +737,7 @@ impl Builder<'a, 'll, 'tcx> {
pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
inputs: &[&'ll Value], output: &'ll Type,
volatile: bool, alignstack: bool,
dia: AsmDialect) -> &'ll Value {
dia: AsmDialect) -> Option<&'ll Value> {
self.count_insn("inlineasm");
let volatile = if volatile { llvm::True }
@ -753,9 +753,17 @@ impl Builder<'a, 'll, 'tcx> {
debug!("Asm Output Type: {:?}", output);
let fty = Type::func(&argtys[..], output);
unsafe {
let v = llvm::LLVMRustInlineAsm(
fty, asm, cons, volatile, alignstack, dia);
self.call(v, inputs, None)
// Ask LLVM to verify that the constraints are well-formed.
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons);
debug!("Constraint verification result: {:?}", constraints_ok);
if constraints_ok == llvm::True {
let v = llvm::LLVMRustInlineAsm(
fty, asm, cons, volatile, alignstack, dia);
Some(self.call(v, inputs, None))
} else {
// LLVM has detected an issue with our constaints, bail out
None
}
}
}

View file

@ -279,7 +279,7 @@ pub fn create_function_debug_context(
}
None => {}
};
if cx.layout_of(sig.output()).abi.is_uninhabited() {
if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited {
flags = flags | DIFlags::FlagNoReturn;
}

View file

@ -23,7 +23,7 @@
use llvm;
use llvm::AttributePlace::Function;
use rustc::ty::{self, Ty};
use rustc::ty::layout::LayoutOf;
use rustc::ty::layout::{self, LayoutOf};
use rustc::session::config::Sanitizer;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_target::spec::PanicStrategy;
@ -137,7 +137,7 @@ pub fn declare_fn(
let fty = FnType::new(cx, sig, &[]);
let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx));
if cx.layout_of(sig.output()).abi.is_uninhabited() {
if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited {
llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
}

View file

@ -47,4 +47,26 @@ unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok!
```
"##,
E0668: r##"
Malformed inline assembly rejected by LLVM.
LLVM checks the validity of the constraints and the assembly string passed to
it. This error implies that LLVM seems something wrong with the inline
assembly call.
In particular, it can happen if you forgot the closing bracket of a register
constraint (see issue #51430):
```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail)
#![feature(asm)]
fn main() {
let rax: u64;
unsafe {
asm!("" :"={rax"(rax));
println!("Accumulator is: {}", rax);
}
}
```
"##,
}

View file

@ -1208,6 +1208,9 @@ extern "C" {
AlignStack: Bool,
Dialect: AsmDialect)
-> &Value;
pub fn LLVMRustInlineAsmVerify(Ty: &Type,
Constraints: *const c_char)
-> Bool;
pub fn LLVMRustDebugMetadataVersion() -> u32;
pub fn LLVMRustVersionMajor() -> u32;
@ -1460,7 +1463,8 @@ extern "C" {
DataSections: bool,
TrapUnreachable: bool,
Singlethread: bool,
AsmComments: bool)
AsmComments: bool,
EmitStackSizeSection: bool)
-> Option<&'static mut TargetMachine>;
pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
pub fn LLVMRustAddAnalysisPasses(T: &'a TargetMachine, PM: &PassManager<'a>, M: &'a Module);

View file

@ -482,54 +482,6 @@ impl FunctionCx<'a, 'll, 'tcx> {
_ => FnType::new(bx.cx, sig, &extra_args)
};
// emit a panic instead of instantiating an uninhabited type
if (intrinsic == Some("init") || intrinsic == Some("uninit")) &&
fn_ty.ret.layout.abi.is_uninhabited()
{
let loc = bx.sess().source_map().lookup_char_pos(span.lo());
let filename = Symbol::intern(&loc.file.name.to_string()).as_str();
let filename = C_str_slice(bx.cx, filename);
let line = C_u32(bx.cx, loc.line as u32);
let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1);
let align = tcx.data_layout.aggregate_align
.max(tcx.data_layout.i32_align)
.max(tcx.data_layout.pointer_align);
let str = format!(
"Attempted to instantiate uninhabited type {} using mem::{}",
sig.output(),
if intrinsic == Some("init") { "zeroed" } else { "uninitialized" }
);
let msg_str = Symbol::intern(&str).as_str();
let msg_str = C_str_slice(bx.cx, msg_str);
let msg_file_line_col = C_struct(bx.cx,
&[msg_str, filename, line, col],
false);
let msg_file_line_col = consts::addr_of(bx.cx,
msg_file_line_col,
align,
Some("panic_loc"));
// Obtain the panic entry point.
let def_id =
common::langcall(bx.tcx(), Some(span), "", lang_items::PanicFnLangItem);
let instance = ty::Instance::mono(bx.tcx(), def_id);
let fn_ty = FnType::of_instance(bx.cx, &instance);
let llfn = callee::get_fn(bx.cx, instance);
// Codegen the actual panic invoke/call.
do_call(
self,
bx,
fn_ty,
llfn,
&[msg_file_line_col],
destination.as_ref().map(|(_, bb)| (ReturnDest::Nothing, *bb)),
cleanup,
);
return;
}
// The arguments we'll be passing. Plus one to account for outptr, if used.
let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize;
let mut llargs = Vec::with_capacity(arg_count);

View file

@ -173,10 +173,7 @@ impl PlaceRef<'ll, 'tcx> {
let cx = bx.cx;
let field = self.layout.field(cx, ix);
let offset = self.layout.fields.offset(ix);
let effective_field_align = self.align
.min(self.layout.align)
.min(field.align)
.restrict_for_offset(offset);
let effective_field_align = self.align.restrict_for_offset(offset);
let simple = || {
// Unions and newtypes only use an offset of 0.
@ -278,7 +275,7 @@ impl PlaceRef<'ll, 'tcx> {
/// Obtain the actual discriminant of a value.
pub fn codegen_get_discr(self, bx: &Builder<'a, 'll, 'tcx>, cast_to: Ty<'tcx>) -> &'ll Value {
let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx);
if self.layout.abi.is_uninhabited() {
if self.layout.abi == layout::Abi::Uninhabited {
return C_undef(cast_to);
}
match self.layout.variants {
@ -341,7 +338,7 @@ impl PlaceRef<'ll, 'tcx> {
/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn codegen_set_discr(&self, bx: &Builder<'a, 'll, 'tcx>, variant_index: usize) {
if self.layout.for_variant(bx.cx, variant_index).abi.is_uninhabited() {
if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited {
return;
}
match self.layout.variants {

View file

@ -290,7 +290,7 @@ impl FunctionCx<'a, 'll, 'tcx> {
mir::CastKind::Misc => {
assert!(cast.is_llvm_immediate());
let ll_t_out = cast.immediate_llvm_type(bx.cx);
if operand.layout.abi.is_uninhabited() {
if operand.layout.abi == layout::Abi::Uninhabited {
return (bx, OperandRef {
val: OperandValue::Immediate(C_undef(ll_t_out)),
layout: cast,

View file

@ -86,7 +86,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
self.codegen_operand(&bx, input).immediate()
}).collect();
asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
if !res {
span_err!(bx.sess(), statement.source_info.span, E0668,
"malformed inline assembly");
}
bx
}
mir::StatementKind::FakeRead(..) |

View file

@ -97,6 +97,19 @@ pub fn find_crate_name(sess: Option<&Session>,
"rust_out".to_string()
}
pub fn filename_for_metadata(sess: &Session,
crate_name: &str,
outputs: &OutputFilenames) -> PathBuf {
let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
let out_filename = outputs.single_output_file.clone()
.unwrap_or(outputs.out_directory.join(&format!("lib{}.rmeta", libname)));
check_file_is_writeable(&out_filename, sess);
out_filename
}
pub fn filename_for_input(sess: &Session,
crate_type: config::CrateType,
crate_name: &str,

View file

@ -535,6 +535,13 @@ impl<I: Idx, T> IndexVec<I, T> {
self.raw.len()
}
/// Gives the next index that will be assigned when `push` is
/// called.
#[inline]
pub fn next_index(&self) -> I {
I::new(self.len())
}
#[inline]
pub fn is_empty(&self) -> bool {
self.raw.is_empty()

View file

@ -980,6 +980,7 @@ pub fn enable_save_analysis(control: &mut CompileController) {
state.expanded_crate.unwrap(),
state.analysis.unwrap(),
state.crate_name.unwrap(),
state.input,
None,
DumpHandler::new(state.out_dir,
state.crate_name.unwrap()))

View file

@ -14,29 +14,29 @@ use std::path::PathBuf;
use std::sync::mpsc;
use driver;
use rustc_lint;
use rustc_resolve::MakeGlobMap;
use rustc::middle::region;
use rustc::ty::subst::Subst;
use rustc::traits::ObligationCause;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::query::OnDiskCache;
use rustc::infer::{self, InferOk, InferResult};
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc_metadata::cstore::CStore;
use rustc::hir::map as hir_map;
use rustc::session::{self, config};
use rustc::session::config::{OutputFilenames, OutputTypes};
use rustc_data_structures::sync::{self, Lrc};
use syntax;
use syntax::ast;
use rustc_target::spec::abi::Abi;
use syntax::source_map::{SourceMap, FilePathMapping, FileName};
use errors;
use errors::emitter::Emitter;
use errors::{Level, DiagnosticBuilder};
use errors::{DiagnosticBuilder, Level};
use rustc::hir::map as hir_map;
use rustc::infer::outlives::env::OutlivesEnvironment;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors};
use rustc::middle::region;
use rustc::session::config::{OutputFilenames, OutputTypes};
use rustc::session::{self, config};
use rustc::traits::ObligationCause;
use rustc::ty::query::OnDiskCache;
use rustc::ty::subst::Subst;
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::sync::{self, Lrc};
use rustc_lint;
use rustc_metadata::cstore::CStore;
use rustc_resolve::MakeGlobMap;
use rustc_target::spec::abi::Abi;
use syntax;
use syntax::ast;
use syntax::feature_gate::UnstableFeatures;
use syntax::source_map::{FileName, FilePathMapping, SourceMap};
use syntax::symbol::Symbol;
use syntax_pos::DUMMY_SP;
@ -90,13 +90,15 @@ impl Emitter for ExpectErrorEmitter {
fn errors(msgs: &[&str]) -> (Box<dyn Emitter + sync::Send>, usize) {
let v = msgs.iter().map(|m| m.to_string()).collect();
(box ExpectErrorEmitter { messages: v } as Box<dyn Emitter + sync::Send>, msgs.len())
(
box ExpectErrorEmitter { messages: v } as Box<dyn Emitter + sync::Send>,
msgs.len(),
)
}
fn test_env<F>(source_string: &str,
args: (Box<dyn Emitter + sync::Send>, usize),
body: F)
where F: FnOnce(Env)
fn test_env<F>(source_string: &str, args: (Box<dyn Emitter + sync::Send>, usize), body: F)
where
F: FnOnce(Env),
{
syntax::with_globals(|| {
let mut options = config::Options::default();
@ -113,34 +115,41 @@ fn test_env_with_pool<F>(
options: config::Options,
source_string: &str,
(emitter, expected_err_count): (Box<dyn Emitter + sync::Send>, usize),
body: F
)
where F: FnOnce(Env)
body: F,
) where
F: FnOnce(Env),
{
let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
let sess = session::build_session_(options,
None,
diagnostic_handler,
Lrc::new(SourceMap::new(FilePathMapping::empty())));
let sess = session::build_session_(
options,
None,
diagnostic_handler,
Lrc::new(SourceMap::new(FilePathMapping::empty())),
);
let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader());
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let input = config::Input::Str {
name: FileName::Anon,
input: source_string.to_string(),
};
let krate = driver::phase_1_parse_input(&driver::CompileController::basic(),
&sess,
&input).unwrap();
let driver::ExpansionResult { defs, resolutions, mut hir_forest, .. } = {
driver::phase_2_configure_and_expand(&sess,
&cstore,
krate,
None,
"test",
None,
MakeGlobMap::No,
|_| Ok(()))
.expect("phase 2 aborted")
let krate =
driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input).unwrap();
let driver::ExpansionResult {
defs,
resolutions,
mut hir_forest,
..
} = {
driver::phase_2_configure_and_expand(
&sess,
&cstore,
krate,
None,
"test",
None,
MakeGlobMap::No,
|_| Ok(()),
).expect("phase 2 aborted")
};
let arenas = ty::AllArenas::new();
@ -155,32 +164,39 @@ fn test_env_with_pool<F>(
extra: String::new(),
outputs: OutputTypes::new(&[]),
};
TyCtxt::create_and_enter(&sess,
&cstore,
ty::query::Providers::default(),
ty::query::Providers::default(),
&arenas,
resolutions,
hir_map,
OnDiskCache::new_empty(sess.source_map()),
"test_crate",
tx,
&outputs,
|tcx| {
tcx.infer_ctxt().enter(|infcx| {
let mut region_scope_tree = region::ScopeTree::default();
let param_env = ty::ParamEnv::empty();
body(Env {
infcx: &infcx,
region_scope_tree: &mut region_scope_tree,
param_env: param_env,
TyCtxt::create_and_enter(
&sess,
&cstore,
ty::query::Providers::default(),
ty::query::Providers::default(),
&arenas,
resolutions,
hir_map,
OnDiskCache::new_empty(sess.source_map()),
"test_crate",
tx,
&outputs,
|tcx| {
tcx.infer_ctxt().enter(|infcx| {
let mut region_scope_tree = region::ScopeTree::default();
let param_env = ty::ParamEnv::empty();
body(Env {
infcx: &infcx,
region_scope_tree: &mut region_scope_tree,
param_env: param_env,
});
let outlives_env = OutlivesEnvironment::new(param_env);
let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
infcx.resolve_regions_and_report_errors(
def_id,
&region_scope_tree,
&outlives_env,
SuppressRegionErrors::default(),
);
assert_eq!(tcx.sess.err_count(), expected_err_count);
});
let outlives_env = OutlivesEnvironment::new(param_env);
let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID);
infcx.resolve_regions_and_report_errors(def_id, &region_scope_tree, &outlives_env);
assert_eq!(tcx.sess.err_count(), expected_err_count);
});
});
},
);
}
fn d1() -> ty::DebruijnIndex {
@ -196,9 +212,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
self.infcx.tcx
}
pub fn create_region_hierarchy(&mut self, rh: &RH,
parent: (region::Scope, region::ScopeDepth)) {
let me = region::Scope { id: rh.id, data: region::ScopeData::Node };
pub fn create_region_hierarchy(
&mut self,
rh: &RH,
parent: (region::Scope, region::ScopeDepth),
) {
let me = region::Scope {
id: rh.id,
data: region::ScopeData::Node,
};
self.region_scope_tree.record_scope_parent(me, Some(parent));
for child_rh in rh.sub {
self.create_region_hierarchy(child_rh, (me, parent.1 + 1));
@ -211,20 +233,25 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
let dscope = region::Scope {
id: hir::ItemLocalId(1),
data: region::ScopeData::Destruction
data: region::ScopeData::Destruction,
};
self.region_scope_tree.record_scope_parent(dscope, None);
self.create_region_hierarchy(&RH {
id: hir::ItemLocalId(1),
sub: &[RH {
id: hir::ItemLocalId(10),
sub: &[],
self.create_region_hierarchy(
&RH {
id: hir::ItemLocalId(1),
sub: &[
RH {
id: hir::ItemLocalId(10),
sub: &[],
},
RH {
id: hir::ItemLocalId(11),
sub: &[],
},
],
},
RH {
id: hir::ItemLocalId(11),
sub: &[],
}],
}, (dscope, 1));
(dscope, 1),
);
}
#[allow(dead_code)] // this seems like it could be useful, even if we don't use it now
@ -236,11 +263,12 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
};
fn search_mod(this: &Env,
m: &hir::Mod,
idx: usize,
names: &[String])
-> Option<ast::NodeId> {
fn search_mod(
this: &Env,
m: &hir::Mod,
idx: usize,
names: &[String],
) -> Option<ast::NodeId> {
assert!(idx < names.len());
for item in &m.item_ids {
let item = this.infcx.tcx.hir.expect_item(item.id);
@ -257,22 +285,22 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
return match it.node {
hir::ItemKind::Use(..) |
hir::ItemKind::ExternCrate(..) |
hir::ItemKind::Const(..) |
hir::ItemKind::Static(..) |
hir::ItemKind::Fn(..) |
hir::ItemKind::ForeignMod(..) |
hir::ItemKind::GlobalAsm(..) |
hir::ItemKind::Existential(..) |
hir::ItemKind::Ty(..) => None,
hir::ItemKind::Use(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::ForeignMod(..)
| hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::Existential(..)
| hir::ItemKind::Ty(..) => None,
hir::ItemKind::Enum(..) |
hir::ItemKind::Struct(..) |
hir::ItemKind::Union(..) |
hir::ItemKind::Trait(..) |
hir::ItemKind::TraitAlias(..) |
hir::ItemKind::Impl(..) => None,
hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..)
| hir::ItemKind::Trait(..)
| hir::ItemKind::TraitAlias(..)
| hir::ItemKind::Impl(..) => None,
hir::ItemKind::Mod(ref m) => search_mod(this, m, idx, names),
};
@ -280,7 +308,10 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn make_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
match self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(a, b) {
match self.infcx
.at(&ObligationCause::dummy(), self.param_env)
.sub(a, b)
{
Ok(_) => true,
Err(ref e) => panic!("Encountered error: {}", e),
}
@ -302,13 +333,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn t_fn(&self, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>) -> Ty<'tcx> {
self.infcx.tcx.mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig(
input_tys.iter().cloned(),
output_ty,
false,
hir::Unsafety::Normal,
Abi::Rust
)))
self.infcx
.tcx
.mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig(
input_tys.iter().cloned(),
output_ty,
false,
hir::Unsafety::Normal,
Abi::Rust,
)))
}
pub fn t_nil(&self) -> Ty<'tcx> {
@ -321,23 +354,30 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
pub fn t_param(&self, index: u32) -> Ty<'tcx> {
let name = format!("T{}", index);
self.infcx.tcx.mk_ty_param(index, Symbol::intern(&name).as_interned_str())
self.infcx
.tcx
.mk_ty_param(index, Symbol::intern(&name).as_interned_str())
}
pub fn re_early_bound(&self, index: u32, name: &'static str) -> ty::Region<'tcx> {
let name = Symbol::intern(name).as_interned_str();
self.infcx.tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID),
index,
name,
}))
self.infcx
.tcx
.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID),
index,
name,
}))
}
pub fn re_late_bound_with_debruijn(&self,
id: u32,
debruijn: ty::DebruijnIndex)
-> ty::Region<'tcx> {
self.infcx.tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id)))
pub fn re_late_bound_with_debruijn(
&self,
id: u32,
debruijn: ty::DebruijnIndex,
) -> ty::Region<'tcx> {
self.infcx
.tcx
.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id)))
}
pub fn t_rptr(&self, r: ty::Region<'tcx>) -> Ty<'tcx> {
@ -349,10 +389,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
}
pub fn t_rptr_late_bound_with_debruijn(&self,
id: u32,
debruijn: ty::DebruijnIndex)
-> Ty<'tcx> {
pub fn t_rptr_late_bound_with_debruijn(
&self,
id: u32,
debruijn: ty::DebruijnIndex,
) -> Ty<'tcx> {
let r = self.re_late_bound_with_debruijn(id, debruijn);
self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize)
}
@ -360,9 +401,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
pub fn t_rptr_scope(&self, id: u32) -> Ty<'tcx> {
let r = ty::ReScope(region::Scope {
id: hir::ItemLocalId(id),
data: region::ScopeData::Node
data: region::ScopeData::Node,
});
self.infcx.tcx.mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize)
self.infcx
.tcx
.mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize)
}
pub fn re_free(&self, id: u32) -> ty::Region<'tcx> {
@ -378,14 +421,19 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> {
}
pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> {
self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(t1, t2)
self.infcx
.at(&ObligationCause::dummy(), self.param_env)
.sub(t1, t2)
}
/// Checks that `t1 <: t2` is true (this may register additional
/// region checks).
pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) {
match self.sub(t1, t2) {
Ok(InferOk { obligations, value: () }) => {
Ok(InferOk {
obligations,
value: (),
}) => {
// None of these tests should require nested obligations:
assert!(obligations.is_empty());
}
@ -445,8 +493,10 @@ fn sub_free_bound_false() {
env.create_simple_region_hierarchy();
let t_rptr_free1 = env.t_rptr_free(1);
let t_rptr_bound1 = env.t_rptr_late_bound(1);
env.check_not_sub(env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize));
env.check_not_sub(
env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
);
})
}
@ -462,8 +512,10 @@ fn sub_bound_free_true() {
env.create_simple_region_hierarchy();
let t_rptr_bound1 = env.t_rptr_late_bound(1);
let t_rptr_free1 = env.t_rptr_free(1);
env.check_sub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
env.t_fn(&[t_rptr_free1], env.tcx().types.isize));
env.check_sub(
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
env.t_fn(&[t_rptr_free1], env.tcx().types.isize),
);
})
}
@ -476,10 +528,13 @@ fn sub_free_bound_false_infer() {
//! does NOT hold for any instantiation of `_#1`.
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
let t_infer1 = env.infcx
.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP));
let t_rptr_bound1 = env.t_rptr_late_bound(1);
env.check_not_sub(env.t_fn(&[t_infer1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize));
env.check_not_sub(
env.t_fn(&[t_infer1], env.tcx().types.isize),
env.t_fn(&[t_rptr_bound1], env.tcx().types.isize),
);
})
}
@ -487,7 +542,6 @@ fn sub_free_bound_false_infer() {
/// This requires adjusting the Debruijn index.
#[test]
fn subst_ty_renumber_bound() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
// Situation:
// Theta = [A -> &'a foo]
@ -509,11 +563,10 @@ fn subst_ty_renumber_bound() {
env.t_fn(&[t_ptr_bound2], env.t_nil())
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -546,11 +599,10 @@ fn subst_ty_renumber_some_bounds() {
env.t_pair(t_rptr_bound1, env.t_fn(&[t_rptr_bound2], env.t_nil()))
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -559,7 +611,6 @@ fn subst_ty_renumber_some_bounds() {
/// Test that we correctly compute whether a type has escaping regions or not.
#[test]
fn escaping() {
test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| {
// Situation:
// Theta = [A -> &'a foo]
@ -608,11 +659,10 @@ fn subst_region_renumber_region() {
env.t_fn(&[t_rptr_bound2], env.t_nil())
};
debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source,
substs,
t_substituted,
t_expected);
debug!(
"subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}",
t_source, substs, t_substituted, t_expected
);
assert_eq!(t_substituted, t_expected);
})
@ -627,9 +677,13 @@ fn walk_ty() {
let tup1_ty = tcx.intern_tup(&[int_ty, usize_ty, int_ty, usize_ty]);
let tup2_ty = tcx.intern_tup(&[tup1_ty, tup1_ty, usize_ty]);
let walked: Vec<_> = tup2_ty.walk().collect();
assert_eq!(walked,
[tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty,
usize_ty, int_ty, usize_ty, usize_ty]);
assert_eq!(
walked,
[
tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty, usize_ty,
int_ty, usize_ty, usize_ty
]
);
})
}
@ -644,14 +698,16 @@ fn walk_ty_skip_subtree() {
// types we expect to see (in order), plus a boolean saying
// whether to skip the subtree.
let mut expected = vec![(tup2_ty, false),
(tup1_ty, false),
(int_ty, false),
(usize_ty, false),
(int_ty, false),
(usize_ty, false),
(tup1_ty, true), // skip the isize/usize/isize/usize
(usize_ty, false)];
let mut expected = vec![
(tup2_ty, false),
(tup1_ty, false),
(int_ty, false),
(usize_ty, false),
(int_ty, false),
(usize_ty, false),
(tup1_ty, true), // skip the isize/usize/isize/usize
(usize_ty, false),
];
expected.reverse();
let mut walker = tup2_ty.walk();

View file

@ -1736,8 +1736,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
if cx.tcx.features().trivial_bounds {
let def_id = cx.tcx.hir.local_def_id(item.id);
let predicates = cx.tcx.predicates_of(def_id);
for predicate in &predicates.predicates {
let predicate_kind_name = match *predicate {
for &(predicate, span) in &predicates.predicates {
let predicate_kind_name = match predicate {
Trait(..) => "Trait",
TypeOutlives(..) |
RegionOutlives(..) => "Lifetime",
@ -1755,7 +1755,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
if predicate.is_global() {
cx.span_lint(
TRIVIAL_BOUNDS,
item.span,
span,
&format!("{} bound {} does not depend on any type \
or lifetime parameters", predicate_kind_name, predicate),
);
@ -1999,3 +1999,233 @@ impl EarlyLintPass for KeywordIdents {
lint.emit()
}
}
pub struct ExplicitOutlivesRequirements;
impl LintPass for ExplicitOutlivesRequirements {
fn get_lints(&self) -> LintArray {
lint_array![EXPLICIT_OUTLIVES_REQUIREMENTS]
}
}
impl ExplicitOutlivesRequirements {
fn collect_outlives_bound_spans(
&self,
cx: &LateContext,
item_def_id: DefId,
param_name: &str,
bounds: &hir::GenericBounds,
infer_static: bool
) -> Vec<(usize, Span)> {
// For lack of a more elegant strategy for comparing the `ty::Predicate`s
// returned by this query with the params/bounds grabbed from the HIR—and
// with some regrets—we're going to covert the param/lifetime names to
// strings
let inferred_outlives = cx.tcx.inferred_outlives_of(item_def_id);
let ty_lt_names = inferred_outlives.iter().filter_map(|pred| {
let binder = match pred {
ty::Predicate::TypeOutlives(binder) => binder,
_ => { return None; }
};
let ty_outlives_pred = binder.skip_binder();
let ty_name = match ty_outlives_pred.0.sty {
ty::Param(param) => param.name.to_string(),
_ => { return None; }
};
let lt_name = match ty_outlives_pred.1 {
ty::RegionKind::ReEarlyBound(region) => {
region.name.to_string()
},
_ => { return None; }
};
Some((ty_name, lt_name))
}).collect::<Vec<_>>();
let mut bound_spans = Vec::new();
for (i, bound) in bounds.iter().enumerate() {
if let hir::GenericBound::Outlives(lifetime) = bound {
let is_static = match lifetime.name {
hir::LifetimeName::Static => true,
_ => false
};
if is_static && !infer_static {
// infer-outlives for 'static is still feature-gated (tracking issue #44493)
continue;
}
let lt_name = &lifetime.name.ident().to_string();
if ty_lt_names.contains(&(param_name.to_owned(), lt_name.to_owned())) {
bound_spans.push((i, bound.span()));
}
}
}
bound_spans
}
fn consolidate_outlives_bound_spans(
&self,
lo: Span,
bounds: &hir::GenericBounds,
bound_spans: Vec<(usize, Span)>
) -> Vec<Span> {
if bounds.is_empty() {
return Vec::new();
}
if bound_spans.len() == bounds.len() {
let (_, last_bound_span) = bound_spans[bound_spans.len()-1];
// If all bounds are inferable, we want to delete the colon, so
// start from just after the parameter (span passed as argument)
vec![lo.to(last_bound_span)]
} else {
let mut merged = Vec::new();
let mut last_merged_i = None;
let mut from_start = true;
for (i, bound_span) in bound_spans {
match last_merged_i {
// If the first bound is inferable, our span should also eat the trailing `+`
None if i == 0 => {
merged.push(bound_span.to(bounds[1].span().shrink_to_lo()));
last_merged_i = Some(0);
},
// If consecutive bounds are inferable, merge their spans
Some(h) if i == h+1 => {
if let Some(tail) = merged.last_mut() {
// Also eat the trailing `+` if the first
// more-than-one bound is inferable
let to_span = if from_start && i < bounds.len() {
bounds[i+1].span().shrink_to_lo()
} else {
bound_span
};
*tail = tail.to(to_span);
last_merged_i = Some(i);
} else {
bug!("another bound-span visited earlier");
}
},
_ => {
// When we find a non-inferable bound, subsequent inferable bounds
// won't be consecutive from the start (and we'll eat the leading
// `+` rather than the trailing one)
from_start = false;
merged.push(bounds[i-1].span().shrink_to_hi().to(bound_span));
last_merged_i = Some(i);
}
}
}
merged
}
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
let infer_static = cx.tcx.features().infer_static_outlives_requirements;
let def_id = cx.tcx.hir.local_def_id(item.id);
if let hir::ItemKind::Struct(_, ref generics) = item.node {
let mut bound_count = 0;
let mut lint_spans = Vec::new();
for param in &generics.params {
let param_name = match param.kind {
hir::GenericParamKind::Lifetime { .. } => { continue; },
hir::GenericParamKind::Type { .. } => {
match param.name {
hir::ParamName::Fresh(_) => { continue; },
hir::ParamName::Plain(name) => name.to_string()
}
}
};
let bound_spans = self.collect_outlives_bound_spans(
cx, def_id, &param_name, &param.bounds, infer_static
);
bound_count += bound_spans.len();
lint_spans.extend(
self.consolidate_outlives_bound_spans(
param.span.shrink_to_hi(), &param.bounds, bound_spans
)
);
}
let mut where_lint_spans = Vec::new();
let mut dropped_predicate_count = 0;
let num_predicates = generics.where_clause.predicates.len();
for (i, where_predicate) in generics.where_clause.predicates.iter().enumerate() {
if let hir::WherePredicate::BoundPredicate(predicate) = where_predicate {
let param_name = match predicate.bounded_ty.node {
hir::TyKind::Path(ref qpath) => {
if let hir::QPath::Resolved(None, ty_param_path) = qpath {
ty_param_path.segments[0].ident.to_string()
} else {
continue;
}
},
_ => { continue; }
};
let bound_spans = self.collect_outlives_bound_spans(
cx, def_id, &param_name, &predicate.bounds, infer_static
);
bound_count += bound_spans.len();
let drop_predicate = bound_spans.len() == predicate.bounds.len();
if drop_predicate {
dropped_predicate_count += 1;
}
// If all the bounds on a predicate were inferable and there are
// further predicates, we want to eat the trailing comma
if drop_predicate && i + 1 < num_predicates {
let next_predicate_span = generics.where_clause.predicates[i+1].span();
where_lint_spans.push(
predicate.span.to(next_predicate_span.shrink_to_lo())
);
} else {
where_lint_spans.extend(
self.consolidate_outlives_bound_spans(
predicate.span.shrink_to_lo(),
&predicate.bounds,
bound_spans
)
);
}
}
}
// If all predicates are inferable, drop the entire clause
// (including the `where`)
if num_predicates > 0 && dropped_predicate_count == num_predicates {
let full_where_span = generics.span.shrink_to_hi()
.to(generics.where_clause.span()
.expect("span of (nonempty) where clause should exist"));
lint_spans.push(
full_where_span
);
} else {
lint_spans.extend(where_lint_spans);
}
if !lint_spans.is_empty() {
let mut err = cx.struct_span_lint(
EXPLICIT_OUTLIVES_REQUIREMENTS,
lint_spans.clone(),
"outlives requirements can be inferred"
);
err.multipart_suggestion_with_applicability(
if bound_count == 1 {
"remove this bound"
} else {
"remove these bounds"
},
lint_spans.into_iter().map(|span| (span, "".to_owned())).collect::<Vec<_>>(),
Applicability::MachineApplicable
);
err.emit();
}
}
}
}

View file

@ -48,6 +48,7 @@ use rustc::lint::builtin::{
BARE_TRAIT_OBJECTS,
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
ELIDED_LIFETIMES_IN_PATHS,
EXPLICIT_OUTLIVES_REQUIREMENTS,
parser::QUESTION_MARK_MACRO_SEP
};
use rustc::session;
@ -157,6 +158,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
TypeLimits: TypeLimits::new(),
MissingDoc: MissingDoc::new(),
MissingDebugImplementations: MissingDebugImplementations::new(),
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
]], ['tcx]);
store.register_late_pass(sess, false, box BuiltinCombinedLateLintPass::new());
@ -199,7 +201,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
BARE_TRAIT_OBJECTS,
UNUSED_EXTERN_CRATES,
ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
ELIDED_LIFETIMES_IN_PATHS
ELIDED_LIFETIMES_IN_PATHS,
EXPLICIT_OUTLIVES_REQUIREMENTS
// FIXME(#52665, #47816) not always applicable and not all
// macros are ready for this yet.

View file

@ -256,6 +256,7 @@ impl<'a> CrateLoader<'a> {
let cmeta = cstore::CrateMetadata {
name: crate_root.name,
imported_name: ident,
extern_crate: Lock::new(None),
def_path_table: Lrc::new(def_path_table),
trait_impls,

View file

@ -53,8 +53,13 @@ pub struct ImportedSourceFile {
}
pub struct CrateMetadata {
/// Original name of the crate.
pub name: Symbol,
/// Name of the crate as imported. I.e. if imported with
/// `extern crate foo as bar;` this will be `bar`.
pub imported_name: Symbol,
/// Information about the extern crate that caused this crate to
/// be loaded. If this is `None`, then the crate was injected
/// (e.g., by the allocator)

View file

@ -441,8 +441,7 @@ impl cstore::CStore {
let data = self.get_crate_data(id.krate);
if let Some(ref proc_macros) = data.proc_macros {
return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
} else if data.name == "proc_macro" &&
self.get_crate_data(id.krate).item_name(id.index) == "quote" {
} else if data.name == "proc_macro" && data.item_name(id.index) == "quote" {
use syntax::ext::base::SyntaxExtension;
use syntax_ext::proc_macro_impl::BangProcMacro;
@ -454,8 +453,9 @@ impl cstore::CStore {
return LoadedMacro::ProcMacro(Lrc::new(ext));
}
let (name, def) = data.get_macro(id.index);
let source_name = FileName::Macros(name.to_string());
let def = data.get_macro(id.index);
let macro_full_name = data.def_path(id.index).to_string_friendly(|_| data.imported_name);
let source_name = FileName::Macros(macro_full_name);
let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
let local_span = Span::new(source_file.start_pos, source_file.end_pos, NO_EXPANSION);

View file

@ -1106,10 +1106,10 @@ impl<'a, 'tcx> CrateMetadata {
}
}
pub fn get_macro(&self, id: DefIndex) -> (InternedString, MacroDef) {
pub fn get_macro(&self, id: DefIndex) -> MacroDef {
let entry = self.entry(id);
match entry.kind {
EntryKind::MacroDef(macro_def) => (self.item_name(id), macro_def.decode(self)),
EntryKind::MacroDef(macro_def) => macro_def.decode(self),
_ => bug!(),
}
}

View file

@ -1319,7 +1319,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
);
if let StatementKind::Assign(
Place::Local(assigned_to),
rvalue,
box rvalue,
) = &stmt.kind {
debug!("annotate_argument_and_return_for_borrow: assigned_to={:?} \
rvalue={:?}", assigned_to, rvalue);
@ -1823,7 +1823,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
None => return OtherUse(self.mir.source_info(location).span),
};
if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
if let StatementKind::Assign(_, box Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
if let AggregateKind::Closure(def_id, _) = **kind {
debug!("find_closure_move_span: found closure {:?}", places);
@ -1886,7 +1886,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}
for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
if let StatementKind::Assign(_, box Rvalue::Aggregate(ref kind, ref places))
= stmt.kind {
if let AggregateKind::Closure(def_id, _) = **kind {
debug!("find_closure_borrow_span: found closure {:?}", places);

View file

@ -534,7 +534,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
ref inputs,
} => {
let context = ContextKind::InlineAsm.new(location);
for (o, output) in asm.outputs.iter().zip(outputs) {
for (o, output) in asm.outputs.iter().zip(outputs.iter()) {
if o.is_indirect {
// FIXME(eddyb) indirect inline asm outputs should
// be encoeded through MIR place derefs instead.
@ -561,7 +561,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
);
}
}
for input in inputs {
for input in inputs.iter() {
self.consume_operand(context, (input, span), flow_state);
}
}

View file

@ -100,7 +100,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
// flow could be used.
if let Some(StatementKind::Assign(
Place::Local(local),
Rvalue::Use(Operand::Move(move_from)),
box Rvalue::Use(Operand::Move(move_from)),
)) = self.mir.basic_blocks()[location.block]
.statements
.get(location.statement_index)

View file

@ -109,7 +109,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
ref inputs,
} => {
let context = ContextKind::InlineAsm.new(location);
for (o, output) in asm.outputs.iter().zip(outputs) {
for (o, output) in asm.outputs.iter().zip(outputs.iter()) {
if o.is_indirect {
// FIXME(eddyb) indirect inline asm outputs should
// be encoeded through MIR place derefs instead.
@ -128,7 +128,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
);
}
}
for input in inputs {
for input in inputs.iter() {
self.consume_operand(context, input);
}
}
@ -479,7 +479,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
/// Generate a new invalidates(L, B) fact
fn generate_invalidates(&mut self, b: BorrowIndex, l: Location) {
let lidx = self.location_table.mid_index(l);
let lidx = self.location_table.start_index(l);
self.all_facts.invalidates.push((lidx, b));
}
}

View file

@ -10,15 +10,13 @@
use super::universal_regions::UniversalRegions;
use borrow_check::nll::constraints::graph::NormalConstraintGraph;
use borrow_check::nll::constraints::{
ConstraintSccIndex, ConstraintSet, OutlivesConstraint,
};
use borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint};
use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex};
use borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations;
use borrow_check::nll::type_check::Locations;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::region_constraints::{GenericKind, VarInfos};
use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin};
use rustc::mir::{
ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location,
@ -29,7 +27,7 @@ use rustc::util::common;
use rustc_data_structures::bit_set::BitSet;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::{DiagnosticBuilder, Diagnostic};
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use std::rc::Rc;
@ -71,6 +69,15 @@ pub struct RegionInferenceContext<'tcx> {
/// visible from this index.
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
/// Contains a "representative" from each SCC. This will be the
/// minimal RegionVid belonging to that universe. It is used as a
/// kind of hacky way to manage checking outlives relationships,
/// since we can 'canonicalize' each region to the representative
/// of its SCC and be sure that -- if they have the same repr --
/// they *must* be equal (though not having the same repr does not
/// mean they are unequal).
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
/// The final inferred values of the region variables; we compute
/// one value per SCC. To get the value for any given *region*,
/// you first find which scc it is a part of.
@ -162,42 +169,7 @@ pub struct TypeTest<'tcx> {
/// A test which, if met by the region `'x`, proves that this type
/// constraint is satisfied.
pub test: RegionTest,
}
/// A "test" that can be applied to some "subject region" `'x`. These are used to
/// describe type constraints. Tests do not presently affect the
/// region values that get inferred for each variable; they only
/// examine the results *after* inference. This means they can
/// conveniently include disjuction ("a or b must be true").
#[derive(Clone, Debug)]
pub enum RegionTest {
/// The subject region `'x` must by outlived by *some* region in
/// the given set of regions.
///
/// This test comes from e.g. a where clause like `T: 'a + 'b`,
/// which implies that we know that `T: 'a` and that `T:
/// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can
/// do so by showing that `'a: 'x` *or* `'b: 'x`.
IsOutlivedByAnyRegionIn(Vec<RegionVid>),
/// The subject region `'x` must by outlived by *all* regions in
/// the given set of regions.
///
/// This test comes from e.g. a projection type like `T = <u32 as
/// Trait<'a, 'b>>::Foo`, which must outlive `'a` or `'b`, and
/// maybe both. Therefore we can prove that `T: 'x` if we know
/// that `'a: 'x` *and* `'b: 'x`.
IsOutlivedByAllRegionsIn(Vec<RegionVid>),
/// Any of the given tests are true.
///
/// This arises from projections, for which there are multiple
/// ways to prove an outlives relationship.
Any(Vec<RegionTest>),
/// All of the given tests are true.
All(Vec<RegionTest>),
pub verify_bound: VerifyBound<'tcx>,
}
impl<'tcx> RegionInferenceContext<'tcx> {
@ -245,6 +217,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
let mut result = Self {
definitions,
liveness_constraints,
@ -252,6 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
constraint_graph,
constraint_sccs,
scc_universes,
scc_representatives,
scc_values,
type_tests,
universal_regions,
@ -288,6 +263,27 @@ impl<'tcx> RegionInferenceContext<'tcx> {
scc_universes
}
/// For each SCC, we compute a unique `RegionVid` (in fact, the
/// minimal one that belongs to the SCC). See
/// `scc_representatives` field of `RegionInferenceContext` for
/// more details.
fn compute_scc_representatives(
constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
let num_sccs = constraints_scc.num_sccs();
let next_region_vid = definitions.next_index();
let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs);
for region_vid in definitions.indices() {
let scc = constraints_scc.scc(region_vid);
let prev_min = scc_representatives[scc];
scc_representatives[scc] = region_vid.min(prev_min);
}
scc_representatives
}
/// Initializes the region variables for each universally
/// quantified region (lifetime parameter). The first N variables
/// always correspond to the regions appearing in the function
@ -582,7 +578,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
for type_test in &self.type_tests {
debug!("check_type_test: {:?}", type_test);
if self.eval_region_test(mir, type_test.lower_bound, &type_test.test) {
let generic_ty = type_test.generic_kind.to_ty(tcx);
if self.eval_verify_bound(
tcx,
mir,
generic_ty,
type_test.lower_bound,
&type_test.verify_bound,
) {
continue;
}
@ -689,7 +692,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
generic_kind,
lower_bound,
locations,
test: _,
verify_bound: _,
} = type_test;
let generic_ty = generic_kind.to_ty(tcx);
@ -716,7 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// where `ur` is a local bound -- we are sometimes in a
// position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_region_test(mir, ur, &type_test.test) {
if self.eval_verify_bound(tcx, mir, generic_ty, ur, &type_test.verify_bound) {
continue;
}
@ -888,31 +891,99 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// Test if `test` is true when applied to `lower_bound` at
/// `point`, and returns true or false.
fn eval_region_test(&self, mir: &Mir<'tcx>, lower_bound: RegionVid, test: &RegionTest) -> bool {
fn eval_verify_bound(
&self,
tcx: TyCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
generic_ty: Ty<'tcx>,
lower_bound: RegionVid,
verify_bound: &VerifyBound<'tcx>,
) -> bool {
debug!(
"eval_region_test(lower_bound={:?}, test={:?})",
lower_bound, test
"eval_verify_bound(lower_bound={:?}, verify_bound={:?})",
lower_bound, verify_bound
);
match test {
RegionTest::IsOutlivedByAllRegionsIn(regions) => regions
.iter()
.all(|&r| self.eval_outlives(mir, r, lower_bound)),
match verify_bound {
VerifyBound::IfEq(test_ty, verify_bound1) => {
self.eval_if_eq(tcx, mir, generic_ty, lower_bound, test_ty, verify_bound1)
}
RegionTest::IsOutlivedByAnyRegionIn(regions) => regions
.iter()
.any(|&r| self.eval_outlives(mir, r, lower_bound)),
VerifyBound::OutlivedBy(r) => {
let r_vid = self.to_region_vid(r);
self.eval_outlives(mir, r_vid, lower_bound)
}
RegionTest::Any(tests) => tests
.iter()
.any(|test| self.eval_region_test(mir, lower_bound, test)),
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
}),
RegionTest::All(tests) => tests
.iter()
.all(|test| self.eval_region_test(mir, lower_bound, test)),
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
}),
}
}
fn eval_if_eq(
&self,
tcx: TyCtxt<'_, '_, 'tcx>,
mir: &Mir<'tcx>,
generic_ty: Ty<'tcx>,
lower_bound: RegionVid,
test_ty: Ty<'tcx>,
verify_bound: &VerifyBound<'tcx>,
) -> bool {
let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty);
let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty);
if generic_ty_normalized == test_ty_normalized {
self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound)
} else {
false
}
}
/// This is a conservative normalization procedure. It takes every
/// free region in `value` and replaces it with the
/// "representative" of its SCC (see `scc_representatives` field).
/// We are guaranteed that if two values normalize to the same
/// thing, then they are equal; this is a conservative check in
/// that they could still be equal even if they normalize to
/// different results. (For example, there might be two regions
/// with the same value that are not in the same SCC).
///
/// NB. This is not an ideal approach and I would like to revisit
/// it. However, it works pretty well in practice. In particular,
/// this is needed to deal with projection outlives bounds like
///
/// <T as Foo<'0>>::Item: '1
///
/// In particular, this routine winds up being important when
/// there are bounds like `where <T as Foo<'a>>::Item: 'b` in the
/// environment. In this case, if we can show that `'0 == 'a`,
/// and that `'b: '1`, then we know that the clause is
/// satisfied. In such cases, particularly due to limitations of
/// the trait solver =), we usually wind up with a where-clause like
/// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as
/// a constraint, and thus ensures that they are in the same SCC.
///
/// So why can't we do a more correct routine? Well, we could
/// *almost* use the `relate_tys` code, but the way it is
/// currently setup it creates inference variables to deal with
/// higher-ranked things and so forth, and right now the inference
/// context is not permitted to make more inference variables. So
/// we use this kind of hacky solution.
fn normalize_to_scc_representatives<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
tcx.fold_regions(&value, &mut false, |r, _db| {
let vid = self.to_region_vid(r);
let scc = self.constraint_sccs.scc(vid);
let repr = self.scc_representatives[scc];
tcx.mk_region(ty::ReVar(repr))
})
}
// Evaluate whether `sup_region: sub_region @ point`.
fn eval_outlives(
&self,

View file

@ -8,13 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::location::LocationTable;
use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint};
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::region_infer::{RegionTest, TypeTest};
use borrow_check::nll::region_infer::TypeTest;
use borrow_check::nll::type_check::Locations;
use borrow_check::nll::universal_regions::UniversalRegions;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate};
use rustc::infer::region_constraints::{GenericKind, VerifyBound};
use rustc::infer::{self, SubregionOrigin};
@ -25,35 +24,30 @@ use syntax_pos::DUMMY_SP;
crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> {
tcx: TyCtxt<'a, 'gcx, 'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
locations: Locations,
category: ConstraintCategory,
outlives_constraints: &'a mut ConstraintSet,
type_tests: &'a mut Vec<TypeTest<'tcx>>,
all_facts: &'a mut Option<AllFacts>,
}
impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
crate fn new(
tcx: TyCtxt<'a, 'gcx, 'tcx>,
universal_regions: &'a UniversalRegions<'tcx>,
location_table: &'a LocationTable,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
param_env: ty::ParamEnv<'tcx>,
locations: Locations,
category: ConstraintCategory,
outlives_constraints: &'a mut ConstraintSet,
type_tests: &'a mut Vec<TypeTest<'tcx>>,
all_facts: &'a mut Option<AllFacts>,
) -> Self {
Self {
tcx,
universal_regions,
location_table,
region_bound_pairs,
implicit_region_bound,
param_env,
@ -61,7 +55,6 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
category,
outlives_constraints,
type_tests,
all_facts,
}
}
@ -100,23 +93,6 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
let r1_vid = self.to_region_vid(r1);
let r2_vid = self.to_region_vid(r2);
self.add_outlives(r1_vid, r2_vid);
// In the new analysis, all outlives relations etc
// "take effect" at the mid point of the statement
// that requires them, so ignore the `at_location`.
if let Some(all_facts) = &mut self.all_facts {
if let Some(from_location) = self.locations.from_location() {
all_facts.outlives.push((
r1_vid,
r2_vid,
self.location_table.mid_index(from_location),
));
} else {
for location in self.location_table.all_points() {
all_facts.outlives.push((r1_vid, r2_vid, location));
}
}
}
}
UnpackedKind::Type(t1) => {
@ -139,43 +115,15 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> {
&self,
generic_kind: GenericKind<'tcx>,
region: ty::Region<'tcx>,
bound: VerifyBound<'tcx>,
verify_bound: VerifyBound<'tcx>,
) -> TypeTest<'tcx> {
let lower_bound = self.to_region_vid(region);
let test = self.verify_bound_to_region_test(&bound);
TypeTest {
generic_kind,
lower_bound,
locations: self.locations,
test,
}
}
fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest {
match verify_bound {
VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn(
regions.iter().map(|r| self.to_region_vid(r)).collect(),
),
VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn(
regions.iter().map(|r| self.to_region_vid(r)).collect(),
),
VerifyBound::AnyBound(bounds) => RegionTest::Any(
bounds
.iter()
.map(|b| self.verify_bound_to_region_test(b))
.collect(),
),
VerifyBound::AllBounds(bounds) => RegionTest::All(
bounds
.iter()
.map(|b| self.verify_bound_to_region_test(b))
.collect(),
),
verify_bound,
}
}

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::location::LocationTable;
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::type_check::constraint_conversion;
use borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints};
use borrow_check::nll::universal_regions::UniversalRegions;
@ -69,19 +67,15 @@ crate struct CreateResult<'tcx> {
crate fn create(
infcx: &InferCtxt<'_, '_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
location_table: &LocationTable,
implicit_region_bound: Option<ty::Region<'tcx>>,
universal_regions: &Rc<UniversalRegions<'tcx>>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
all_facts: &mut Option<AllFacts>,
) -> CreateResult<'tcx> {
UniversalRegionRelationsBuilder {
infcx,
param_env,
implicit_region_bound,
constraints,
location_table,
all_facts,
universal_regions: universal_regions.clone(),
region_bound_pairs: Vec::new(),
relations: UniversalRegionRelations {
@ -210,11 +204,9 @@ impl UniversalRegionRelations<'tcx> {
struct UniversalRegionRelationsBuilder<'this, 'gcx: 'tcx, 'tcx: 'this> {
infcx: &'this InferCtxt<'this, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
location_table: &'this LocationTable,
universal_regions: Rc<UniversalRegions<'tcx>>,
implicit_region_bound: Option<ty::Region<'tcx>>,
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
all_facts: &'this mut Option<AllFacts>,
// outputs:
relations: UniversalRegionRelations<'tcx>,
@ -281,7 +273,6 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
constraint_conversion::ConstraintConversion::new(
self.infcx.tcx,
&self.universal_regions,
&self.location_table,
&self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
@ -289,7 +280,6 @@ impl UniversalRegionRelationsBuilder<'cx, 'gcx, 'tcx> {
ConstraintCategory::Internal,
&mut self.constraints.outlives_constraints,
&mut self.constraints.type_tests,
&mut self.all_facts,
).convert_all(&data);
}

View file

@ -17,6 +17,7 @@
//! types, instead of all variables.
use borrow_check::nll::ToRegionVid;
use borrow_check::nll::facts::{AllFacts, AllFactsExt};
use rustc::mir::{Local, Mir};
use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
@ -61,12 +62,13 @@ impl NllLivenessMap {
mir: &Mir<'tcx>,
) -> Self {
let mut to_local = IndexVec::default();
let facts_enabled = AllFacts::enabled(tcx);
let from_local: IndexVec<Local, Option<_>> = mir.local_decls
.iter_enumerated()
.map(|(local, local_decl)| {
if tcx.all_free_regions_meet(&local_decl.ty, |r| {
free_regions.contains(&r.to_region_vid())
}) {
}) && !facts_enabled {
// If all the regions in the type are free regions
// (or there are no regions), then we don't need
// to track liveness for this variable.

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::location::LocationTable;
use borrow_check::nll::region_infer::values::RegionValueElements;
use borrow_check::nll::constraints::ConstraintSet;
use borrow_check::nll::NllLivenessMap;
@ -40,6 +41,7 @@ pub(super) fn generate<'gcx, 'tcx>(
elements: &Rc<RegionValueElements>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
location_table: &LocationTable,
) {
debug!("liveness::generate");
let free_regions = {
@ -51,7 +53,7 @@ pub(super) fn generate<'gcx, 'tcx>(
)
};
let liveness_map = NllLivenessMap::compute(typeck.tcx(), &free_regions, mir);
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map);
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map, location_table);
}
/// Compute all regions that are (currently) known to outlive free

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::location::LocationTable;
use borrow_check::nll::constraints::ConstraintCategory;
use borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
use borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
@ -49,6 +50,7 @@ pub(super) fn trace(
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
liveness_map: &NllLivenessMap,
location_table: &LocationTable,
) {
debug!("trace()");
@ -67,6 +69,7 @@ pub(super) fn trace(
move_data,
liveness_map,
drop_data: FxHashMap::default(),
location_table,
};
LivenessResults::new(cx).compute_for_all_locals();
@ -105,6 +108,9 @@ where
/// Map tracking which variables need liveness computation.
liveness_map: &'me NllLivenessMap,
/// Maps between a MIR Location and a LocationIndex
location_table: &'me LocationTable,
}
struct DropData<'tcx> {
@ -453,7 +459,13 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
) {
debug!("add_use_live_facts_for(value={:?})", value);
Self::make_all_regions_live(self.elements, &mut self.typeck, value, live_at)
Self::make_all_regions_live(
self.elements,
&mut self.typeck,
value,
live_at,
self.location_table,
)
}
/// Some variable with type `live_ty` is "drop live" at `location`
@ -505,7 +517,13 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for &kind in &drop_data.dropck_result.kinds {
Self::make_all_regions_live(self.elements, &mut self.typeck, kind, live_at);
Self::make_all_regions_live(
self.elements,
&mut self.typeck,
kind,
live_at,
self.location_table,
);
}
}
@ -514,6 +532,7 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
typeck: &mut TypeChecker<'_, '_, 'tcx>,
value: impl TypeFoldable<'tcx>,
live_at: &HybridBitSet<PointIndex>,
location_table: &LocationTable,
) {
debug!("make_all_regions_live(value={:?})", value);
debug!(
@ -532,8 +551,12 @@ impl LivenessContext<'_, '_, '_, '_, 'tcx> {
.liveness_constraints
.add_elements(live_region_vid, live_at);
if let Some(_) = borrowck_context.all_facts {
bug!("polonius liveness facts not implemented yet")
if let Some(facts) = borrowck_context.all_facts {
for point in live_at.iter() {
let loc = elements.to_location(point);
facts.region_live_at.push((live_region_vid, location_table.start_index(loc)));
facts.region_live_at.push((live_region_vid, location_table.mid_index(loc)));
}
}
});
}

View file

@ -17,37 +17,38 @@ use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, Outlives
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
use borrow_check::nll::renumber;
use borrow_check::nll::type_check::free_region_relations::{
CreateResult, UniversalRegionRelations,
};
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::ToRegionVid;
use borrow_check::nll::renumber;
use dataflow::move_paths::MoveData;
use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedPlaces;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::*;
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp;
use rustc::traits::query::{Fallible, NoSolution};
use rustc::traits::{ObligationCause, PredicateObligations};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
use rustc::ty::subst::Subst;
use std::fmt;
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
use std::{fmt, iter};
use std::rc::Rc;
use syntax_pos::{Span, DUMMY_SP};
use transform::{MirPass, MirSource};
use rustc_data_structures::fx::FxHashSet;
use either::Either;
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
@ -135,41 +136,35 @@ pub(crate) fn type_check<'gcx, 'tcx>(
} = free_region_relations::create(
infcx,
param_env,
location_table,
Some(implicit_region_bound),
universal_regions,
&mut constraints,
all_facts,
);
{
let mut borrowck_context = BorrowCheckContext {
universal_regions,
location_table,
borrow_set,
all_facts,
constraints: &mut constraints,
};
let mut borrowck_context = BorrowCheckContext {
universal_regions,
location_table,
borrow_set,
all_facts,
constraints: &mut constraints,
};
type_check_internal(
infcx,
mir_def_id,
param_env,
mir,
&region_bound_pairs,
Some(implicit_region_bound),
Some(&mut borrowck_context),
Some(&universal_region_relations),
|cx| {
cx.equate_inputs_and_outputs(
mir,
universal_regions,
&normalized_inputs_and_output,
);
liveness::generate(cx, mir, elements, flow_inits, move_data);
},
);
}
type_check_internal(
infcx,
mir_def_id,
param_env,
mir,
&region_bound_pairs,
Some(implicit_region_bound),
Some(&mut borrowck_context),
Some(&universal_region_relations),
|cx| {
cx.equate_inputs_and_outputs(mir, universal_regions, &normalized_inputs_and_output);
liveness::generate(cx, mir, elements, flow_inits, move_data, location_table);
cx.borrowck_context.as_mut().map(|bcx| translate_outlives_facts(bcx));
},
);
MirTypeckResults {
constraints,
@ -182,7 +177,7 @@ fn type_check_internal<'a, 'gcx, 'tcx, R>(
mir_def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
mir: &'a Mir<'tcx>,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>,
@ -212,6 +207,27 @@ fn type_check_internal<'a, 'gcx, 'tcx, R>(
extra(&mut checker)
}
fn translate_outlives_facts(cx: &mut BorrowCheckContext) {
if let Some(facts) = cx.all_facts {
let location_table = cx.location_table;
facts.outlives.extend(
cx.constraints.outlives_constraints.iter().flat_map(|constraint: &OutlivesConstraint| {
if let Some(from_location) = constraint.locations.from_location() {
Either::Left(iter::once((
constraint.sup,
constraint.sub,
location_table.mid_index(from_location),
)))
} else {
Either::Right(location_table.all_points().map(move |location| {
(constraint.sup, constraint.sub, location)
}))
}
})
);
}
}
fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
// We sometimes see MIR failures (notably predicate failures) due to
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
@ -377,14 +393,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty);
if let Err(terr) = self.cx
.eq_types(
constant.literal.ty,
constant.ty,
location.to_locations(),
ConstraintCategory::Boring,
)
{
if let Err(terr) = self.cx.eq_types(
constant.literal.ty,
constant.ty,
location.to_locations(),
ConstraintCategory::Boring,
) {
span_mirbug!(
self,
constant,
@ -429,12 +443,10 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
let sty = self.sanitize_type(place, sty);
let ty = self.tcx().type_of(def_id);
let ty = self.cx.normalize(ty, location);
if let Err(terr) = self.cx.eq_types(
ty,
sty,
location.to_locations(),
ConstraintCategory::Boring,
) {
if let Err(terr) =
self.cx
.eq_types(ty, sty, location.to_locations(), ConstraintCategory::Boring)
{
span_mirbug!(
self,
place,
@ -693,7 +705,7 @@ struct TypeChecker<'a, 'gcx: 'tcx, 'tcx: 'a> {
last_span: Span,
mir: &'a Mir<'tcx>,
mir_def_id: DefId,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
@ -802,7 +814,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
mir: &'a Mir<'tcx>,
mir_def_id: DefId,
param_env: ty::ParamEnv<'gcx>,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: Option<ty::Region<'tcx>>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>,
@ -861,7 +873,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
constraint_conversion::ConstraintConversion::new(
self.infcx.tcx,
borrowck_context.universal_regions,
borrowck_context.location_table,
self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
@ -869,7 +880,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
category,
&mut borrowck_context.constraints.outlives_constraints,
&mut borrowck_context.constraints.type_tests,
&mut borrowck_context.all_facts,
).convert_all(&data);
}
}
@ -955,66 +965,55 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let tcx = infcx.tcx;
let param_env = self.param_env;
let parent_def_id = infcx.tcx.closure_base_def_id(self.mir_def_id);
let opaque_type_map =
self.fully_perform_op(
locations,
category,
CustomTypeOp::new(
|infcx| {
let mut obligations = ObligationAccumulator::default();
let opaque_type_map = self.fully_perform_op(
locations,
category,
CustomTypeOp::new(
|infcx| {
let mut obligations = ObligationAccumulator::default();
let dummy_body_id = ObligationCause::dummy().body_id;
let (output_ty, opaque_type_map) =
obligations.add(infcx.instantiate_opaque_types(
parent_def_id,
dummy_body_id,
param_env,
&anon_ty,
));
let dummy_body_id = ObligationCause::dummy().body_id;
let (output_ty, opaque_type_map) =
obligations.add(infcx.instantiate_opaque_types(
parent_def_id,
dummy_body_id,
param_env,
&anon_ty,
));
debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty, opaque_type_map, revealed_ty
);
obligations.add(infcx
.at(&ObligationCause::dummy(), param_env)
.eq(output_ty, revealed_ty)?);
for (&opaque_def_id, opaque_decl) in &opaque_type_map {
let opaque_defn_ty = tcx.type_of(opaque_def_id);
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
let opaque_defn_ty = renumber::renumber_regions(infcx, &opaque_defn_ty);
debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty,
opaque_type_map,
revealed_ty
);
obligations.add(
infcx
.at(&ObligationCause::dummy(), param_env)
.eq(output_ty, revealed_ty)?,
"eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}",
opaque_decl.concrete_ty, opaque_defn_ty
);
obligations.add(infcx
.at(&ObligationCause::dummy(), param_env)
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?);
}
for (&opaque_def_id, opaque_decl) in &opaque_type_map {
let opaque_defn_ty = tcx.type_of(opaque_def_id);
let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs);
let opaque_defn_ty = renumber::renumber_regions(
infcx,
&opaque_defn_ty,
);
debug!(
"eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}",
opaque_decl.concrete_ty,
opaque_defn_ty
);
obligations.add(
infcx
.at(&ObligationCause::dummy(), param_env)
.eq(opaque_decl.concrete_ty, opaque_defn_ty)?,
);
}
debug!("eq_opaque_type_and_type: equated");
debug!("eq_opaque_type_and_type: equated");
Ok(InferOk {
value: Some(opaque_type_map),
obligations: obligations.into_vec(),
})
},
|| "input_output".to_string(),
),
)?;
Ok(InferOk {
value: Some(opaque_type_map),
obligations: obligations.into_vec(),
})
},
|| "input_output".to_string(),
),
)?;
let universal_region_relations = match self.universal_region_relations {
Some(rel) => rel,
@ -1035,7 +1034,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
infcx.constrain_opaque_type(
opaque_def_id,
&opaque_decl,
universal_region_relations
universal_region_relations,
);
Ok(InferOk {
value: (),
@ -1073,12 +1072,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let place_ty = place.ty(mir, tcx).to_ty(tcx);
let rv_ty = rv.ty(mir, tcx);
if let Err(terr) = self.sub_types_or_anon(
rv_ty,
place_ty,
location.to_locations(),
category,
) {
if let Err(terr) =
self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category)
{
span_mirbug!(
self,
stmt,
@ -1117,7 +1113,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::SizedBound,
ConstraintCategory::SizedBound,
);
}
}
@ -1148,15 +1144,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
StatementKind::AscribeUserType(ref place, variance, c_ty) => {
let place_ty = place.ty(mir, tcx).to_ty(tcx);
if let Err(terr) =
self.relate_type_and_user_type(
place_ty,
variance,
c_ty,
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
)
{
if let Err(terr) = self.relate_type_and_user_type(
place_ty,
variance,
c_ty,
Locations::All(stmt.source_info.span),
ConstraintCategory::TypeAnnotation,
) {
span_mirbug!(
self,
stmt,
@ -1208,12 +1202,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let rv_ty = value.ty(mir, tcx);
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types(
rv_ty,
place_ty,
locations,
ConstraintCategory::Assignment,
) {
if let Err(terr) =
self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment)
{
span_mirbug!(
self,
term,
@ -1327,8 +1318,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ty,
term_location.to_locations(),
ConstraintCategory::Return,
)
{
) {
span_mirbug!(
self,
term,
@ -1366,12 +1356,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
let locations = term_location.to_locations();
if let Err(terr) = self.sub_types_or_anon(
sig.output(),
dest_ty,
locations,
category,
) {
if let Err(terr) =
self.sub_types_or_anon(sig.output(), dest_ty, locations, category)
{
span_mirbug!(
self,
term,
@ -1539,12 +1526,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
fn check_local(
&mut self,
mir: &Mir<'tcx>,
local: Local,
local_decl: &LocalDecl<'tcx>,
) {
fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) {
match mir.local_kind(local) {
LocalKind::ReturnPointer | LocalKind::Arg => {
// return values of normal functions are required to be
@ -1713,13 +1695,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -1739,13 +1721,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -1768,13 +1750,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
ConstraintCategory::Cast,
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}
@ -1957,14 +1939,6 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
category: ConstraintCategory::Boring,
});
if let Some(all_facts) = all_facts {
all_facts.outlives.push((
ref_region.to_region_vid(),
borrow_region.to_region_vid(),
location_table.mid_index(location),
));
}
match mutbl {
hir::Mutability::MutImmutable => {
// Immutable reference. We don't need the base
@ -2232,7 +2206,7 @@ impl MirPass for TypeckMir {
def_id,
param_env,
mir,
&[],
&vec![],
None,
None,
None,
@ -2277,4 +2251,3 @@ impl<'tcx> ObligationAccumulator<'tcx> {
self.obligations
}
}

View file

@ -10,15 +10,13 @@
use borrow_check::nll::constraints::{ConstraintCategory, OutlivesConstraint};
use borrow_check::nll::type_check::{BorrowCheckContext, Locations};
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::ToRegionVid;
use rustc::infer::canonical::{Canonical, CanonicalVarInfos};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc::traits::query::Fallible;
use rustc::ty::fold::{TypeFoldable, TypeVisitor};
use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc::ty::subst::Kind;
use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt};
use rustc::ty::{self, CanonicalTy, CanonicalVar, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::IndexVec;
@ -33,11 +31,9 @@ pub(super) fn sub_types<'tcx>(
) -> Fallible<()> {
debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
ty::Variance::Covariant,
locations,
category,
borrowck_context,
ty::List::empty(),
).relate(&a, &b)?;
Ok(())
@ -54,11 +50,9 @@ pub(super) fn eq_types<'tcx>(
) -> Fallible<()> {
debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
ty::Variance::Invariant,
locations,
category,
borrowck_context,
ty::List::empty(),
).relate(&a, &b)?;
Ok(())
@ -91,18 +85,22 @@ pub(super) fn relate_type_and_user_type<'tcx>(
let v1 = ty::Contravariant.xform(v);
TypeRelating::new(
infcx,
infcx.tcx,
NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category),
v1,
locations,
category,
borrowck_context,
b_variables,
).relate(&b_value, &a)?;
Ok(())
}
struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx>,
{
tcx: TyCtxt<'me, 'gcx, 'tcx>,
/// Callback to use when we deduce an outlives relationship
delegate: D,
/// How are we relating `a` and `b`?
///
@ -122,19 +120,10 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
///
/// This field stores the instantiations for late-bound regions in
/// the `a` type.
a_scopes: Vec<BoundRegionScope>,
a_scopes: Vec<BoundRegionScope<'tcx>>,
/// Same as `a_scopes`, but for the `b` type.
b_scopes: Vec<BoundRegionScope>,
/// Where (and why) is this relation taking place?
locations: Locations,
category: ConstraintCategory,
/// This will be `Some` when we are running the type check as part
/// of NLL, and `None` if we are running a "sanity check".
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
b_scopes: Vec<BoundRegionScope<'tcx>>,
/// As we execute, the type on the LHS *may* come from a canonical
/// source. In that case, we will sometimes find a constraint like
@ -150,37 +139,128 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> {
canonical_var_values: IndexVec<CanonicalVar, Option<Kind<'tcx>>>,
}
trait TypeRelatingDelegate<'tcx> {
/// Push a constraint `sup: sub` -- this constraint must be
/// satisfied for the two types to be related. `sub` and `sup` may
/// be regions from the type or new variables created through the
/// delegate.
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
/// Creates a new region variable representing an instantiated
/// higher-ranked region; this will be either existential or
/// universal depending on the context. So e.g. if you have
/// `for<'a> fn(..) <: for<'b> fn(..)`, then we will first
/// instantiate `'b` with a universally quantitifed region and
/// then `'a` with an existentially quantified region (the order
/// is important so that the existential region `'a` can see the
/// universal one).
fn next_region_var(
&mut self,
universally_quantified: UniversallyQuantified,
) -> ty::Region<'tcx>;
/// Creates a new existential region in the given universe. This
/// is used when handling subtyping and type variables -- if we
/// have that `?X <: Foo<'a>`, for example, we would instantiate
/// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh
/// existential variable created by this function. We would then
/// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives
/// relation stating that `'?0: 'a`).
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
}
struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>,
/// Where (and why) is this relation taking place?
locations: Locations,
/// What category do we assign the resulting `'a: 'b` relationships?
category: ConstraintCategory,
}
impl NllTypeRelatingDelegate<'me, 'bccx, 'gcx, 'tcx> {
fn new(
infcx: &'me InferCtxt<'me, 'gcx, 'tcx>,
borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>,
locations: Locations,
category: ConstraintCategory,
) -> Self {
Self {
infcx,
borrowck_context,
locations,
category,
}
}
}
impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> {
fn next_region_var(
&mut self,
universally_quantified: UniversallyQuantified,
) -> ty::Region<'tcx> {
let origin = if universally_quantified.0 {
NLLRegionVariableOrigin::BoundRegion(self.infcx.create_subuniverse())
} else {
NLLRegionVariableOrigin::Existential
};
self.infcx.next_nll_region_var(origin)
}
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
self.infcx
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, universe)
}
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
if let Some(borrowck_context) = &mut self.borrowck_context {
let sub = borrowck_context.universal_regions.to_region_vid(sub);
let sup = borrowck_context.universal_regions.to_region_vid(sup);
borrowck_context
.constraints
.outlives_constraints
.push(OutlivesConstraint {
sup,
sub,
locations: self.locations,
category: self.category,
});
}
}
}
#[derive(Clone, Debug)]
struct ScopesAndKind<'tcx> {
scopes: Vec<BoundRegionScope>,
scopes: Vec<BoundRegionScope<'tcx>>,
kind: Kind<'tcx>,
}
#[derive(Clone, Debug, Default)]
struct BoundRegionScope {
map: FxHashMap<ty::BoundRegion, RegionVid>,
struct BoundRegionScope<'tcx> {
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
}
#[derive(Copy, Clone)]
struct UniversallyQuantified(bool);
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
impl<'me, 'gcx, 'tcx, D> TypeRelating<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn new(
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
tcx: TyCtxt<'me, 'gcx, 'tcx>,
delegate: D,
ambient_variance: ty::Variance,
locations: Locations,
category: ConstraintCategory,
borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>,
canonical_var_infos: CanonicalVarInfos<'tcx>,
) -> Self {
let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len());
Self {
infcx,
tcx,
delegate,
ambient_variance,
borrowck_context,
locations,
canonical_var_values,
category,
a_scopes: vec![],
b_scopes: vec![],
}
@ -204,10 +284,10 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
&mut self,
value: &ty::Binder<impl TypeFoldable<'tcx>>,
universally_quantified: UniversallyQuantified,
) -> BoundRegionScope {
) -> BoundRegionScope<'tcx> {
let mut scope = BoundRegionScope::default();
value.skip_binder().visit_with(&mut ScopeInstantiator {
infcx: self.infcx,
delegate: &mut self.delegate,
target_index: ty::INNERMOST,
universally_quantified,
bound_region_scope: &mut scope,
@ -227,8 +307,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
debruijn: ty::DebruijnIndex,
br: &ty::BoundRegion,
first_free_index: ty::DebruijnIndex,
scopes: &[BoundRegionScope],
) -> RegionVid {
scopes: &[BoundRegionScope<'tcx>],
) -> ty::Region<'tcx> {
// The debruijn index is a "reverse index" into the
// scopes listing. So when we have INNERMOST (0), we
// want the *last* scope pushed, and so forth.
@ -245,40 +325,23 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
/// with. Otherwise just return `r`.
fn replace_bound_region(
&self,
universal_regions: &UniversalRegions<'tcx>,
r: ty::Region<'tcx>,
first_free_index: ty::DebruijnIndex,
scopes: &[BoundRegionScope],
) -> RegionVid {
match r {
ty::ReLateBound(debruijn, br) => {
Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
}
ty::ReVar(v) => *v,
_ => universal_regions.to_region_vid(r),
scopes: &[BoundRegionScope<'tcx>],
) -> ty::Region<'tcx> {
if let ty::ReLateBound(debruijn, br) = r {
Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
} else {
r
}
}
/// Push a new outlives requirement into our output set of
/// constraints.
fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) {
fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) {
debug!("push_outlives({:?}: {:?})", sup, sub);
if let Some(borrowck_context) = &mut self.borrowck_context {
borrowck_context
.constraints
.outlives_constraints
.push(OutlivesConstraint {
sup,
sub,
locations: self.locations,
category: self.category,
});
// FIXME all facts!
}
self.delegate.push_outlives(sup, sub);
}
/// When we encounter a canonical variable `var` in the output,
@ -316,12 +379,10 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
return result;
}
fn generalize_value(
&self,
kind: Kind<'tcx>,
) -> Kind<'tcx> {
fn generalize_value(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> {
TypeGeneralizer {
type_rel: self,
tcx: self.tcx,
delegate: &mut self.delegate,
first_free_index: ty::INNERMOST,
ambient_variance: self.ambient_variance,
@ -333,11 +394,12 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> {
}
}
impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
for TypeRelating<'cx, 'bccx, 'gcx, 'tcx>
impl<D> TypeRelation<'me, 'gcx, 'tcx> for TypeRelating<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
self.infcx.tcx
fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> {
self.tcx
}
fn tag(&self) -> &'static str {
@ -397,37 +459,30 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
a: ty::Region<'tcx>,
b: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
if let Some(&mut BorrowCheckContext {
universal_regions, ..
}) = self.borrowck_context
{
if let ty::ReCanonical(var) = a {
self.relate_var(*var, b.into())?;
return Ok(a);
}
if let ty::ReCanonical(var) = a {
self.relate_var(*var, b.into())?;
return Ok(a);
}
debug!(
"regions(a={:?}, b={:?}, variance={:?})",
a, b, self.ambient_variance
);
debug!(
"regions(a={:?}, b={:?}, variance={:?})",
a, b, self.ambient_variance
);
let v_a =
self.replace_bound_region(universal_regions, a, ty::INNERMOST, &self.a_scopes);
let v_b =
self.replace_bound_region(universal_regions, b, ty::INNERMOST, &self.b_scopes);
let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes);
let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes);
debug!("regions: v_a = {:?}", v_a);
debug!("regions: v_b = {:?}", v_b);
debug!("regions: v_a = {:?}", v_a);
debug!("regions: v_b = {:?}", v_b);
if self.ambient_covariance() {
// Covariance: a <= b. Hence, `b: a`.
self.push_outlives(v_b, v_a);
}
if self.ambient_covariance() {
// Covariance: a <= b. Hence, `b: a`.
self.push_outlives(v_b, v_a);
}
if self.ambient_contravariance() {
// Contravariant: b <= a. Hence, `a: b`.
self.push_outlives(v_a, v_b);
}
if self.ambient_contravariance() {
// Contravariant: b <= a. Hence, `a: b`.
self.push_outlives(v_a, v_b);
}
Ok(a)
@ -527,10 +582,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
// Reset ambient variance to contravariance. See the
// covariant case above for an explanation.
let variance = ::std::mem::replace(
&mut self.ambient_variance,
ty::Variance::Contravariant,
);
let variance =
::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
self.relate(a.skip_binder(), b.skip_binder())?;
@ -551,15 +604,21 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx>
/// binder depth, and finds late-bound regions targeting the
/// `for<..`>. For each of those, it creates an entry in
/// `bound_region_scope`.
struct ScopeInstantiator<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
struct ScopeInstantiator<'me, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx> + 'me,
{
delegate: &'me mut D,
// The debruijn index of the scope we are instantiating.
target_index: ty::DebruijnIndex,
universally_quantified: UniversallyQuantified,
bound_region_scope: &'cx mut BoundRegionScope,
bound_region_scope: &'me mut BoundRegionScope<'tcx>,
}
impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
impl<'me, 'tcx, D> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> bool {
self.target_index.shift_in(1);
t.super_visit_with(self);
@ -570,21 +629,18 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
let ScopeInstantiator {
infcx,
universally_quantified,
bound_region_scope,
delegate,
..
} = *self;
} = self;
match r {
ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
self.bound_region_scope.map.entry(*br).or_insert_with(|| {
let origin = if universally_quantified.0 {
NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse())
} else {
NLLRegionVariableOrigin::Existential
};
infcx.next_nll_region_var(origin).to_region_vid()
});
bound_region_scope
.map
.entry(*br)
.or_insert_with(|| delegate.next_region_var(*universally_quantified));
}
_ => {}
@ -613,8 +669,13 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> {
/// scopes.
///
/// [blog post]: https://is.gd/0hKvIr
struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
type_rel: &'me TypeRelating<'me, 'bccx, 'gcx, 'tcx>,
struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D>
where
D: TypeRelatingDelegate<'tcx> + 'me,
{
tcx: TyCtxt<'me, 'gcx, 'tcx>,
delegate: &'me mut D,
/// After we generalize this type, we are going to relative it to
/// some other type. What will be the variance at this point?
@ -625,9 +686,12 @@ struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> {
universe: ty::UniverseIndex,
}
impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> {
impl<D> TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'gcx, 'tcx, D>
where
D: TypeRelatingDelegate<'tcx>,
{
fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> {
self.type_rel.infcx.tcx
self.tcx
}
fn tag(&self) -> &'static str {
@ -710,9 +774,7 @@ impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> {
// though, we may however need to check well-formedness or
// risk a problem like #41677 again.
let replacement_region_vid = self.type_rel
.infcx
.next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, self.universe);
let replacement_region_vid = self.delegate.generalize_existential(self.universe);
Ok(replacement_region_vid)
}

View file

@ -76,7 +76,7 @@ impl<'tcx> CFG<'tcx> {
rvalue: Rvalue<'tcx>) {
self.push(block, Statement {
source_info,
kind: StatementKind::Assign(place.clone(), rvalue)
kind: StatementKind::Assign(place.clone(), box rvalue)
});
}

View file

@ -143,11 +143,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let outputs = outputs
.into_iter()
.map(|output| unpack!(block = this.as_place(block, output)))
.collect();
.collect::<Vec<_>>()
.into_boxed_slice();
let inputs = inputs
.into_iter()
.map(|input| unpack!(block = this.as_local_operand(block, input)))
.collect();
.collect::<Vec<_>>()
.into_boxed_slice();
this.cfg.push(
block,
Statement {

View file

@ -270,7 +270,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
// re-consider the current implementations of the
// propagate_call_return method.
if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
if let mir::Rvalue::Ref(region, _, ref place) = **rhs {
if place.ignore_borrow(
self.tcx,
self.mir,

View file

@ -290,7 +290,7 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
self.gather_init(output, InitKind::Deep);
}
}
for input in inputs {
for input in inputs.iter() {
self.gather_operand(input);
}
}

View file

@ -539,7 +539,7 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
.emit();
} else if has_guard && !cx.tcx.allow_bind_by_move_patterns_with_guards() {
let mut err = struct_span_err!(cx.tcx.sess, p.span, E0008,
"cannot bind by-move into a pattern guard");
"cannot bind by-move into a pattern guard");
err.span_label(p.span, "moves value into pattern guard");
if cx.tcx.sess.opts.unstable_features.is_nightly_build() && cx.tcx.use_mir_borrowck() {
err.help("add #![feature(bind_by_move_pattern_guards)] to the \

View file

@ -514,7 +514,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
rval: OpTy<'tcx>,
) -> EvalResult<'tcx, (u128, usize)> {
trace!("read_discriminant_value {:#?}", rval.layout);
if rval.layout.abi.is_uninhabited() {
if rval.layout.abi == layout::Abi::Uninhabited {
return err!(Unreachable);
}

View file

@ -407,7 +407,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let ret_statement = self.make_statement(
StatementKind::Assign(
Place::Local(RETURN_PLACE),
Rvalue::Use(Operand::Copy(rcvr))
box Rvalue::Use(Operand::Copy(rcvr))
)
);
self.block(vec![ret_statement], TerminatorKind::Return, false);
@ -458,7 +458,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let statement = self.make_statement(
StatementKind::Assign(
ref_loc.clone(),
Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, src)
box Rvalue::Ref(tcx.types.re_erased, BorrowKind::Shared, src)
)
);
@ -485,7 +485,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let compute_cond = self.make_statement(
StatementKind::Assign(
cond.clone(),
Rvalue::BinaryOp(BinOp::Ne, Operand::Copy(end), Operand::Copy(beg))
box Rvalue::BinaryOp(BinOp::Ne, Operand::Copy(end), Operand::Copy(beg))
)
);
@ -521,13 +521,13 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
self.make_statement(
StatementKind::Assign(
Place::Local(beg),
Rvalue::Use(Operand::Constant(self.make_usize(0)))
box Rvalue::Use(Operand::Constant(self.make_usize(0)))
)
),
self.make_statement(
StatementKind::Assign(
end.clone(),
Rvalue::Use(Operand::Constant(self.make_usize(len)))
box Rvalue::Use(Operand::Constant(self.make_usize(len)))
)
)
];
@ -555,7 +555,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
self.make_statement(
StatementKind::Assign(
Place::Local(beg),
Rvalue::BinaryOp(
box Rvalue::BinaryOp(
BinOp::Add,
Operand::Copy(Place::Local(beg)),
Operand::Constant(self.make_usize(1))
@ -578,7 +578,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let init = self.make_statement(
StatementKind::Assign(
Place::Local(beg),
Rvalue::Use(Operand::Constant(self.make_usize(0)))
box Rvalue::Use(Operand::Constant(self.make_usize(0)))
)
);
self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);
@ -605,7 +605,7 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
let statement = self.make_statement(
StatementKind::Assign(
Place::Local(beg),
Rvalue::BinaryOp(
box Rvalue::BinaryOp(
BinOp::Add,
Operand::Copy(Place::Local(beg)),
Operand::Constant(self.make_usize(1))
@ -715,7 +715,7 @@ fn build_call_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
source_info,
kind: StatementKind::Assign(
Place::Local(ref_rcvr),
Rvalue::Ref(tcx.types.re_erased, borrow_kind, rcvr_l)
box Rvalue::Ref(tcx.types.re_erased, borrow_kind, rcvr_l)
)
});
Operand::Move(Place::Local(ref_rcvr))
@ -851,7 +851,7 @@ pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>,
source_info,
kind: StatementKind::Assign(
Place::Local(RETURN_PLACE),
Rvalue::Aggregate(
box Rvalue::Aggregate(
box AggregateKind::Adt(adt_def, variant_no, substs, None, None),
(1..sig.inputs().len()+1).map(|i| {
Operand::Move(Place::Local(Local::new(i)))

View file

@ -320,12 +320,12 @@ impl MirPass for AddValidation {
for i in (0..block_data.statements.len()).rev() {
match block_data.statements[i].kind {
// When the borrow of this ref expires, we need to recover validation.
StatementKind::Assign(_, Rvalue::Ref(_, _, _)) => {
StatementKind::Assign(_, box Rvalue::Ref(_, _, _)) => {
// Due to a lack of NLL; we can't capture anything directly here.
// Instead, we have to re-match and clone there.
let (dest_place, re, src_place) = match block_data.statements[i].kind {
StatementKind::Assign(ref dest_place,
Rvalue::Ref(re, _, ref src_place)) => {
box Rvalue::Ref(re, _, ref src_place)) => {
(dest_place.clone(), re, src_place.clone())
},
_ => bug!("We already matched this."),
@ -354,17 +354,17 @@ impl MirPass for AddValidation {
block_data.statements.insert(i, release_stmt);
}
// Casts can change what validation does (e.g. unsizing)
StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Copy(_), _)) |
StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Move(_), _))
StatementKind::Assign(_, box Rvalue::Cast(kind, Operand::Copy(_), _)) |
StatementKind::Assign(_, box Rvalue::Cast(kind, Operand::Move(_), _))
if kind != CastKind::Misc =>
{
// Due to a lack of NLL; we can't capture anything directly here.
// Instead, we have to re-match and clone there.
let (dest_place, src_place) = match block_data.statements[i].kind {
StatementKind::Assign(ref dest_place,
Rvalue::Cast(_, Operand::Copy(ref src_place), _)) |
box Rvalue::Cast(_, Operand::Copy(ref src_place), _)) |
StatementKind::Assign(ref dest_place,
Rvalue::Cast(_, Operand::Move(ref src_place), _)) =>
box Rvalue::Cast(_, Operand::Move(ref src_place), _)) =>
{
(dest_place.clone(), src_place.clone())
},

View file

@ -104,7 +104,7 @@ impl MirPass for CopyPropagation {
// That use of the source must be an assignment.
match statement.kind {
StatementKind::Assign(Place::Local(local), Rvalue::Use(ref operand)) if
StatementKind::Assign(Place::Local(local), box Rvalue::Use(ref operand)) if
local == dest_local => {
let maybe_action = match *operand {
Operand::Copy(ref src_place) |
@ -155,11 +155,11 @@ fn eliminate_self_assignments<'tcx>(
match stmt.kind {
StatementKind::Assign(
Place::Local(local),
Rvalue::Use(Operand::Copy(Place::Local(src_local))),
box Rvalue::Use(Operand::Copy(Place::Local(src_local))),
) |
StatementKind::Assign(
Place::Local(local),
Rvalue::Use(Operand::Move(Place::Local(src_local))),
box Rvalue::Use(Operand::Move(Place::Local(src_local))),
) if local == dest_local && dest_local == src_local => {}
_ => {
continue;

View file

@ -26,7 +26,7 @@ impl MirPass for Deaggregator {
bb.expand_statements(|stmt| {
// FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
if let StatementKind::Assign(_, ref rhs) = stmt.kind {
if let Rvalue::Aggregate(ref kind, _) = *rhs {
if let Rvalue::Aggregate(ref kind, _) = **rhs {
// FIXME(#48193) Deaggregate arrays when it's cheaper to do so.
if let AggregateKind::Array(_) = **kind {
return None;
@ -41,8 +41,12 @@ impl MirPass for Deaggregator {
let stmt = stmt.replace_nop();
let source_info = stmt.source_info;
let (mut lhs, kind, operands) = match stmt.kind {
StatementKind::Assign(lhs, Rvalue::Aggregate(kind, operands))
=> (lhs, kind, operands),
StatementKind::Assign(lhs, box rvalue) => {
match rvalue {
Rvalue::Aggregate(kind, operands) => (lhs, kind, operands),
_ => bug!()
}
}
_ => bug!()
};
@ -82,7 +86,7 @@ impl MirPass for Deaggregator {
};
Statement {
source_info,
kind: StatementKind::Assign(lhs_field, Rvalue::Use(op)),
kind: StatementKind::Assign(lhs_field, box Rvalue::Use(op)),
}
}).chain(set_discriminant))
});

View file

@ -478,7 +478,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
assert!(!data.is_cleanup, "DropAndReplace in unwind path not supported");
let assign = Statement {
kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())),
kind: StatementKind::Assign(location.clone(), box Rvalue::Use(value.clone())),
source_info: terminator.source_info
};

View file

@ -188,7 +188,7 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
});
Statement {
source_info,
kind: StatementKind::Assign(state, Rvalue::Use(val)),
kind: StatementKind::Assign(state, box Rvalue::Use(val)),
}
}
}
@ -246,7 +246,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for TransformVisitor<'a, 'tcx> {
data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Place::Local(RETURN_PLACE),
self.make_state(state_idx, v)),
box self.make_state(state_idx, v)),
});
let state = if let Some(resume) = resume { // Yield
let state = 3 + self.suspension_points.len() as u32;

View file

@ -447,7 +447,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
let stmt = Statement {
source_info: callsite.location,
kind: StatementKind::Assign(tmp.clone(), dest)
kind: StatementKind::Assign(tmp.clone(), box dest)
};
caller_mir[callsite.bb]
.statements.push(stmt);
@ -594,7 +594,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
let stmt = Statement {
source_info: callsite.location,
kind: StatementKind::Assign(Place::Local(arg_tmp), arg),
kind: StatementKind::Assign(Place::Local(arg_tmp), box arg),
};
caller_mir[callsite.bb].statements.push(stmt);
arg_tmp

View file

@ -79,11 +79,14 @@ impl Lower128Bit {
let bin_statement = block.statements.pop().unwrap();
let source_info = bin_statement.source_info;
let (place, lhs, mut rhs) = match bin_statement.kind {
StatementKind::Assign(place, Rvalue::BinaryOp(_, lhs, rhs))
| StatementKind::Assign(place, Rvalue::CheckedBinaryOp(_, lhs, rhs)) => {
(place, lhs, rhs)
StatementKind::Assign(place, box rvalue) => {
match rvalue {
Rvalue::BinaryOp(_, lhs, rhs)
| Rvalue::CheckedBinaryOp(_, lhs, rhs) => (place, lhs, rhs),
_ => bug!(),
}
}
_ => bug!("Statement doesn't match pattern any more?"),
_ => bug!()
};
if let Some(local) = cast_local {
@ -95,7 +98,7 @@ impl Lower128Bit {
source_info: source_info,
kind: StatementKind::Assign(
Place::Local(local),
Rvalue::Cast(
box Rvalue::Cast(
CastKind::Misc,
rhs,
rhs_override_ty.unwrap())),
@ -154,13 +157,13 @@ fn lower_to<'a, 'tcx, D>(statement: &Statement<'tcx>, local_decls: &D, tcx: TyCt
where D: HasLocalDecls<'tcx>
{
match statement.kind {
StatementKind::Assign(_, Rvalue::BinaryOp(bin_op, ref lhs, _)) => {
StatementKind::Assign(_, box Rvalue::BinaryOp(bin_op, ref lhs, _)) => {
let ty = lhs.ty(local_decls, tcx);
if let Some(is_signed) = sign_of_128bit(ty) {
return item_for_op(bin_op, is_signed);
}
},
StatementKind::Assign(_, Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => {
StatementKind::Assign(_, box Rvalue::CheckedBinaryOp(bin_op, ref lhs, _)) => {
let ty = lhs.ty(local_decls, tcx);
if let Some(is_signed) = sign_of_128bit(ty) {
return item_for_checked_op(bin_op, is_signed);

View file

@ -182,7 +182,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
span,
scope: OUTERMOST_SOURCE_SCOPE
},
kind: StatementKind::Assign(Place::Local(dest), rvalue)
kind: StatementKind::Assign(Place::Local(dest), box rvalue)
});
}
@ -217,7 +217,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
// First, take the Rvalue or Call out of the source MIR,
// or duplicate it, depending on keep_original.
if loc.statement_index < no_stmts {
let (mut rvalue, source_info) = {
let (rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
let rhs = match statement.kind {
StatementKind::Assign(_, ref mut rhs) => rhs,
@ -230,11 +230,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
(if self.keep_original {
rhs.clone()
} else {
let unit = Rvalue::Aggregate(box AggregateKind::Tuple, vec![]);
let unit = box Rvalue::Aggregate(box AggregateKind::Tuple, vec![]);
mem::replace(rhs, unit)
}, statement.source_info)
};
let mut rvalue = *rvalue;
self.visit_rvalue(&mut rvalue, loc);
self.assign(new_temp, rvalue, source_info.span);
} else {
@ -301,7 +302,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
Candidate::Ref(loc) => {
let ref mut statement = blocks[loc.block].statements[loc.statement_index];
match statement.kind {
StatementKind::Assign(_, Rvalue::Ref(_, _, ref mut place)) => {
StatementKind::Assign(_, box Rvalue::Ref(_, _, ref mut place)) => {
// Find the underlying local for this (necessarily interior) borrow.
// HACK(eddyb) using a recursive function because of mutable borrows.
fn interior_base<'a, 'tcx>(place: &'a mut Place<'tcx>)

View file

@ -388,7 +388,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
match *candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
match self.mir[bb].statements[stmt_idx].kind {
StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => {
StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
promoted_temps.insert(index);
}
_ => {}

View file

@ -15,7 +15,7 @@ pub fn is_min_const_fn(
let mut current = def_id;
loop {
let predicates = tcx.predicates_of(current);
for predicate in &predicates.predicates {
for (predicate, _) in &predicates.predicates {
match predicate {
| Predicate::RegionOutlives(_)
| Predicate::TypeOutlives(_)

View file

@ -60,7 +60,7 @@ impl RemoveNoopLandingPads {
// instructions, but this should all run after borrowck).
}
StatementKind::Assign(Place::Local(_), Rvalue::Use(_)) => {
StatementKind::Assign(Place::Local(_), box Rvalue::Use(_)) => {
// Writing to a local (e.g. a drop flag) does not
// turn a landing pad to a non-nop
}

View file

@ -171,7 +171,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
};
if place == peek_arg_place {
if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue {
if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = **rvalue {
// Okay, our search is over.
match move_data.rev_lookup.find(peeking_at_place) {
LookupResult::Exact(peek_mpi) => {

View file

@ -184,7 +184,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
for candidate in &visitor.candidates {
let statement = &mir[candidate.block].statements[candidate.statement_index];
if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval {
if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = **rval {
let items : Vec<_> = items.iter().map(|item| {
if let Operand::Move(Place::Local(local)) = item {
let local_use = &visitor.locals_use[*local];
@ -268,7 +268,7 @@ impl RestoreSubsliceArrayMoveOut {
let statement = &block.statements[location.statement_index];
if let StatementKind::Assign(
Place::Local(_),
Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{
box Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{
ref base, elem: ProjectionElem::ConstantIndex{
offset, min_length: _, from_end: false}})))) = statement.kind {
return Some((offset, base))

View file

@ -977,7 +977,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
fn assign(&self, lhs: &Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> {
Statement {
source_info: self.source_info,
kind: StatementKind::Assign(lhs.clone(), rhs)
kind: StatementKind::Assign(lhs.clone(), box rhs)
}
}
}

View file

@ -130,7 +130,7 @@ impl<'tcx> MirPatch<'tcx> {
}
pub fn add_assign(&mut self, loc: Location, place: Place<'tcx>, rv: Rvalue<'tcx>) {
self.add_statement(loc, StatementKind::Assign(place, rv));
self.add_statement(loc, StatementKind::Assign(place, box rv));
}
pub fn make_nop(&mut self, loc: Location) {

View file

@ -434,7 +434,7 @@ impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
fn predicates(&mut self) -> &mut Self {
let predicates = self.ev.tcx.predicates_of(self.item_def_id);
for predicate in &predicates.predicates {
for (predicate, _) in &predicates.predicates {
predicate.visit_with(self);
match predicate {
&ty::Predicate::Trait(poly_predicate) => {
@ -781,7 +781,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
if self.check_trait_ref(*principal.skip_binder()) {
return;
}
for poly_predicate in projections {
for (poly_predicate, _) in projections {
let tcx = self.tcx;
if self.check_trait_ref(poly_predicate.skip_binder().projection_ty.trait_ref(tcx)) {
return;
@ -956,7 +956,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
}
}
ty::Opaque(def_id, ..) => {
for predicate in &self.tcx.predicates_of(def_id).predicates {
for (predicate, _) in &self.tcx.predicates_of(def_id).predicates {
let trait_ref = match *predicate {
ty::Predicate::Trait(ref poly_trait_predicate) => {
Some(poly_trait_predicate.skip_binder().trait_ref)
@ -1387,7 +1387,7 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
// for the inferred outlives rules; see
// `src/test/ui/rfc-2093-infer-outlives/privacy.rs`.
let predicates = self.tcx.explicit_predicates_of(self.item_def_id);
for predicate in &predicates.predicates {
for (predicate, _) in &predicates.predicates {
predicate.visit_with(self);
match predicate {
&ty::Predicate::Trait(poly_predicate) => {

View file

@ -42,8 +42,9 @@ use rustc::lint;
use rustc::hir::def::*;
use rustc::hir::def::Namespace::*;
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId};
use rustc::ty;
use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap};
use rustc::session::config::nightly_options;
use rustc::ty;
use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap};
use rustc_metadata::creader::CrateLoader;
@ -1381,6 +1382,9 @@ pub struct Resolver<'a, 'b: 'a> {
/// The current self type if inside an impl (used for better errors).
current_self_type: Option<Ty>,
/// The current self item if inside an ADT (used for better errors).
current_self_item: Option<NodeId>,
/// The idents for the primitive types.
primitive_type_table: PrimitiveTypeTable,
@ -1710,6 +1714,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
current_trait_ref: None,
current_self_type: None,
current_self_item: None,
primitive_type_table: PrimitiveTypeTable::new(),
@ -2186,15 +2191,17 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
}
fn resolve_adt(&mut self, item: &Item, generics: &Generics) {
self.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| {
let item_def_id = this.definitions.local_def_id(item.id);
if this.session.features_untracked().self_in_typedefs {
this.with_self_rib(Def::SelfTy(None, Some(item_def_id)), |this| {
self.with_current_self_item(item, |this| {
this.with_type_parameter_rib(HasTypeParameters(generics, ItemRibKind), |this| {
let item_def_id = this.definitions.local_def_id(item.id);
if this.session.features_untracked().self_in_typedefs {
this.with_self_rib(Def::SelfTy(None, Some(item_def_id)), |this| {
visit::walk_item(this, item);
});
} else {
visit::walk_item(this, item);
});
} else {
visit::walk_item(this, item);
}
}
});
});
}
@ -2435,6 +2442,15 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
result
}
fn with_current_self_item<T, F>(&mut self, self_item: &Item, f: F) -> T
where F: FnOnce(&mut Resolver) -> T
{
let previous_value = replace(&mut self.current_self_item, Some(self_item.id));
let result = f(self);
self.current_self_item = previous_value;
result
}
/// This is called to resolve a trait reference from an `impl` (i.e. `impl Trait for Foo`)
fn with_optional_trait_ref<T, F>(&mut self, opt_trait_ref: Option<&TraitRef>, f: F) -> T
where F: FnOnce(&mut Resolver, Option<DefId>) -> T
@ -3004,6 +3020,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
"traits and impls"
};
err.span_label(span, format!("`Self` is only available in {}", available_in));
if this.current_self_item.is_some() && nightly_options::is_nightly_build() {
err.help("add #![feature(self_in_typedefs)] to the crate attributes \
to enable");
}
return (err, Vec::new());
}
if is_self_value(path, ns) {

View file

@ -12,6 +12,7 @@ crate-type = ["dylib"]
log = "0.4"
rustc = { path = "../librustc" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_codegen_utils = { path = "../librustc_codegen_utils" }
rustc_target = { path = "../librustc_target" }
rustc_typeck = { path = "../librustc_typeck" }
syntax = { path = "../libsyntax" }

View file

@ -25,10 +25,12 @@
use rustc::hir::def::Def as HirDef;
use rustc::hir::def_id::DefId;
use rustc::session::config::Input;
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
use std::path::Path;
use std::env;
use syntax::ast::{self, Attribute, NodeId, PatKind, CRATE_NODE_ID};
use syntax::parse::token;
@ -49,8 +51,8 @@ use json_dumper::{Access, DumpOutput, JsonDumper};
use span_utils::SpanUtils;
use sig;
use rls_data::{CratePreludeData, Def, DefKind, GlobalCrateId, Import, ImportKind, Ref, RefKind,
Relation, RelationKind, SpanData};
use rls_data::{CompilationOptions, CratePreludeData, Def, DefKind, GlobalCrateId, Import,
ImportKind, Ref, RefKind, Relation, RelationKind, SpanData};
macro_rules! down_cast_data {
($id:ident, $kind:ident, $sp:expr) => {
@ -169,6 +171,54 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
self.dumper.crate_prelude(data);
}
pub fn dump_compilation_options(&mut self, input: &Input, crate_name: &str) {
// Apply possible `remap-path-prefix` remapping to the input source file
// (and don't include remapping args anymore)
let (program, arguments) = {
let remap_arg_indices = {
let mut indices = FxHashSet();
// Args are guaranteed to be valid UTF-8 (checked early)
for (i, e) in env::args().enumerate() {
if e.starts_with("--remap-path-prefix=") {
indices.insert(i);
} else if e == "--remap-path-prefix" {
indices.insert(i);
indices.insert(i + 1);
}
}
indices
};
let mut args = env::args()
.enumerate()
.filter(|(i, _)| !remap_arg_indices.contains(i))
.map(|(_, arg)| {
match input {
Input::File(ref path) if path == Path::new(&arg) => {
let mapped = &self.tcx.sess.local_crate_source_file;
mapped
.as_ref()
.unwrap()
.to_string_lossy()
.into()
},
_ => arg,
}
});
(args.next().unwrap(), args.collect())
};
let data = CompilationOptions {
directory: self.tcx.sess.working_dir.0.clone(),
program,
arguments,
output: self.save_ctxt.compilation_output(crate_name),
};
self.dumper.compilation_opts(data);
}
// Return all non-empty prefixes of a path.
// For each prefix, we return the span for the last segment in the prefix and
// a str representation of the entire prefix.

View file

@ -12,9 +12,9 @@ use std::io::Write;
use rustc_serialize::json::as_json;
use rls_data::{self, Analysis, CratePreludeData, Def, DefKind, Import, MacroRef, Ref, RefKind,
Relation, Impl};
use rls_data::config::Config;
use rls_data::{self, Analysis, CompilationOptions, CratePreludeData, Def, DefKind, Impl, Import,
MacroRef, Ref, RefKind, Relation};
use rls_span::{Column, Row};
#[derive(Debug)]
@ -89,6 +89,10 @@ impl<'b, O: DumpOutput + 'b> JsonDumper<O> {
self.result.prelude = Some(data)
}
pub fn compilation_opts(&mut self, data: CompilationOptions) {
self.result.compilation = Some(data);
}
pub fn macro_use(&mut self, data: MacroRef) {
if self.config.pub_only || self.config.reachable_only {
return;

View file

@ -23,6 +23,7 @@ extern crate rustc;
#[macro_use]
extern crate log;
extern crate rustc_data_structures;
extern crate rustc_codegen_utils;
extern crate rustc_serialize;
extern crate rustc_target;
extern crate rustc_typeck;
@ -45,9 +46,10 @@ use rustc::hir::def::Def as HirDef;
use rustc::hir::Node;
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::middle::cstore::ExternCrate;
use rustc::session::config::CrateType;
use rustc::session::config::{CrateType, Input, OutputType};
use rustc::ty::{self, TyCtxt};
use rustc_typeck::hir_ty_to_ty;
use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
use std::cell::Cell;
use std::default::Default;
@ -110,6 +112,24 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
}
}
// Returns path to the compilation output (e.g. libfoo-12345678.rmeta)
pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
let sess = &self.tcx.sess;
// Save-analysis is emitted per whole session, not per each crate type
let crate_type = sess.crate_types.borrow()[0];
let outputs = &*self.tcx.output_filenames(LOCAL_CRATE);
if outputs.outputs.contains_key(&OutputType::Metadata) {
filename_for_metadata(sess, crate_name, outputs)
} else if outputs.outputs.should_codegen() {
out_filename(sess, crate_type, outputs, crate_name)
} else {
// Otherwise it's only a DepInfo, in which case we return early and
// not even reach the analysis stage.
unreachable!()
}
}
// List external crates used by the current crate.
pub fn get_external_crates(&self) -> Vec<ExternalCrateData> {
let mut result = Vec::with_capacity(self.tcx.crates().len());
@ -126,7 +146,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
result.push(ExternalCrateData {
// FIXME: change file_name field to PathBuf in rls-data
// https://github.com/nrc/rls-data/issues/7
file_name: self.span_utils.make_path_string(&lo_loc.file.name),
file_name: self.span_utils.make_filename_string(&lo_loc.file),
num: n.as_u32(),
id: GlobalCrateId {
name: self.tcx.crate_name(n).to_string(),
@ -1015,6 +1035,7 @@ pub trait SaveHandler {
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str,
input: &'l Input,
);
}
@ -1080,12 +1101,14 @@ impl<'a> SaveHandler for DumpHandler<'a> {
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str,
input: &'l Input,
) {
let output = &mut self.output_file(&save_ctxt);
let mut dumper = JsonDumper::new(output, save_ctxt.config.clone());
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
visitor.dump_crate_info(cratename, krate);
visitor.dump_compilation_options(input, cratename);
visit::walk_crate(&mut visitor, krate);
}
}
@ -1101,6 +1124,7 @@ impl<'b> SaveHandler for CallbackHandler<'b> {
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str,
input: &'l Input,
) {
// We're using the JsonDumper here because it has the format of the
// save-analysis results that we will pass to the callback. IOW, we are
@ -1111,6 +1135,7 @@ impl<'b> SaveHandler for CallbackHandler<'b> {
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
visitor.dump_crate_info(cratename, krate);
visitor.dump_compilation_options(input, cratename);
visit::walk_crate(&mut visitor, krate);
}
}
@ -1120,6 +1145,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(
krate: &ast::Crate,
analysis: &'l ty::CrateAnalysis,
cratename: &str,
input: &'l Input,
config: Option<Config>,
mut handler: H,
) {
@ -1137,7 +1163,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(
impl_counter: Cell::new(0),
};
handler.save(save_ctxt, krate, cratename)
handler.save(save_ctxt, krate, cratename, input)
})
}

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