Skip to content

Commit

Permalink
Auto merge of #64932 - tmandry:rollup-7t8x1nz, r=tmandry
Browse files Browse the repository at this point in the history
Rollup of 9 pull requests

Successful merges:

 - #64377 (Add long error explanation for E0493)
 - #64786 (Use https for curl when building for linux)
 - #64828 (Graphviz debug output for generic dataflow analysis)
 - #64838 (Add long error explanation for E0550)
 - #64891 (Fix `vec![x; n]` with null raw fat pointer zeroing the pointer metadata)
 - #64893 (Zero-initialize `vec![None; n]` for `Option<&T>`, `Option<&mut T>` and `Option<Box<T>>`)
 - #64911 (Fixed a misleading documentation issue #64844)
 - #64921 (Add test for issue-64662)
 - #64923 (Add missing links for mem::needs_drop)

Failed merges:

 - #64918 (Add long error explanation for E0551)

r? @ghost
  • Loading branch information
bors committed Oct 1, 2019
2 parents 22bc9e1 + 913c095 commit 42ec683
Show file tree
Hide file tree
Showing 21 changed files with 719 additions and 36 deletions.
6 changes: 4 additions & 2 deletions src/ci/docker/dist-x86_64-linux/build-curl.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
set -ex
source shared.sh

VERSION=7.51.0
VERSION=7.66.0

curl http://cool.haxx.se/download/curl-$VERSION.tar.bz2 | tar xjf -
curl https://rust-lang-ci-mirrors.s3-us-west-1.amazonaws.com/rustc/curl-$VERSION.tar.xz \
| xz --decompress \
| tar xf -

mkdir curl-build
cd curl-build
Expand Down
48 changes: 48 additions & 0 deletions src/liballoc/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,3 +1281,51 @@ fn test_stable_push_pop() {
v.pop().unwrap();
assert_eq!(*v0, 13);
}

// https://github.com/rust-lang/rust/pull/49496 introduced specialization based on:
//
// ```
// unsafe impl<T: ?Sized> IsZero for *mut T {
// fn is_zero(&self) -> bool {
// (*self).is_null()
// }
// }
// ```
//
// … to call `RawVec::with_capacity_zeroed` for creating `Vec<*mut T>`,
// which is incorrect for fat pointers since `<*mut T>::is_null` only looks at the data component.
// That is, a fat pointer can be “null” without being made entirely of zero bits.
#[test]
fn vec_macro_repeating_null_raw_fat_pointer() {
let raw_dyn = &mut (|| ()) as &mut dyn Fn() as *mut dyn Fn();
let vtable = dbg!(ptr_metadata(raw_dyn));
let null_raw_dyn = ptr_from_raw_parts(std::ptr::null_mut(), vtable);
assert!(null_raw_dyn.is_null());

let vec = vec![null_raw_dyn; 1];
dbg!(ptr_metadata(vec[0]));
assert!(vec[0] == null_raw_dyn);

// Polyfill for https://github.com/rust-lang/rfcs/pull/2580

fn ptr_metadata(ptr: *mut dyn Fn()) -> *mut () {
unsafe {
std::mem::transmute::<*mut dyn Fn(), DynRepr>(ptr).vtable
}
}

fn ptr_from_raw_parts(data: *mut (), vtable: *mut()) -> *mut dyn Fn() {
unsafe {
std::mem::transmute::<DynRepr, *mut dyn Fn()>(DynRepr {
data,
vtable
})
}
}

#[repr(C)]
struct DynRepr {
data: *mut (),
vtable: *mut (),
}
}
29 changes: 27 additions & 2 deletions src/liballoc/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1734,20 +1734,45 @@ impl_is_zero!(char, |x| x == '\0');
impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
impl_is_zero!(f64, |x: f64| x.to_bits() == 0);

unsafe impl<T: ?Sized> IsZero for *const T {
unsafe impl<T> IsZero for *const T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}

unsafe impl<T: ?Sized> IsZero for *mut T {
unsafe impl<T> IsZero for *mut T {
#[inline]
fn is_zero(&self) -> bool {
(*self).is_null()
}
}

// `Option<&T>`, `Option<&mut T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
// For fat pointers, the bytes that would be the pointer metadata in the `Some` variant
// are padding in the `None` variant, so ignoring them and zero-initializing instead is ok.

unsafe impl<T: ?Sized> IsZero for Option<&T> {
#[inline]
fn is_zero(&self) -> bool {
self.is_none()
}
}

unsafe impl<T: ?Sized> IsZero for Option<&mut T> {
#[inline]
fn is_zero(&self) -> bool {
self.is_none()
}
}

unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
#[inline]
fn is_zero(&self) -> bool {
self.is_none()
}
}


