Auto merge of #48476 - Manishearth:rollup, r=Manishearth

Rollup of 12 pull requests

- Successful merges: #47933, #48072, #48083, #48123, #48157, #48219, #48221, #48245, #48429, #48436, #48438, #48472
- Failed merges:
This commit is contained in:
bors 2018-02-24 07:55:34 +00:00
commit 6070d3e47e
35 changed files with 1033 additions and 311 deletions

2
src/Cargo.lock generated
View File

@ -1623,7 +1623,9 @@ dependencies = [
"fmt_macros 0.0.0",
"graphviz 0.0.0",
"jobserver 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"proc_macro 0.0.0",
"rustc_apfloat 0.0.0",
"rustc_back 0.0.0",
"rustc_const_math 0.0.0",

View File

@ -747,8 +747,8 @@ impl<T> LinkedList<T> {
/// Creates an iterator which uses a closure to determine if an element should be removed.
///
/// If the closure returns true, then the element is removed and yielded.
/// If the closure returns false, it will try again, and call the closure on the next element,
/// seeing if it passes the test.
/// If the closure returns false, the element will remain in the list and will not be yielded
/// by the iterator.
///
/// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of
/// whether you choose to keep or remove it.

View File

@ -364,7 +364,7 @@ impl String {
///
/// Given that the `String` is empty, this will not allocate any initial
/// buffer. While that means that this initial operation is very
/// inexpensive, but may cause excessive allocation later, when you add
/// inexpensive, it may cause excessive allocation later when you add
/// data. If you have an idea of how much data the `String` will hold,
/// consider the [`with_capacity`] method to prevent excessive
/// re-allocation.

View File

@ -1966,8 +1966,8 @@ impl<T> Vec<T> {
/// Creates an iterator which uses a closure to determine if an element should be removed.
///
/// If the closure returns true, then the element is removed and yielded.
/// If the closure returns false, it will try again, and call the closure
/// on the next element, seeing if it passes the test.
/// If the closure returns false, the element will remain in the vector and will not be yielded
/// by the iterator.
///
/// Using this method is equivalent to the following code:
///

View File

@ -1366,9 +1366,9 @@ pub trait Iterator {
///
/// In particular, try to have this call `try_fold()` on the internal parts
/// from which this iterator is composed. If multiple calls are needed,
/// the `?` operator be convenient for chaining the accumulator value along,
/// but beware any invariants that need to be upheld before those early
/// returns. This is a `&mut self` method, so iteration needs to be
/// the `?` operator may be convenient for chaining the accumulator value
/// along, but beware any invariants that need to be upheld before those
/// early returns. This is a `&mut self` method, so iteration needs to be
/// resumable after hitting an error here.
///
/// # Examples
@ -1414,6 +1414,42 @@ pub trait Iterator {
Try::from_ok(accum)
}
/// An iterator method that applies a fallible function to each item in the
/// iterator, stopping at the first error and returning that error.
///
/// This can also be thought of as the fallible form of [`for_each()`]
/// or as the stateless version of [`try_fold()`].
///
/// [`for_each()`]: #method.for_each
/// [`try_fold()`]: #method.try_fold
///
/// # Examples
///
/// ```
/// #![feature(iterator_try_fold)]
/// use std::fs::rename;
/// use std::io::{stdout, Write};
/// use std::path::Path;
///
/// let data = ["no_tea.txt", "stale_bread.json", "torrential_rain.png"];
///
/// let res = data.iter().try_for_each(|x| writeln!(stdout(), "{}", x));
/// assert!(res.is_ok());
///
/// let mut it = data.iter().cloned();
/// let res = it.try_for_each(|x| rename(x, Path::new(x).with_extension("old")));
/// assert!(res.is_err());
/// // It short-circuited, so the remaining items are still in the iterator:
/// assert_eq!(it.next(), Some("stale_bread.json"));
/// ```
#[inline]
#[unstable(feature = "iterator_try_fold", issue = "45594")]
fn try_for_each<F, R>(&mut self, mut f: F) -> R where
Self: Sized, F: FnMut(Self::Item) -> R, R: Try<Ok=()>
{
self.try_fold((), move |(), x| f(x))
}
/// An iterator method that applies a function, producing a single, final value.
///
/// `fold()` takes two arguments: an initial value, and a closure with two
@ -1532,7 +1568,7 @@ pub trait Iterator {
fn all<F>(&mut self, mut f: F) -> bool where
Self: Sized, F: FnMut(Self::Item) -> bool
{
self.try_fold((), move |(), x| {
self.try_for_each(move |x| {
if f(x) { LoopState::Continue(()) }
else { LoopState::Break(()) }
}) == LoopState::Continue(())
@ -1581,7 +1617,7 @@ pub trait Iterator {
Self: Sized,
F: FnMut(Self::Item) -> bool
{
self.try_fold((), move |(), x| {
self.try_for_each(move |x| {
if f(x) { LoopState::Break(()) }
else { LoopState::Continue(()) }
}) == LoopState::Break(())
@ -1635,7 +1671,7 @@ pub trait Iterator {
Self: Sized,
P: FnMut(&Self::Item) -> bool,
{
self.try_fold((), move |(), x| {
self.try_for_each(move |x| {
if predicate(&x) { LoopState::Break(x) }
else { LoopState::Continue(()) }
}).break_value()

View File

@ -844,6 +844,12 @@ pub mod __internal {
})
}
pub fn in_sess() -> bool
{
let p = CURRENT_SESS.with(|p| p.get());
!p.0.is_null()
}
pub fn with_sess<F, R>(f: F) -> R
where F: FnOnce((&ParseSess, Mark)) -> R
{

View File

@ -14,7 +14,9 @@ bitflags = "1.0"
fmt_macros = { path = "../libfmt_macros" }
graphviz = { path = "../libgraphviz" }
jobserver = "0.1"
lazy_static = "1.0.0"
log = { version = "0.4", features = ["release_max_level_info", "std"] }
proc_macro = { path = "../libproc_macro" }
rustc_apfloat = { path = "../librustc_apfloat" }
rustc_back = { path = "../librustc_back" }
rustc_const_math = { path = "../librustc_const_math" }

View File

@ -60,6 +60,7 @@
#![feature(never_type)]
#![feature(non_exhaustive)]
#![feature(nonzero)]
#![feature(proc_macro_internals)]
#![feature(quote)]
#![feature(refcell_replace_swap)]
#![feature(rustc_diagnostic_macros)]
@ -81,6 +82,7 @@ extern crate core;
extern crate fmt_macros;
extern crate getopts;
extern crate graphviz;
#[macro_use] extern crate lazy_static;
#[cfg(windows)]
extern crate libc;
extern crate rustc_back;
@ -92,6 +94,7 @@ extern crate rustc_errors as errors;
#[macro_use] extern crate syntax;
extern crate syntax_pos;
extern crate jobserver;
extern crate proc_macro;
extern crate serialize as rustc_serialize; // used by deriving

View File

@ -270,6 +270,19 @@ enum Scope<'a> {
/// we should use for an early-bound region?
next_early_index: u32,
/// Whether or not this binder would serve as the parent
/// binder for abstract types introduced within. For example:
///
/// fn foo<'a>() -> impl for<'b> Trait<Item = impl Trait2<'a>>
///
/// Here, the abstract types we create for the `impl Trait`
/// and `impl Trait2` references will both have the `foo` item
/// as their parent. When we get to `impl Trait2`, we find
/// that it is nested within the `for<>` binder -- this flag
/// allows us to skip that when looking for the parent binder
/// of the resulting abstract type.
abstract_type_parent: bool,
s: ScopeRef<'a>,
},
@ -498,6 +511,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let scope = Scope::Binder {
lifetimes,
next_early_index,
abstract_type_parent: true,
s: ROOT_SCOPE,
};
self.with(scope, |old_scope, this| {
@ -541,6 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
.collect(),
s: self.scope,
next_early_index,
abstract_type_parent: false,
};
self.with(scope, |old_scope, this| {
// a bare fn has no bounds, so everything
@ -614,7 +629,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
ref generics,
ref bounds,
} = *exist_ty;
let mut index = self.next_early_index();
// We want to start our early-bound indices at the end of the parent scope,
// not including any parent `impl Trait`s.
let mut index = self.next_early_index_for_abstract_type();
debug!("visit_ty: index = {}", index);
let mut elision = None;
@ -638,7 +656,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
s: self.scope
};
self.with(scope, |_old_scope, this| {
let scope = Scope::Binder { lifetimes, next_early_index, s: this.scope };
let scope = Scope::Binder {
lifetimes,
next_early_index,
s: this.scope,
abstract_type_parent: false,
};
this.with(scope, |_old_scope, this| {
this.visit_generics(generics);
for bound in bounds {
@ -647,7 +670,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
});
});
} else {
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
let scope = Scope::Binder {
lifetimes,
next_early_index,
s: self.scope,
abstract_type_parent: false,
};
self.with(scope, |_old_scope, this| {
this.visit_generics(generics);
for bound in bounds {
@ -681,7 +709,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
.collect();
let next_early_index = index + generics.ty_params().count() as u32;
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
let scope = Scope::Binder {
lifetimes,
next_early_index,
s: self.scope,
abstract_type_parent: true,
};
self.with(scope, |_old_scope, this| {
this.visit_generics(generics);
for bound in bounds {
@ -721,7 +754,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
.collect();
let next_early_index = index + generics.ty_params().count() as u32;
let scope = Scope::Binder { lifetimes, next_early_index, s: self.scope };
let scope = Scope::Binder {
lifetimes,
next_early_index,
s: self.scope,
abstract_type_parent: true,
};
self.with(scope, |_old_scope, this| {
this.visit_generics(generics);
this.visit_ty(ty);
@ -792,6 +830,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
.collect(),
s: self.scope,
next_early_index,
abstract_type_parent: false,
};
let result = self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &bound_generic_params);
@ -853,6 +892,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
.collect(),
s: self.scope,
next_early_index,
abstract_type_parent: false,
};
self.with(scope, |old_scope, this| {
this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
@ -1046,6 +1086,7 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body) {
ref lifetimes,
s,
next_early_index: _,
abstract_type_parent: _,
} => {
// FIXME (#24278): non-hygienic comparison
if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) {
@ -1303,6 +1344,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
lifetimes,
next_early_index,
s: self.scope,
abstract_type_parent: true,
};
self.with(scope, move |old_scope, this| {
this.check_lifetime_params(old_scope, &generics.params);
@ -1310,25 +1352,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
});
}
/// Returns the next index one would use for an early-bound-region
/// if extending the current scope.
fn next_early_index(&self) -> u32 {
fn next_early_index_helper(&self, only_abstract_type_parent: bool) -> u32 {
let mut scope = self.scope;
loop {
match *scope {
Scope::Root => return 0,
Scope::Binder {
next_early_index, ..
} => return next_early_index,
next_early_index,
abstract_type_parent,
..
} if (!only_abstract_type_parent || abstract_type_parent)
=> return next_early_index,
Scope::Body { s, .. }
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::Elision { s, .. }
| Scope::ObjectLifetimeDefault { s, .. } => scope = s,
}
}
}
/// Returns the next index one would use for an early-bound-region
/// if extending the current scope.
fn next_early_index(&self) -> u32 {
self.next_early_index_helper(true)
}
/// Returns the next index one would use for an `impl Trait` that
/// is being converted into an `abstract type`. This will be the
/// next early index from the enclosing item, for the most
/// part. See the `abstract_type_parent` field for more info.
fn next_early_index_for_abstract_type(&self) -> u32 {
self.next_early_index_helper(false)
}
fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
// Walk up the scope chain, tracking the number of fn scopes
@ -1353,6 +1411,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
ref lifetimes,
s,
next_early_index: _,
abstract_type_parent: _,
} => {
if let Some(&def) = lifetimes.get(&lifetime_ref.name) {
break Some(def.shifted(late_depth));
@ -2102,6 +2161,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
ref lifetimes,
s,
next_early_index: _,
abstract_type_parent: _,
} => {
if let Some(&def) = lifetimes.get(&lifetime.name) {
let node_id = self.tcx.hir.as_local_node_id(def.id().unwrap()).unwrap();

View File

@ -747,7 +747,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
ty::TyTuple(ref tys, _) => tys.iter()
.map(|t| match t.sty {
ty::TypeVariants::TyTuple(ref tys, _) => ArgKind::Tuple(
span,
Some(span),
tys.iter()
.map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
.collect::<Vec<_>>()
@ -815,7 +815,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
/// arguments it expects. This can be supplied to
/// `report_arg_count_mismatch`.
pub fn get_fn_like_arguments(&self, node: hir::map::Node) -> (Span, Vec<ArgKind>) {
match node {
hir::map::NodeExpr(&hir::Expr {
node: hir::ExprClosure(_, ref _decl, id, span, _),
@ -829,7 +833,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
..
} = arg.pat.clone().into_inner() {
ArgKind::Tuple(
span,
Some(span),
args.iter().map(|pat| {
let snippet = self.tcx.sess.codemap()
.span_to_snippet(pat.span).unwrap();
@ -862,7 +866,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
(self.tcx.sess.codemap().def_span(span), decl.inputs.iter()
.map(|arg| match arg.clone().into_inner().node {
hir::TyTup(ref tys) => ArgKind::Tuple(
arg.span,
Some(arg.span),
tys.iter()
.map(|_| ("_".to_owned(), "_".to_owned()))
.collect::<Vec<_>>(),
@ -874,7 +878,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
fn report_arg_count_mismatch(
/// Reports an error when the number of arguments needed by a
/// trait match doesn't match the number that the expression
/// provides.
pub fn report_arg_count_mismatch(
&self,
span: Span,
found_span: Option<Span>,
@ -1385,13 +1392,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}
enum ArgKind {
/// Summarizes information
pub enum ArgKind {
/// An argument of non-tuple type. Parameters are (name, ty)
Arg(String, String),
Tuple(Span, Vec<(String, String)>),
/// An argument of tuple type. For a "found" argument, the span is
/// the locationo in the source of the pattern. For a "expected"
/// argument, it will be None. The vector is a list of (name, ty)
/// strings for the components of the tuple.
Tuple(Option<Span>, Vec<(String, String)>),
}
impl ArgKind {
fn empty() -> ArgKind {
ArgKind::Arg("_".to_owned(), "_".to_owned())
}
/// Creates an `ArgKind` from the expected type of an
/// argument. This has no name (`_`) and no source spans..
pub fn from_expected_ty(t: Ty<'_>) -> ArgKind {
match t.sty {
ty::TyTuple(ref tys, _) => ArgKind::Tuple(
None,
tys.iter()
.map(|ty| ("_".to_owned(), format!("{}", ty.sty)))
.collect::<Vec<_>>()
),
_ => ArgKind::Arg("_".to_owned(), format!("{}", t.sty)),
}
}
}

View File

@ -49,7 +49,7 @@ pub use self::util::SupertraitDefIds;
pub use self::util::transitive_bounds;
mod coherence;
mod error_reporting;
pub mod error_reporting;
mod fulfill;
mod project;
mod object_safety;

View File

@ -16,6 +16,7 @@ use std::ffi::CString;
use std::fmt::Debug;
use std::hash::{Hash, BuildHasher};
use std::iter::repeat;
use std::panic;
use std::path::Path;
use std::time::{Duration, Instant};
@ -23,6 +24,8 @@ use std::sync::mpsc::{Sender};
use syntax_pos::{SpanData};
use ty::maps::{QueryMsg};
use dep_graph::{DepNode};
use proc_macro;
use lazy_static;
// The name of the associated type for `Fn` return types
pub const FN_OUTPUT_NAME: &'static str = "Output";
@ -34,6 +37,24 @@ pub struct ErrorReported;
thread_local!(static TIME_DEPTH: Cell<usize> = Cell::new(0));
lazy_static! {
static ref DEFAULT_HOOK: Box<Fn(&panic::PanicInfo) + Sync + Send + 'static> = {
let hook = panic::take_hook();
panic::set_hook(Box::new(panic_hook));
hook
};
}
fn panic_hook(info: &panic::PanicInfo) {
if !proc_macro::__internal::in_sess() {
(*DEFAULT_HOOK)(info)
}
}
pub fn install_panic_hook() {
lazy_static::initialize(&DEFAULT_HOOK);
}
/// Initialized for -Z profile-queries
thread_local!(static PROFQ_CHAN: RefCell<Option<Sender<ProfileQueriesMsg>>> = RefCell::new(None));

View File

@ -8,19 +8,28 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::marker::PhantomData;
use std::iter::FromIterator;
use indexed_vec::{Idx, IndexVec};
type Word = u128;
const WORD_BITS: usize = 128;
/// A very simple BitVector type.
#[derive(Clone, Debug, PartialEq)]
pub struct BitVector {
data: Vec<u64>,
data: Vec<Word>,
}
impl BitVector {
#[inline]
pub fn new(num_bits: usize) -> BitVector {
let num_words = u64s(num_bits);
BitVector { data: vec![0; num_words] }
let num_words = words(num_bits);
BitVector {
data: vec![0; num_words],
}
}
#[inline]
@ -78,7 +87,7 @@ impl BitVector {
#[inline]
pub fn grow(&mut self, num_bits: usize) {
let num_words = u64s(num_bits);
let num_words = words(num_bits);
if self.data.len() < num_words {
self.data.resize(num_words, 0)
}
@ -96,8 +105,8 @@ impl BitVector {
}
pub struct BitVectorIter<'a> {
iter: ::std::slice::Iter<'a, u64>,
current: u64,
iter: ::std::slice::Iter<'a, Word>,
current: Word,
idx: usize,
}
@ -107,10 +116,10 @@ impl<'a> Iterator for BitVectorIter<'a> {
while self.current == 0 {
self.current = if let Some(&i) = self.iter.next() {
if i == 0 {
self.idx += 64;
self.idx += WORD_BITS;
continue;
} else {
self.idx = u64s(self.idx) * 64;
self.idx = words(self.idx) * WORD_BITS;
i
}
} else {
@ -126,12 +135,15 @@ impl<'a> Iterator for BitVectorIter<'a> {
}
impl FromIterator<bool> for BitVector {
fn from_iter<I>(iter: I) -> BitVector where I: IntoIterator<Item=bool> {
fn from_iter<I>(iter: I) -> BitVector
where
I: IntoIterator<Item = bool>,
{
let iter = iter.into_iter();
let (len, _) = iter.size_hint();
// Make the minimum length for the bitvector 64 bits since that's
// Make the minimum length for the bitvector WORD_BITS bits since that's
// the smallest non-zero size anyway.
let len = if len < 64 { 64 } else { len };
let len = if len < WORD_BITS { WORD_BITS } else { len };
let mut bv = BitVector::new(len);
for (idx, val) in iter.enumerate() {
if idx > len {
@ -152,32 +164,32 @@ impl FromIterator<bool> for BitVector {
#[derive(Clone, Debug)]
pub struct BitMatrix {
columns: usize,
vector: Vec<u64>,
vector: Vec<Word>,
}
impl BitMatrix {
/// Create a new `rows x columns` matrix, initially empty.
pub fn new(rows: usize, columns: usize) -> BitMatrix {
// For every element, we need one bit for every other
// element. Round up to an even number of u64s.
let u64s_per_row = u64s(columns);
// element. Round up to an even number of words.
let words_per_row = words(columns);
BitMatrix {
columns,
vector: vec![0; rows * u64s_per_row],
vector: vec![0; rows * words_per_row],
}
}
/// The range of bits for a given row.
fn range(&self, row: usize) -> (usize, usize) {
let u64s_per_row = u64s(self.columns);
let start = row * u64s_per_row;
(start, start + u64s_per_row)
let words_per_row = words(self.columns);
let start = row * words_per_row;
(start, start + words_per_row)
}
/// Sets the cell at `(row, column)` to true. Put another way, add
/// `column` to the bitset for `row`.
///
/// Returns true if this changed the matrix, and false otherwies.
/// Returns true if this changed the matrix, and false otherwise.
pub fn add(&mut self, row: usize, column: usize) -> bool {
let (start, _) = self.range(row);
let (word, mask) = word_mask(column);
@ -208,12 +220,12 @@ impl BitMatrix {
let mut result = Vec::with_capacity(self.columns);
for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() {
let mut v = self.vector[i] & self.vector[j];
for bit in 0..64 {
for bit in 0..WORD_BITS {
if v == 0 {
break;
}
if v & 0x1 != 0 {
result.push(base * 64 + bit);
result.push(base * WORD_BITS + bit);
}
v >>= 1;
}
@ -254,15 +266,214 @@ impl BitMatrix {
}
}
#[inline]
fn u64s(elements: usize) -> usize {
(elements + 63) / 64
#[derive(Clone, Debug)]
pub struct SparseBitMatrix<R, C>
where
R: Idx,
C: Idx,
{
vector: IndexVec<R, SparseBitSet<C>>,
}
impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
/// Create a new `rows x columns` matrix, initially empty.
pub fn new(rows: R, _columns: C) -> SparseBitMatrix<R, C> {
SparseBitMatrix {
vector: IndexVec::from_elem_n(SparseBitSet::new(), rows.index()),
}
}
/// Sets the cell at `(row, column)` to true. Put another way, insert
/// `column` to the bitset for `row`.
///
/// Returns true if this changed the matrix, and false otherwise.
pub fn add(&mut self, row: R, column: C) -> bool {
self.vector[row].insert(column)
}
/// Do the bits from `row` contain `column`? Put another way, is
/// the matrix cell at `(row, column)` true? Put yet another way,
/// if the matrix represents (transitive) reachability, can
/// `row` reach `column`?
pub fn contains(&self, row: R, column: C) -> bool {
self.vector[row].contains(column)
}
/// Add the bits from row `read` to the bits from row `write`,
/// return true if anything changed.
///
/// This is used when computing transitive reachability because if
/// you have an edge `write -> read`, because in that case
/// `write` can reach everything that `read` can (and
/// potentially more).
pub fn merge(&mut self, read: R, write: R) -> bool {
let mut changed = false;
if read != write {
let (bit_set_read, bit_set_write) = self.vector.pick2_mut(read, write);
for read_val in bit_set_read.iter() {
changed = changed | bit_set_write.insert(read_val);
}
}
changed
}
/// Iterates through all the columns set to true in a given row of
/// the matrix.
pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
self.vector[row].iter()
}
}
#[derive(Clone, Debug)]
pub struct SparseBitSet<I: Idx> {
chunk_bits: BTreeMap<u32, Word>,
_marker: PhantomData<I>,
}
#[derive(Copy, Clone)]
pub struct SparseChunk<I> {
key: u32,
bits: Word,
_marker: PhantomData<I>,
}
impl<I: Idx> SparseChunk<I> {
pub fn one(index: I) -> Self {
let index = index.index();
let key_usize = index / 128;
let key = key_usize as u32;
assert_eq!(key as usize, key_usize);
SparseChunk {
key,
bits: 1 << (index % 128),
_marker: PhantomData,
}
}
pub fn any(&self) -> bool {
self.bits != 0
}
pub fn iter(&self) -> impl Iterator<Item = I> {
let base = self.key as usize * 128;
let mut bits = self.bits;
(0..128)
.map(move |i| {
let current_bits = bits;
bits >>= 1;
(i, current_bits)
})
.take_while(|&(_, bits)| bits != 0)
.filter_map(move |(i, bits)| {
if (bits & 1) != 0 {
Some(I::new(base + i))
} else {
None
}
})
}
}
impl<I: Idx> SparseBitSet<I> {
pub fn new() -> Self {
SparseBitSet {
chunk_bits: BTreeMap::new(),
_marker: PhantomData,
}
}
pub fn capacity(&self) -> usize {
self.chunk_bits.len() * 128
}
pub fn contains_chunk(&self, chunk: SparseChunk<I>) -> SparseChunk<I> {
SparseChunk {
bits: self.chunk_bits
.get(&chunk.key)
.map_or(0, |bits| bits & chunk.bits),
..chunk
}
}
pub fn insert_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
if chunk.bits == 0 {
return chunk;
}
let bits = self.chunk_bits.entry(chunk.key).or_insert(0);
let old_bits = *bits;
let new_bits = old_bits | chunk.bits;
*bits = new_bits;
let changed = new_bits ^ old_bits;
SparseChunk {
bits: changed,
..chunk
}
}
pub fn remove_chunk(&mut self, chunk: SparseChunk<I>) -> SparseChunk<I> {
if chunk.bits == 0 {
return chunk;
}
let changed = match self.chunk_bits.entry(chunk.key) {
Entry::Occupied(mut bits) => {
let old_bits = *bits.get();
let new_bits = old_bits & !chunk.bits;
if new_bits == 0 {
bits.remove();
} else {
bits.insert(new_bits);
}
new_bits ^ old_bits
}
Entry::Vacant(_) => 0,
};
SparseChunk {
bits: changed,
..chunk
}
}
pub fn clear(&mut self) {
self.chunk_bits.clear();
}
pub fn chunks<'a>(&'a self) -> impl Iterator<Item = SparseChunk<I>> + 'a {
self.chunk_bits.iter().map(|(&key, &bits)| SparseChunk {
key,
bits,
_marker: PhantomData,
})
}
pub fn contains(&self, index: I) -> bool {
self.contains_chunk(SparseChunk::one(index)).any()
}
pub fn insert(&mut self, index: I) -> bool {
self.insert_chunk(SparseChunk::one(index)).any()
}
pub fn remove(&mut self, index: I) -> bool {
self.remove_chunk(SparseChunk::one(index)).any()
}
pub fn iter<'a>(&'a self) -> impl Iterator<Item = I> + 'a {
self.chunks().flat_map(|chunk| chunk.iter())
}
}
#[inline]
fn word_mask(index: usize) -> (usize, u64) {
let word = index / 64;
let mask = 1 << (index % 64);
fn words(elements: usize) -> usize {
(elements + WORD_BITS - 1) / WORD_BITS
}
#[inline]
fn word_mask(index: usize) -> (usize, Word) {
let word = index / WORD_BITS;
let mask = 1 << (index % WORD_BITS);
(word, mask)
}
@ -278,11 +489,12 @@ fn bitvec_iter_works() {
bitvec.insert(65);
bitvec.insert(66);
bitvec.insert(99);
assert_eq!(bitvec.iter().collect::<Vec<_>>(),
[1, 10, 19, 62, 63, 64, 65, 66, 99]);
assert_eq!(
bitvec.iter().collect::<Vec<_>>(),
[1, 10, 19, 62, 63, 64, 65, 66, 99]
);
}
#[test]
fn bitvec_iter_works_2() {
let mut bitvec = BitVector::new(319);
@ -314,24 +526,24 @@ fn union_two_vecs() {
#[test]
fn grow() {
let mut vec1 = BitVector::new(65);
for index in 0 .. 65 {
for index in 0..65 {
assert!(vec1.insert(index));
assert!(!vec1.insert(index));
}
vec1.grow(128);
// Check if the bits set before growing are still set
for index in 0 .. 65 {
for index in 0..65 {
assert!(vec1.contains(index));
}
// Check if the new bits are all un-set
for index in 65 .. 128 {
for index in 65..128 {
assert!(!vec1.contains(index));
}
// Check that we can set all new bits without running out of bounds
for index in 65 .. 128 {
for index in 65..128 {
assert!(vec1.insert(index));
assert!(!vec1.insert(index));
}

View File

@ -482,6 +482,21 @@ impl<I: Idx, T> IndexVec<I, T> {
pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
self.raw.get_mut(index.index())
}
/// Return mutable references to two distinct elements, a and b. Panics if a == b.
#[inline]
pub fn pick2_mut(&mut self, a: I, b: I) -> (&mut T, &mut T) {
let (ai, bi) = (a.index(), b.index());
assert!(ai != bi);
if ai < bi {
let (c1, c2) = self.raw.split_at_mut(bi);
(&mut c1[ai], &mut c2[0])
} else {
let (c2, c1) = self.pick2_mut(b, a);
(c1, c2)
}
}
}
impl<I: Idx, T: Clone> IndexVec<I, T> {

View File

@ -24,7 +24,7 @@ use rustc::middle::cstore::CrateStore;
use rustc::middle::privacy::AccessLevels;
use rustc::ty::{self, TyCtxt, Resolutions, AllArenas};
use rustc::traits;
use rustc::util::common::{ErrorReported, time};
use rustc::util::common::{ErrorReported, time, install_panic_hook};
use rustc_allocator as allocator;
use rustc_borrowck as borrowck;
use rustc_incremental;
@ -123,6 +123,8 @@ pub fn compile_input(trans: Box<TransCrate>,
let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
let crate_name =
::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input);
install_panic_hook();
let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
phase_2_configure_and_expand(
sess,

View File

@ -10,7 +10,6 @@
#![allow(non_snake_case)]
use rustc::hir::def_id::DefId;
use rustc::hir::map as hir_map;
use rustc::ty::subst::Substs;
use rustc::ty::{self, AdtKind, Ty, TyCtxt};
@ -26,7 +25,6 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
use syntax::ast;
use syntax::abi::Abi;
use syntax::attr;
use syntax_pos::Span;
use syntax::codemap;
@ -353,13 +351,14 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
cx: &'a LateContext<'a, 'tcx>,
}
enum FfiResult {
enum FfiResult<'tcx> {
FfiSafe,
FfiPhantom,
FfiUnsafe(&'static str),
FfiBadStruct(DefId, &'static str),
FfiBadUnion(DefId, &'static str),
FfiBadEnum(DefId, &'static str),
FfiPhantom(Ty<'tcx>),
FfiUnsafe {
ty: Ty<'tcx>,
reason: &'static str,
help: Option<&'static str>,
},
}
/// Check if this enum can be safely exported based on the
@ -397,23 +396,12 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
false
}
fn is_ffi_safe(ty: attr::IntType) -> bool {
match ty {
attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) |
attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) |
attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) |
attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) |
attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true,
attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => false
}
}
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
/// Check if the given type is "ffi-safe" (has a stable, well-defined
/// representation which can be exported to C code).
fn check_type_for_ffi(&self,
cache: &mut FxHashSet<Ty<'tcx>>,
ty: Ty<'tcx>) -> FfiResult {
ty: Ty<'tcx>) -> FfiResult<'tcx> {
use self::FfiResult::*;
let cx = self.cx.tcx;
@ -429,19 +417,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
match ty.sty {
ty::TyAdt(def, substs) => {
if def.is_phantom_data() {
return FfiPhantom;
return FfiPhantom(ty);
}
match def.adt_kind() {
AdtKind::Struct => {
if !def.repr.c() && !def.repr.transparent() {
return FfiUnsafe("found struct without foreign-function-safe \
representation annotation in foreign module, \
consider adding a #[repr(C)] attribute to the type");
return FfiUnsafe {
ty: ty,
reason: "this struct has unspecified layout",
help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
attribute to this struct"),
};
}
if def.non_enum_variant().fields.is_empty() {
return FfiUnsafe("found zero-size struct in foreign module, consider \
adding a member to this struct");
return FfiUnsafe {
ty: ty,
reason: "this struct has no fields",
help: Some("consider adding a member to this struct"),
};
}
// We can't completely trust repr(C) and repr(transparent) markings;
@ -467,28 +461,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
FfiSafe => {
all_phantom = false;
}
FfiPhantom => {}
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
FfiPhantom(..) => {}
FfiUnsafe { .. } => {
return r;
}
FfiUnsafe(s) => {
return FfiBadStruct(def.did, s);
}
}
}
if all_phantom { FfiPhantom } else { FfiSafe }
if all_phantom { FfiPhantom(ty) } else { FfiSafe }
}
AdtKind::Union => {
if !def.repr.c() {
return FfiUnsafe("found union without foreign-function-safe \
representation annotation in foreign module, \
consider adding a #[repr(C)] attribute to the type");
return FfiUnsafe {
ty: ty,
reason: "this union has unspecified layout",
help: Some("consider adding a #[repr(C)] attribute to this union"),
};
}
if def.non_enum_variant().fields.is_empty() {
return FfiUnsafe("found zero-size union in foreign module, consider \
adding a member to this union");
return FfiUnsafe {
ty: ty,
reason: "this union has no fields",
help: Some("consider adding a field to this union"),
};
}
let mut all_phantom = true;
@ -501,17 +497,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
FfiSafe => {
all_phantom = false;
}
FfiPhantom => {}
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
FfiPhantom(..) => {}
FfiUnsafe { .. } => {
return r;
}
FfiUnsafe(s) => {
return FfiBadUnion(def.did, s);
}
}
}
if all_phantom { FfiPhantom } else { FfiSafe }
if all_phantom { FfiPhantom(ty) } else { FfiSafe }
}
AdtKind::Enum => {
if def.variants.is_empty() {
@ -524,27 +517,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if !def.repr.c() && def.repr.int.is_none() {
// Special-case types like `Option<extern fn()>`.
if !is_repr_nullable_ptr(cx, def, substs) {
return FfiUnsafe("found enum without foreign-function-safe \
representation annotation in foreign \
module, consider adding a #[repr(...)] \
attribute to the type");
return FfiUnsafe {
ty: ty,
reason: "enum has no representation hint",
help: Some("consider adding a #[repr(...)] attribute \
to this enum"),
};
}
}
if let Some(int_ty) = def.repr.int {
if !is_ffi_safe(int_ty) {
// FIXME: This shouldn't be reachable: we should check
// this earlier.
return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
}
// Enum with an explicitly sized discriminant; either
// a C-style enum or a discriminated union.
// The layout of enum variants is implicitly repr(C).
// FIXME: Is that correct?
}
// Check the contained variants.
for variant in &def.variants {
for field in &variant.fields {
@ -554,15 +535,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
let r = self.check_type_for_ffi(cache, arg);
match r {
FfiSafe => {}
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
FfiUnsafe { .. } => {
return r;
}
FfiPhantom => {
return FfiBadEnum(def.did,
"Found phantom data in enum variant");
}
FfiUnsafe(s) => {
return FfiBadEnum(def.did, s);
FfiPhantom(..) => {
return FfiUnsafe {
ty: ty,
reason: "this enum contains a PhantomData field",
help: None,
};
}
}
}
@ -572,45 +553,44 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
}
ty::TyChar => {
FfiUnsafe("found Rust type `char` in foreign module, while \
`u32` or `libc::wchar_t` should be used")
}
ty::TyChar => FfiUnsafe {
ty: ty,
reason: "the `char` type has no C equivalent",
help: Some("consider using `u32` or `libc::wchar_t` instead"),
},
ty::TyInt(ast::IntTy::I128) => {
FfiUnsafe("found Rust type `i128` in foreign module, but \
128-bit integers don't currently have a known \
stable ABI")
}
ty::TyUint(ast::UintTy::U128) => {
FfiUnsafe("found Rust type `u128` in foreign module, but \
128-bit integers don't currently have a known \
stable ABI")
}
ty::TyInt(ast::IntTy::I128) | ty::TyUint(ast::UintTy::U128) => FfiUnsafe {
ty: ty,
reason: "128-bit integers don't currently have a known stable ABI",
help: None,
},
// Primitive types with a stable representation.
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
ty::TySlice(_) => {
FfiUnsafe("found Rust slice type in foreign module, \
consider using a raw pointer instead")
}
ty::TySlice(_) => FfiUnsafe {
ty: ty,
reason: "slices have no C equivalent",
help: Some("consider using a raw pointer instead"),
},
ty::TyDynamic(..) => {
FfiUnsafe("found Rust trait type in foreign module, \
consider using a raw pointer instead")
}
ty::TyDynamic(..) => FfiUnsafe {
ty: ty,
reason: "trait objects have no C equivalent",
help: None,
},
ty::TyStr => {
FfiUnsafe("found Rust type `str` in foreign module; \
consider using a `*const libc::c_char`")
}
ty::TyStr => FfiUnsafe {
ty: ty,
reason: "string slices have no C equivalent",
help: Some("consider using `*const u8` and a length instead"),
},
ty::TyTuple(..) => {
FfiUnsafe("found Rust tuple type in foreign module; \
consider using a struct instead")
}
ty::TyTuple(..) => FfiUnsafe {
ty: ty,
reason: "tuples have unspecified layout",
help: Some("consider using a struct instead"),
},
ty::TyRawPtr(ref m) |
ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
@ -620,9 +600,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::TyFnPtr(sig) => {
match sig.abi() {
Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
return FfiUnsafe("found function pointer with Rust calling convention in \
foreign module; consider using an `extern` function \
pointer")
return FfiUnsafe {
ty: ty,
reason: "this function pointer has Rust-specific calling convention",
help: Some("consider using an `fn \"extern\"(...) -> ...` \
function pointer instead"),
}
}
_ => {}
}
@ -670,40 +653,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
match self.check_type_for_ffi(&mut FxHashSet(), ty) {
FfiResult::FfiSafe => {}
FfiResult::FfiPhantom => {
FfiResult::FfiPhantom(ty) => {
self.cx.span_lint(IMPROPER_CTYPES,
sp,
&format!("found zero-sized type composed only \
of phantom-data in a foreign-function."));
&format!("`extern` block uses type `{}` which is not FFI-safe: \
composed only of PhantomData", ty));
}
FfiResult::FfiUnsafe(s) => {
self.cx.span_lint(IMPROPER_CTYPES, sp, s);
}
FfiResult::FfiBadStruct(_, s) => {
// FIXME: This diagnostic is difficult to read, and doesn't
// point at the relevant field.
self.cx.span_lint(IMPROPER_CTYPES,
sp,
&format!("found non-foreign-function-safe member in struct \
marked #[repr(C)]: {}",
s));
}
FfiResult::FfiBadUnion(_, s) => {
// FIXME: This diagnostic is difficult to read, and doesn't
// point at the relevant field.
self.cx.span_lint(IMPROPER_CTYPES,
sp,
&format!("found non-foreign-function-safe member in union \
marked #[repr(C)]: {}",
s));
}
FfiResult::FfiBadEnum(_, s) => {
// FIXME: This diagnostic is difficult to read, and doesn't
// point at the relevant variant.
self.cx.span_lint(IMPROPER_CTYPES,
sp,
&format!("found non-foreign-function-safe member in enum: {}",
s));
FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
unsafe_ty, reason);
let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
if let Some(s) = help {
diag.help(s);
}
if let ty::TyAdt(def, _) = unsafe_ty.sty {
if let Some(sp) = self.cx.tcx.hir.span_if_local(def.did) {
diag.span_note(sp, "type defined here");
}
}
diag.emit();
}
}
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
use std::rc::Rc;
use rustc_data_structures::bitvec::BitMatrix;
use rustc_data_structures::bitvec::SparseBitMatrix;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
@ -69,9 +69,7 @@ impl RegionValueElements {
/// Iterates over the `RegionElementIndex` for all points in the CFG.
pub(super) fn all_point_indices<'a>(&'a self) -> impl Iterator<Item = RegionElementIndex> + 'a {
(0..self.num_points).map(move |i| {
RegionElementIndex::new(i + self.num_universal_regions)
})
(0..self.num_points).map(move |i| RegionElementIndex::new(i + self.num_universal_regions))
}
/// Iterates over the `RegionElementIndex` for all points in the CFG.
@ -132,7 +130,7 @@ impl RegionValueElements {
}
/// A newtype for the integers that represent one of the possible
/// elements in a region. These are the rows in the `BitMatrix` that
/// elements in a region. These are the rows in the `SparseBitMatrix` that
/// is used to store the values of all regions. They have the following
/// convention:
///
@ -154,7 +152,6 @@ pub(super) enum RegionElement {
UniversalRegion(RegionVid),
}
pub(super) trait ToElementIndex {
fn to_element_index(self, elements: &RegionValueElements) -> RegionElementIndex;
}
@ -184,18 +181,18 @@ impl ToElementIndex for RegionElementIndex {
}
/// Stores the values for a set of regions. These are stored in a
/// compact `BitMatrix` representation, with one row per region
/// compact `SparseBitMatrix` representation, with one row per region
/// variable. The columns consist of either universal regions or
/// points in the CFG.
#[derive(Clone)]
pub(super) struct RegionValues {
elements: Rc<RegionValueElements>,
matrix: BitMatrix,
matrix: SparseBitMatrix<RegionVid, RegionElementIndex>,
/// If cause tracking is enabled, maps from a pair (r, e)
/// consisting of a region `r` that contains some element `e` to
/// the reason that the element is contained. There should be an
/// entry for every bit set to 1 in `BitMatrix`.
/// entry for every bit set to 1 in `SparseBitMatrix`.
causes: Option<CauseMap>,
}
@ -214,7 +211,10 @@ impl RegionValues {
Self {
elements: elements.clone(),
matrix: BitMatrix::new(num_region_variables, elements.num_elements()),
matrix: SparseBitMatrix::new(
RegionVid::new(num_region_variables),
RegionElementIndex::new(elements.num_elements()),
),
causes: if track_causes.0 {
Some(CauseMap::default())
} else {
@ -238,7 +238,7 @@ impl RegionValues {
where
F: FnOnce(&CauseMap) -> Cause,
{
if self.matrix.add(r.index(), i.index()) {
if self.matrix.add(r, i) {
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
if let Some(causes) = &mut self.causes {
@ -289,13 +289,12 @@ impl RegionValues {
constraint_location: Location,
constraint_span: Span,
) -> bool {
// We could optimize this by improving `BitMatrix::merge` so
// We could optimize this by improving `SparseBitMatrix::merge` so
// it does not always merge an entire row. That would
// complicate causal tracking though.
debug!(
"add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
from_region,
to_region
from_region, to_region
);
let mut changed = false;
for elem in self.elements.all_universal_region_indices() {
@ -315,7 +314,7 @@ impl RegionValues {
/// True if the region `r` contains the given element.
pub(super) fn contains<E: ToElementIndex>(&self, r: RegionVid, elem: E) -> bool {
let i = self.elements.index(elem);
self.matrix.contains(r.index(), i.index())
self.matrix.contains(r, i)
}
/// Iterate over the value of the region `r`, yielding up element
@ -325,9 +324,7 @@ impl RegionValues {
&'a self,
r: RegionVid,
) -> impl Iterator<Item = RegionElementIndex> + 'a {
self.matrix
.iter(r.index())
.map(move |i| RegionElementIndex::new(i))
self.matrix.iter(r).map(move |i| i)
}
/// Returns just the universal regions that are contained in a given region's value.
@ -415,9 +412,7 @@ impl RegionValues {
assert_eq!(location1.block, location2.block);
str.push_str(&format!(
"{:?}[{}..={}]",
location1.block,
location1.statement_index,
location2.statement_index
location1.block, location1.statement_index, location2.statement_index
));
}
}

View File

@ -122,8 +122,9 @@ pub(crate) fn run(cgcx: &CodegenContext,
None
}
};
let mut symbol_white_list = cgcx.exported_symbols[&LOCAL_CRATE]
let exported_symbols = cgcx.exported_symbols
.as_ref().expect("needs exported symbols for LTO");
let mut symbol_white_list = exported_symbols[&LOCAL_CRATE]
.iter()
.filter_map(symbol_filter)
.collect::<Vec<CString>>();
@ -156,8 +157,10 @@ pub(crate) fn run(cgcx: &CodegenContext,
}
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
let exported_symbols = cgcx.exported_symbols
.as_ref().expect("needs exported symbols for LTO");
symbol_white_list.extend(
cgcx.exported_symbols[&cnum]
exported_symbols[&cnum]
.iter()
.filter_map(symbol_filter));

View File

@ -333,7 +333,7 @@ pub struct CodegenContext {
pub no_landing_pads: bool,
pub save_temps: bool,
pub fewer_names: bool,
pub exported_symbols: Arc<ExportedSymbols>,
pub exported_symbols: Option<Arc<ExportedSymbols>>,
pub opts: Arc<config::Options>,
pub crate_types: Vec<config::CrateType>,
pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>,
@ -1394,14 +1394,25 @@ fn start_executing_work(tcx: TyCtxt,
allocator_config: Arc<ModuleConfig>)
-> thread::JoinHandle<Result<CompiledModules, ()>> {
let coordinator_send = tcx.tx_to_llvm_workers.clone();
let mut exported_symbols = FxHashMap();
exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
for &cnum in tcx.crates().iter() {
exported_symbols.insert(cnum, tcx.exported_symbols(cnum));
}
let exported_symbols = Arc::new(exported_symbols);
let sess = tcx.sess;
let exported_symbols = match sess.lto() {
Lto::No => None,
Lto::ThinLocal => {
let mut exported_symbols = FxHashMap();
exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
Some(Arc::new(exported_symbols))
}
Lto::Yes | Lto::Fat | Lto::Thin => {
let mut exported_symbols = FxHashMap();
exported_symbols.insert(LOCAL_CRATE, tcx.exported_symbols(LOCAL_CRATE));
for &cnum in tcx.crates().iter() {
exported_symbols.insert(cnum, tcx.exported_symbols(cnum));
}
Some(Arc::new(exported_symbols))
}
};
// First up, convert our jobserver into a helper thread so we can use normal
// mpsc channels to manage our messages and such.
// After we've requested tokens then we'll, when we can,

View File

@ -17,14 +17,24 @@ use rustc::hir::def_id::DefId;
use rustc::infer::{InferOk, InferResult};
use rustc::infer::LateBoundRegionConversionTime;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::error_reporting::ArgKind;
use rustc::ty::{self, ToPolyTraitRef, Ty};
use rustc::ty::subst::Substs;
use rustc::ty::TypeFoldable;
use std::cmp;
use std::iter;
use syntax::abi::Abi;
use syntax::codemap::Span;
use rustc::hir;
/// What signature do we *expect* the closure to have from context?
#[derive(Debug)]
struct ExpectedSig<'tcx> {
/// Span that gave us this expectation, if we know that.
cause_span: Option<Span>,
sig: ty::FnSig<'tcx>,
}
struct ClosureSignatures<'tcx> {
bound_sig: ty::PolyFnSig<'tcx>,
liberated_sig: ty::FnSig<'tcx>,
@ -42,8 +52,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
) -> Ty<'tcx> {
debug!(
"check_expr_closure(expr={:?},expected={:?})",
expr,
expected
expr, expected
);
// It's always helpful for inference if we know the kind of
@ -64,12 +73,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
decl: &'gcx hir::FnDecl,
body: &'gcx hir::Body,
gen: Option<hir::GeneratorMovability>,
expected_sig: Option<ty::FnSig<'tcx>>,
expected_sig: Option<ExpectedSig<'tcx>>,
) -> Ty<'tcx> {
debug!(
"check_closure(opt_kind={:?}, expected_sig={:?})",
opt_kind,
expected_sig
opt_kind, expected_sig
);
let expr_def_id = self.tcx.hir.local_def_id(expr.id);
@ -109,19 +117,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let closure_type = self.tcx.mk_closure(expr_def_id, substs);
if let Some(GeneratorTypes { yield_ty, interior }) = generator_types {
self.demand_eqtype(expr.span,
yield_ty,
substs.generator_yield_ty(expr_def_id, self.tcx));
self.demand_eqtype(expr.span,
liberated_sig.output(),
substs.generator_return_ty(expr_def_id, self.tcx));
self.demand_eqtype(
expr.span,
yield_ty,
substs.generator_yield_ty(expr_def_id, self.tcx),
);
self.demand_eqtype(
expr.span,
liberated_sig.output(),
substs.generator_return_ty(expr_def_id, self.tcx),
);
return self.tcx.mk_generator(expr_def_id, substs, interior);
}
debug!(
"check_closure: expr.id={:?} closure_type={:?}",
expr.id,
closure_type
expr.id, closure_type
);
// Tuple up the arguments and insert the resulting function type into
@ -138,29 +149,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
debug!(
"check_closure: expr_def_id={:?}, sig={:?}, opt_kind={:?}",
expr_def_id,
sig,
opt_kind
expr_def_id, sig, opt_kind
);
let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig);
self.demand_eqtype(expr.span,
sig_fn_ptr_ty,
substs.closure_sig_ty(expr_def_id, self.tcx));
self.demand_eqtype(
expr.span,
sig_fn_ptr_ty,
substs.closure_sig_ty(expr_def_id, self.tcx),
);
if let Some(kind) = opt_kind {
self.demand_eqtype(expr.span,
kind.to_ty(self.tcx),
substs.closure_kind_ty(expr_def_id, self.tcx));
self.demand_eqtype(
expr.span,
kind.to_ty(self.tcx),
substs.closure_kind_ty(expr_def_id, self.tcx),
);
}
closure_type
}
/// Given the expected type, figures out what it can about this closure we
/// are about to type check:
fn deduce_expectations_from_expected_type(
&self,
expected_ty: Ty<'tcx>,
) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
debug!(
"deduce_expectations_from_expected_type(expected_ty={:?})",
expected_ty
@ -172,7 +187,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
.projection_bounds()
.filter_map(|pb| {
let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
self.deduce_sig_from_projection(&pb)
self.deduce_sig_from_projection(None, &pb)
})
.next();
let kind = object_type
@ -181,7 +196,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
(sig, kind)
}
ty::TyInfer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid),
ty::TyFnPtr(sig) => (Some(sig.skip_binder().clone()), Some(ty::ClosureKind::Fn)),
ty::TyFnPtr(sig) => {
let expected_sig = ExpectedSig {
cause_span: None,
sig: sig.skip_binder().clone(),
};
(Some(expected_sig), Some(ty::ClosureKind::Fn))
}
_ => (None, None),
}
}
@ -189,7 +210,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn deduce_expectations_from_obligations(
&self,
expected_vid: ty::TyVid,
) -> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>) {
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let fulfillment_cx = self.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
@ -209,7 +230,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ty::Predicate::Projection(ref proj_predicate) => {
let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
self.self_type_matches_expected_vid(trait_ref, expected_vid)
.and_then(|_| self.deduce_sig_from_projection(proj_predicate))
.and_then(|_| {
self.deduce_sig_from_projection(
Some(obligation.cause.span),
proj_predicate,
)
})
}
_ => None,
}
@ -259,10 +285,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
/// everything we need to know about a closure.
///
/// The `cause_span` should be the span that caused us to
/// have this expected signature, or `None` if we can't readily
/// know that.
fn deduce_sig_from_projection(
&self,
cause_span: Option<Span>,
projection: &ty::PolyProjectionPredicate<'tcx>,
) -> Option<ty::FnSig<'tcx>> {
) -> Option<ExpectedSig<'tcx>> {
let tcx = self.tcx;
debug!("deduce_sig_from_projection({:?})", projection);
@ -294,16 +325,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ret_param_ty
);
let fn_sig = self.tcx.mk_fn_sig(
let sig = self.tcx.mk_fn_sig(
input_tys.cloned(),
ret_param_ty,
false,
hir::Unsafety::Normal,
Abi::Rust,
);
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
debug!("deduce_sig_from_projection: sig {:?}", sig);
Some(fn_sig)
Some(ExpectedSig { cause_span, sig })
}
fn self_type_matches_expected_vid(
@ -314,8 +345,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let self_ty = self.shallow_resolve(trait_ref.self_ty());
debug!(
"self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
trait_ref,
self_ty
trait_ref, self_ty
);
match self_ty.sty {
ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
@ -328,7 +358,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_def_id: DefId,
decl: &hir::FnDecl,
body: &hir::Body,
expected_sig: Option<ty::FnSig<'tcx>>,
expected_sig: Option<ExpectedSig<'tcx>>,
) -> ClosureSignatures<'tcx> {
if let Some(e) = expected_sig {
self.sig_of_closure_with_expectation(expr_def_id, decl, body, e)
@ -404,7 +434,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_def_id: DefId,
decl: &hir::FnDecl,
body: &hir::Body,
expected_sig: ty::FnSig<'tcx>,
expected_sig: ExpectedSig<'tcx>,
) -> ClosureSignatures<'tcx> {
debug!(
"sig_of_closure_with_expectation(expected_sig={:?})",
@ -414,20 +444,24 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Watch out for some surprises and just ignore the
// expectation if things don't see to match up with what we
// expect.
if expected_sig.variadic != decl.variadic {
return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
} else if expected_sig.inputs_and_output.len() != decl.inputs.len() + 1 {
// we could probably handle this case more gracefully
if expected_sig.sig.variadic != decl.variadic {
return self.sig_of_closure_no_expectation(expr_def_id, decl, body);
} else if expected_sig.sig.inputs_and_output.len() != decl.inputs.len() + 1 {
return self.sig_of_closure_with_mismatched_number_of_arguments(
expr_def_id,
decl,
body,
expected_sig,
);
}
// Create a `PolyFnSig`. Note the oddity that late bound
// regions appearing free in `expected_sig` are now bound up
// in this binder we are creating.
assert!(!expected_sig.has_regions_escaping_depth(1));
assert!(!expected_sig.sig.has_regions_escaping_depth(1));
let bound_sig = ty::Binder(self.tcx.mk_fn_sig(
expected_sig.inputs().iter().cloned(),
expected_sig.output(),
expected_sig.sig.inputs().iter().cloned(),
expected_sig.sig.output(),
decl.variadic,
hir::Unsafety::Normal,
Abi::RustCall,
@ -453,6 +487,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
closure_sigs
}
fn sig_of_closure_with_mismatched_number_of_arguments(
&self,
expr_def_id: DefId,
decl: &hir::FnDecl,
body: &hir::Body,
expected_sig: ExpectedSig<'tcx>,
) -> ClosureSignatures<'tcx> {
let expr_map_node = self.tcx.hir.get_if_local(expr_def_id).unwrap();
let expected_args: Vec<_> = expected_sig
.sig
.inputs()
.iter()
.map(|ty| ArgKind::from_expected_ty(ty))
.collect();
let (closure_span, found_args) = self.get_fn_like_arguments(expr_map_node);
let expected_span = expected_sig.cause_span.unwrap_or(closure_span);
self.report_arg_count_mismatch(
expected_span,
Some(closure_span),
expected_args,
found_args,
true,
).emit();
let error_sig = self.error_sig_of_closure(decl);
self.closure_sigs(expr_def_id, body, error_sig)
}
/// Enforce the user's types against the expectation. See
/// `sig_of_closure_with_expectation` for details on the overall
/// strategy.
@ -558,13 +621,46 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
result
}
/// Converts the types that the user supplied, in case that doing
/// so should yield an error, but returns back a signature where
/// all parameters are of type `TyErr`.
fn error_sig_of_closure(&self, decl: &hir::FnDecl) -> ty::PolyFnSig<'tcx> {
let astconv: &AstConv = self;
let supplied_arguments = decl.inputs.iter().map(|a| {
// Convert the types that the user supplied (if any), but ignore them.
astconv.ast_ty_to_ty(a);
self.tcx.types.err
});
match decl.output {
hir::Return(ref output) => {
astconv.ast_ty_to_ty(&output);
}
hir::DefaultReturn(_) => {}
}
let result = ty::Binder(self.tcx.mk_fn_sig(
supplied_arguments,
self.tcx.types.err,
decl.variadic,
hir::Unsafety::Normal,
Abi::RustCall,
));
debug!("supplied_sig_of_closure: result={:?}", result);
result
}
fn closure_sigs(
&self,
expr_def_id: DefId,
body: &hir::Body,
bound_sig: ty::PolyFnSig<'tcx>,
) -> ClosureSignatures<'tcx> {
let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, &bound_sig);
let liberated_sig = self.tcx()
.liberate_late_bound_regions(expr_def_id, &bound_sig);
let liberated_sig = self.inh.normalize_associated_types_in(
body.value.span,
body.value.id,

View File

@ -186,7 +186,7 @@ declare_features! (
(active, rustc_attrs, "1.0.0", Some(29642)),
// Allows the use of non lexical lifetimes; RFC 2094
(active, nll, "1.0.0", Some(44928)),
(active, nll, "1.0.0", Some(43234)),
// Allows the use of #[allow_internal_unstable]. This is an
// attribute on macro_rules! and can't use the attribute handling

View File

@ -2630,8 +2630,7 @@ impl<'a> Parser<'a> {
// A tuple index may not have a suffix
self.expect_no_suffix(sp, "tuple index", suf);
let dot_span = self.prev_span;
hi = self.span;
let idx_span = self.span;
self.bump();
let invalid_msg = "invalid tuple or struct index";
@ -2646,9 +2645,8 @@ impl<'a> Parser<'a> {
n.to_string());
err.emit();
}
let id = respan(dot_span.to(hi), n);
let field = self.mk_tup_field(e, id);
e = self.mk_expr(lo.to(hi), field, ThinVec::new());
let field = self.mk_tup_field(e, respan(idx_span, n));
e = self.mk_expr(lo.to(idx_span), field, ThinVec::new());
}
None => {
let prev_span = self.prev_span;

View File

@ -37,13 +37,13 @@ struct D {
}
extern "C" {
fn foo(x: A); //~ ERROR found struct without foreign-function-safe
fn bar(x: B); //~ ERROR foreign-function-safe
fn foo(x: A); //~ ERROR type `A` which is not FFI-safe
fn bar(x: B); //~ ERROR type `A`
fn baz(x: C);
fn qux(x: A2); //~ ERROR foreign-function-safe
fn quux(x: B2); //~ ERROR foreign-function-safe
fn qux(x: A2); //~ ERROR type `A`
fn quux(x: B2); //~ ERROR type `A`
fn corge(x: C2);
fn fred(x: D); //~ ERROR foreign-function-safe
fn fred(x: D); //~ ERROR type `A`
}
fn main() { }

View File

@ -13,7 +13,7 @@
pub struct Foo;
extern {
pub fn foo(x: (Foo)); //~ ERROR found struct without
pub fn foo(x: (Foo)); //~ ERROR unspecified layout
}
fn main() {

View File

@ -16,11 +16,23 @@ enum U { A }
enum B { C, D }
enum T { E, F, G }
#[repr(C)]
enum ReprC { A, B, C }
#[repr(u8)]
enum U8 { A, B, C }
#[repr(isize)]
enum Isize { A, B, C }
extern {
fn zf(x: Z);
fn uf(x: U); //~ ERROR found enum without foreign-function-safe
fn bf(x: B); //~ ERROR found enum without foreign-function-safe
fn tf(x: T); //~ ERROR found enum without foreign-function-safe
fn uf(x: U); //~ ERROR enum has no representation hint
fn bf(x: B); //~ ERROR enum has no representation hint
fn tf(x: T); //~ ERROR enum has no representation hint
fn reprc(x: ReprC);
fn u8(x: U8);
fn isize(x: Isize);
}
pub fn main() { }

View File

@ -22,7 +22,7 @@ union W {
extern "C" {
static FOREIGN1: U; // OK
static FOREIGN2: W; //~ ERROR found union without foreign-function-safe representation
static FOREIGN2: W; //~ ERROR union has unspecified layout
}
fn main() {}

View File

@ -0,0 +1,30 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-pretty pretty-printing is unhygienic
#![feature(decl_macro)]
#![allow(unused)]
mod foo {
pub macro m($s:tt, $i:tt) {
$s.$i
}
}
mod bar {
struct S(i32);
fn f() {
let s = S(0);
::foo::m!(s, 0);
}
}
fn main() {}

View File

@ -50,6 +50,14 @@ fn closure_hr_elided_return() -> impl Fn(&u32) -> &u32 { |x| x }
fn closure_pass_through_elided_return(x: impl Fn(&u32) -> &u32) -> impl Fn(&u32) -> &u32 { x }
fn closure_pass_through_reference_elided(x: &impl Fn(&u32) -> &u32) -> &impl Fn(&u32) -> &u32 { x }
fn nested_lifetime<'a>(input: &'a str)
-> impl Iterator<Item = impl Iterator<Item = i32> + 'a> + 'a
{
input.lines().map(|line| {
line.split_whitespace().map(|cell| cell.parse().unwrap())
})
}
fn pass_through_elision(x: &u32) -> impl Into<&u32> { x }
fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) -> &u32> { x }

View File

@ -9,6 +9,7 @@
// except according to those terms.
// aux-build:derive-panic.rs
// compile-flags:--error-format human
#[macro_use]
extern crate derive_panic;

View File

@ -1,7 +1,7 @@
error: proc-macro derive panicked
--> $DIR/load-panic.rs:16:10
--> $DIR/load-panic.rs:17:10
|
16 | #[derive(A)]
17 | #[derive(A)]
| ^
|
= help: message: nope!

View File

@ -51,27 +51,27 @@ pub struct TransparentCustomZst(i32, ZeroSize);
pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
extern {
pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without
pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without
pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type
pub fn str_type(p: &str); //~ ERROR: found Rust type
pub fn box_type(p: Box<u32>); //~ ERROR found struct without
pub fn char_type(p: char); //~ ERROR found Rust type
pub fn i128_type(p: i128); //~ ERROR found Rust type
pub fn u128_type(p: u128); //~ ERROR found Rust type
pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type
pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type
pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type
pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct
pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type
pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
pub fn str_type(p: &str); //~ ERROR: uses type `str`
pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
pub fn char_type(p: char); //~ ERROR uses type `char`
pub fn i128_type(p: i128); //~ ERROR uses type `i128`
pub fn u128_type(p: u128); //~ ERROR uses type `u128`
pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
pub fn zero_size_phantom_toplevel()
-> ::std::marker::PhantomData<bool>; //~ ERROR: found zero-sized type
pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without
pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128`
pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str`
pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without
-> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
pub fn good3(fptr: Option<extern fn()>);
pub fn good4(aptr: &[u8; 4 as usize]);

View File

@ -0,0 +1,170 @@
error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
--> $DIR/lint-ctypes.rs:54:28
|
54 | pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
| ^^^^^^^^^^
|
note: lint level defined here
--> $DIR/lint-ctypes.rs:11:9
|
11 | #![deny(improper_ctypes)]
| ^^^^^^^^^^^^^^^
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
note: type defined here
--> $DIR/lint-ctypes.rs:32:1
|
32 | pub struct Foo;
| ^^^^^^^^^^^^^^^
error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
--> $DIR/lint-ctypes.rs:55:28
|
55 | pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
| ^^^^^^^^^^
|
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
note: type defined here
--> $DIR/lint-ctypes.rs:32:1
|
32 | pub struct Foo;
| ^^^^^^^^^^^^^^^
error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent
--> $DIR/lint-ctypes.rs:56:26
|
56 | pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
| ^^^^^^
|
= help: consider using a raw pointer instead
error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
--> $DIR/lint-ctypes.rs:57:24
|
57 | pub fn str_type(p: &str); //~ ERROR: uses type `str`
| ^^^^
|
= help: consider using `*const u8` and a length instead
error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
--> $DIR/lint-ctypes.rs:58:24
|
58 | pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
| ^^^^^^^^
|
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent
--> $DIR/lint-ctypes.rs:59:25
|
59 | pub fn char_type(p: char); //~ ERROR uses type `char`
| ^^^^
|
= help: consider using `u32` or `libc::wchar_t` instead
error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
--> $DIR/lint-ctypes.rs:60:25
|
60 | pub fn i128_type(p: i128); //~ ERROR uses type `i128`
| ^^^^
error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
--> $DIR/lint-ctypes.rs:61:25
|
61 | pub fn u128_type(p: u128); //~ ERROR uses type `u128`
| ^^^^
error: `extern` block uses type `std::clone::Clone` which is not FFI-safe: trait objects have no C equivalent
--> $DIR/lint-ctypes.rs:62:26
|
62 | pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
| ^^^^^^
error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
--> $DIR/lint-ctypes.rs:63:26
|
63 | pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
| ^^^^^^^^^^
|
= help: consider using a struct instead
error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
--> $DIR/lint-ctypes.rs:64:27
|
64 | pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
| ^^^^^^^
|
= help: consider using a struct instead
error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct has no fields
--> $DIR/lint-ctypes.rs:65:25
|
65 | pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
| ^^^^^^^^
|
= help: consider adding a member to this struct
note: type defined here
--> $DIR/lint-ctypes.rs:28:1
|
28 | pub struct ZeroSize;
| ^^^^^^^^^^^^^^^^^^^^
error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData
--> $DIR/lint-ctypes.rs:66:33
|
66 | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
| ^^^^^^^^^^^^^^^^^^^^^^^
error: `extern` block uses type `std::marker::PhantomData<bool>` which is not FFI-safe: composed only of PhantomData
--> $DIR/lint-ctypes.rs:68:12
|
68 | -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
--> $DIR/lint-ctypes.rs:69:23
|
69 | pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
| ^^^^^^
|
= help: consider using an `fn "extern"(...) -> ...` function pointer instead
error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
--> $DIR/lint-ctypes.rs:70:24
|
70 | pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
| ^^^^
|
= help: consider using an `fn "extern"(...) -> ...` function pointer instead
error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
--> $DIR/lint-ctypes.rs:71:28
|
71 | pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
| ^^^^^^^^^^
|
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
--> $DIR/lint-ctypes.rs:72:32
|
72 | pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
| ^^^^^^^^^^^^^^^
error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
--> $DIR/lint-ctypes.rs:73:31
|
73 | pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
| ^^^^^^^^^^^^^^
|
= help: consider using `*const u8` and a length instead
error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
--> $DIR/lint-ctypes.rs:74:30
|
74 | pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
| ^^^^^^^^^^^^^^^^
|
= help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
error: aborting due to 20 previous errors

View File

@ -0,0 +1,29 @@
// Copyright 2016 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.
// Regression test for #47244: in this specific scenario, when the
// expected type indicated 1 argument but the closure takes two, we
// would (early on) create type variables for the type of `b`. If the
// user then attempts to invoke a method on `b`, we would get an error
// saying that the type of `b` must be known, which was not very
// helpful.
use std::collections::HashMap;
fn main() {
let m = HashMap::new();
m.insert( "foo", "bar" );
m.iter().map( |_, b| {
//~^ ERROR closure is expected to take a single 2-tuple
b.to_string()
});
}

View File

@ -0,0 +1,14 @@
error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments
--> $DIR/closure-arg-count-expected-type-issue-47244.rs:24:14
|
24 | m.iter().map( |_, b| {
| ^^^ ------ takes 2 distinct arguments
| |
| expected closure that takes a single 2-tuple as argument
help: change the closure to accept a tuple instead of individual arguments
|
24 | m.iter().map( |(_, b)| {
| ^^^^^^^^
error: aborting due to previous error

@ -1 +1 @@
Subproject commit ce47e529d29f0bf19b31ae80b37b467e42fb97e2
Subproject commit d5e233a720495c52af25d8f6dcc9e55e1193beb9