////////////////////////////////////////////////////////////////////////////////
// Common trait implementations for Vec
Expand Down
10 changes: 6 additions & 4 deletions src/libcore/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,17 @@ pub fn align_of_val<T: ?Sized>(val: &T) -> usize {
/// make a difference in release builds (where a loop that has no side-effects
/// is easily detected and eliminated), but is often a big win for debug builds.
///
/// Note that `ptr::drop_in_place` already performs this check, so if your workload
/// can be reduced to some small number of drop_in_place calls, using this is
/// unnecessary. In particular note that you can drop_in_place a slice, and that
/// Note that [`drop_in_place`] already performs this check, so if your workload
/// can be reduced to some small number of [`drop_in_place`] calls, using this is
/// unnecessary. In particular note that you can [`drop_in_place`] a slice, and that
/// will do a single needs_drop check for all the values.
///
/// Types like Vec therefore just `drop_in_place(&mut self[..])` without using
/// needs_drop explicitly. Types like `HashMap`, on the other hand, have to drop
/// `needs_drop` explicitly. Types like [`HashMap`], on the other hand, have to drop
/// values one at a time and should use this API.
///
/// [`drop_in_place`]: ../ptr/fn.drop_in_place.html
/// [`HashMap`]: ../../std/collections/struct.HashMap.html
///
/// # Examples
///
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
//! # Options and pointers ("nullable" pointers)
//!
//! Rust's pointer types must always point to a valid location; there are
//! no "null" pointers. Instead, Rust has *optional* pointers, like
//! no "null" references. Instead, Rust has *optional* pointers, like
//! the optional owned box, [`Option`]`<`[`Box<T>`]`>`.
//!
//! The following example uses [`Option`] to create an optional box of
Expand Down
129 changes: 115 additions & 14 deletions src/librustc_mir/dataflow/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,24 @@
//! [gk]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
//! [#64566]: https://github.com/rust-lang/rust/pull/64566

use std::borrow::Borrow;
use std::cmp::Ordering;
use std::ops;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::{fs, io, ops};

use rustc::hir::def_id::DefId;
use rustc::mir::{self, traversal, BasicBlock, Location};
use rustc::ty::{self, TyCtxt};
use rustc_data_structures::work_queue::WorkQueue;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::work_queue::WorkQueue;
use syntax::symbol::sym;

use crate::dataflow::BottomValue;

mod graphviz;

/// A specific kind of dataflow analysis.
///
/// To run a dataflow analysis, one must set the initial state of the `START_BLOCK` via
Expand Down Expand Up @@ -62,6 +70,13 @@ pub trait Analysis<'tcx>: BottomValue {
/// and try to keep it short.
const NAME: &'static str;

/// How each element of your dataflow state will be displayed during debugging.
///
/// By default, this is the `fmt::Debug` representation of `Self::Idx`.
fn pretty_print_idx(&self, w: &mut impl io::Write, idx: Self::Idx) -> io::Result<()> {
write!(w, "{:?}", idx)
}

/// The size of each bitvector allocated for each block.
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize;

Expand All @@ -77,7 +92,7 @@ pub trait Analysis<'tcx>: BottomValue {
location: Location,
);

/// Updates the current dataflow state with the effect of evaluating a statement.
/// Updates the current dataflow state with the effect of evaluating a terminator.
///
/// Note that the effect of a successful return from a `Call` terminator should **not** be
/// acounted for in this function. That should go in `apply_call_return_effect`. For example,
Expand Down Expand Up @@ -180,17 +195,20 @@ impl CursorPosition {
}
}

type ResultsRefCursor<'a, 'mir, 'tcx, A> =
ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;

/// Inspect the results of dataflow analysis.
///
/// This cursor has linear performance when visiting statements in a block in order. Visiting
/// statements within a block in reverse order is `O(n^2)`, where `n` is the number of statements
/// in that block.
pub struct ResultsCursor<'mir, 'tcx, A>
pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
where
A: Analysis<'tcx>,
{
body: &'mir mir::Body<'tcx>,
results: Results<'tcx, A>,
results: R,
state: BitSet<A::Idx>,

pos: CursorPosition,
Expand All @@ -202,24 +220,29 @@ where
is_call_return_effect_applied: bool,
}

impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A>
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx>,
R: Borrow<Results<'tcx, A>>,
{
/// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self {
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
ResultsCursor {
body,
pos: CursorPosition::AtBlockStart(mir::START_BLOCK),
is_call_return_effect_applied: false,
state: results.entry_sets[mir::START_BLOCK].clone(),
state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
results,
}
}

pub fn analysis(&self) -> &A {
&self.results.borrow().analysis
}

/// Resets the cursor to the start of the given `block`.
pub fn seek_to_block_start(&mut self, block: BasicBlock) {
self.state.overwrite(&self.results.entry_sets[block]);
self.state.overwrite(&self.results.borrow().entry_sets[block]);
self.pos = CursorPosition::AtBlockStart(block);
self.is_call_return_effect_applied = false;
}
Expand Down Expand Up @@ -275,7 +298,7 @@ where
} = &term.kind {
if !self.is_call_return_effect_applied {
self.is_call_return_effect_applied = true;
self.results.analysis.apply_call_return_effect(
self.results.borrow().analysis.apply_call_return_effect(
&mut self.state,
target.block,
func,
Expand Down Expand Up @@ -316,7 +339,7 @@ where
};

let block_data = &self.body.basic_blocks()[target_block];
self.results.analysis.apply_partial_block_effect(
self.results.borrow().analysis.apply_partial_block_effect(
&mut self.state,
target_block,
block_data,
Expand Down Expand Up @@ -349,7 +372,9 @@ where
{
analysis: A,
bits_per_block: usize,
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
def_id: DefId,
dead_unwinds: &'a BitSet<BasicBlock>,
entry_sets: IndexVec<BasicBlock, BitSet<A::Idx>>,
}
Expand All @@ -359,7 +384,9 @@ where
A: Analysis<'tcx>,
{
pub fn new(
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
def_id: DefId,
dead_unwinds: &'a BitSet<BasicBlock>,
analysis: A,
) -> Self {
Expand All @@ -377,7 +404,9 @@ where
Engine {
analysis,
bits_per_block,
tcx,
body,
def_id,
dead_unwinds,
entry_sets,
}
Expand Down Expand Up @@ -413,10 +442,26 @@ where
);
}

Results {
analysis: self.analysis,
entry_sets: self.entry_sets,
let Engine {
tcx,
body,
def_id,
analysis,
entry_sets,
..
} = self;

let results = Results { analysis, entry_sets };

let attrs = tcx.get_attrs(def_id);
if let Some(path) = get_dataflow_graphviz_output_path(tcx, attrs, A::NAME) {
let result = write_dataflow_graphviz_results(body, def_id, &path, &results);
if let Err(e) = result {
warn!("Failed to write dataflow results to {}: {}", path.display(), e);
}
}

results
}

fn propagate_bits_into_graph_successors_of(
Expand Down Expand Up @@ -510,3 +555,59 @@ where
}
}
}

/// Looks for attributes like `#[rustc_mir(borrowck_graphviz_postflow="./path/to/suffix.dot")]` and
/// extracts the path with the given analysis name prepended to the suffix.
///
/// Returns `None` if no such attribute exists.
fn get_dataflow_graphviz_output_path(
tcx: TyCtxt<'tcx>,
attrs: ty::Attributes<'tcx>,
analysis: &str,
) -> Option<PathBuf> {
let mut rustc_mir_attrs = attrs
.into_iter()
.filter(|attr| attr.check_name(sym::rustc_mir))
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));

let borrowck_graphviz_postflow = rustc_mir_attrs
.find(|attr| attr.check_name(sym::borrowck_graphviz_postflow))?;

let path_and_suffix = match borrowck_graphviz_postflow.value_str() {
Some(p) => p,
None => {
tcx.sess.span_err(
borrowck_graphviz_postflow.span(),
"borrowck_graphviz_postflow requires a path",
);

return None;
}
};

// Change "path/suffix.dot" to "path/analysis_name_suffix.dot"
let mut ret = PathBuf::from(path_and_suffix.to_string());
let suffix = ret.file_name().unwrap();

let mut file_name: OsString = analysis.into();
file_name.push("_");
file_name.push(suffix);
ret.set_file_name(file_name);

Some(ret)
}

fn write_dataflow_graphviz_results<A: Analysis<'tcx>>(
body: &mir::Body<'tcx>,
def_id: DefId,
path: &Path,
results: &Results<'tcx, A>
) -> io::Result<()> {
debug!("printing dataflow results for {:?} to {}", def_id, path.display());

let mut buf = Vec::new();
let graphviz = graphviz::Formatter::new(body, def_id, results);

dot::render(&graphviz, &mut buf)?;
fs::write(path, buf)
}
Loading

0 comments on commit 42ec683

Please sign in to comment.