Auto merge of #64932 - tmandry:rollup-7t8x1nz, r=tmandry
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
This commit is contained in:
commit
42ec6831b0
@ -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
|
||||
|
@ -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 (),
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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;
|
||||
}
|
||||
@ -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,
|
||||
@ -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,
|
||||
@ -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>>,
|
||||
}
|
||||
@ -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 {
|
||||
@ -377,7 +404,9 @@ where
|
||||
Engine {
|
||||
analysis,
|
||||
bits_per_block,
|
||||
tcx,
|
||||
body,
|
||||
def_id,
|
||||
dead_unwinds,
|
||||
entry_sets,
|
||||
}
|
||||
@ -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(
|
||||
@ -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)
|
||||
}
|
||||
|
412
src/librustc_mir/dataflow/generic/graphviz.rs
Normal file
412
src/librustc_mir/dataflow/generic/graphviz.rs
Normal file
@ -0,0 +1,412 @@
|
||||
use std::cell::RefCell;
|
||||
use std::io::{self, Write};
|
||||
use std::{ops, str};
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::{self, BasicBlock, Body, Location};
|
||||
use rustc_index::bit_set::{BitSet, HybridBitSet};
|
||||
use rustc_index::vec::Idx;
|
||||
|
||||
use crate::util::graphviz_safe_def_name;
|
||||
use super::{Analysis, Results, ResultsRefCursor};
|
||||
|
||||
pub struct Formatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
body: &'a Body<'tcx>,
|
||||
def_id: DefId,
|
||||
|
||||
// This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
|
||||
block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
|
||||
}
|
||||
|
||||
impl<A> Formatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
pub fn new(
|
||||
body: &'a Body<'tcx>,
|
||||
def_id: DefId,
|
||||
results: &'a Results<'tcx, A>,
|
||||
) -> Self {
|
||||
let block_formatter = BlockFormatter {
|
||||
bg: Background::Light,
|
||||
prev_state: BitSet::new_empty(results.analysis.bits_per_block(body)),
|
||||
results: ResultsRefCursor::new(body, results),
|
||||
};
|
||||
|
||||
Formatter {
|
||||
body,
|
||||
def_id,
|
||||
block_formatter: RefCell::new(block_formatter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A pair of a basic block and an index into that basic blocks `successors`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct CfgEdge {
|
||||
source: BasicBlock,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
|
||||
body[bb]
|
||||
.terminator()
|
||||
.successors()
|
||||
.enumerate()
|
||||
.map(|(index, _)| CfgEdge { source: bb, index })
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl<A> dot::Labeller<'_> for Formatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
type Node = BasicBlock;
|
||||
type Edge = CfgEdge;
|
||||
|
||||
fn graph_id(&self) -> dot::Id<'_> {
|
||||
let name = graphviz_safe_def_name(self.def_id);
|
||||
dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
|
||||
}
|
||||
|
||||
fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
|
||||
dot::Id::new(format!("bb_{}", n.index())).unwrap()
|
||||
}
|
||||
|
||||
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
|
||||
let mut label = Vec::new();
|
||||
self.block_formatter
|
||||
.borrow_mut()
|
||||
.write_node_label(&mut label, self.body, *block)
|
||||
.unwrap();
|
||||
dot::LabelText::html(String::from_utf8(label).unwrap())
|
||||
}
|
||||
|
||||
fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
|
||||
Some(dot::LabelText::label("none"))
|
||||
}
|
||||
|
||||
fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
|
||||
let label = &self.body
|
||||
[e.source]
|
||||
.terminator()
|
||||
.kind
|
||||
.fmt_successor_labels()
|
||||
[e.index];
|
||||
dot::LabelText::label(label.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
type Node = BasicBlock;
|
||||
type Edge = CfgEdge;
|
||||
|
||||
fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
|
||||
self.body
|
||||
.basic_blocks()
|
||||
.indices()
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn edges(&self) -> dot::Edges<'_, Self::Edge> {
|
||||
self.body
|
||||
.basic_blocks()
|
||||
.indices()
|
||||
.flat_map(|bb| outgoing_edges(self.body, bb))
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn source(&self, edge: &Self::Edge) -> Self::Node {
|
||||
edge.source
|
||||
}
|
||||
|
||||
fn target(&self, edge: &Self::Edge) -> Self::Node {
|
||||
self.body
|
||||
[edge.source]
|
||||
.terminator()
|
||||
.successors()
|
||||
.nth(edge.index)
|
||||
.copied()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct BlockFormatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
prev_state: BitSet<A::Idx>,
|
||||
results: ResultsRefCursor<'a, 'a, 'tcx, A>,
|
||||
bg: Background,
|
||||
}
|
||||
|
||||
impl<A> BlockFormatter<'a, 'tcx, A>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
fn toggle_background(&mut self) -> Background {
|
||||
let bg = self.bg;
|
||||
self.bg = !bg;
|
||||
bg
|
||||
}
|
||||
|
||||
fn write_node_label(
|
||||
&mut self,
|
||||
w: &mut impl io::Write,
|
||||
body: &'a Body<'tcx>,
|
||||
block: BasicBlock,
|
||||
) -> io::Result<()> {
|
||||
// Sample output:
|
||||
// +-+--------------------------------------------------+
|
||||
// A | bb4 |
|
||||
// +-+----------------------------------+---------------+
|
||||
// B | MIR | STATE |
|
||||
// +-+----------------------------------+---------------+
|
||||
// C | | (on entry) | {_0,_2,_3} |
|
||||
// +-+----------------------------------+---------------+
|
||||
// D |0| 0: StorageLive(_7) | |
|
||||
// +-+----------------------------------+---------------+
|
||||
// |1| 1: StorageLive(_8) | |
|
||||
// +-+----------------------------------+---------------+
|
||||
// |2| 2: _8 = &mut _1 | +_8 |
|
||||
// +-+----------------------------------+---------------+
|
||||
// E |T| _7 = const Foo::twiddle(move _8) | -_8 |
|
||||
// +-+----------------------------------+---------------+
|
||||
// F | | (on unwind) | {_0,_2,_3,_7} |
|
||||
// +-+----------------------------------+---------------+
|
||||
// | | (on successful return) | +_7 |
|
||||
// +-+----------------------------------+---------------+
|
||||
|
||||
write!(
|
||||
w,
|
||||
r#"<table border="1" cellborder="1" cellspacing="0" cellpadding="3" sides="rb">"#,
|
||||
)?;
|
||||
|
||||
// A: Block info
|
||||
write!(
|
||||
w,
|
||||
r#"<tr>
|
||||
<td colspan="{num_headers}" sides="tl">bb{block_id}</td>
|
||||
</tr>"#,
|
||||
num_headers = 3,
|
||||
block_id = block.index(),
|
||||
)?;
|
||||
|
||||
// B: Column headings
|
||||
write!(
|
||||
w,
|
||||
r#"<tr>
|
||||
<td colspan="2" {fmt}>MIR</td>
|
||||
<td {fmt}>STATE</td>
|
||||
</tr>"#,
|
||||
fmt = r##"bgcolor="#a0a0a0" sides="tl""##,
|
||||
)?;
|
||||
|
||||
// C: Entry state
|
||||
self.results.seek_to_block_start(block);
|
||||
self.write_row_with_curr_state(w, "", "(on entry)")?;
|
||||
self.prev_state.overwrite(self.results.get());
|
||||
|
||||
// D: Statement transfer functions
|
||||
for (i, statement) in body[block].statements.iter().enumerate() {
|
||||
let location = Location { block, statement_index: i };
|
||||
|
||||
let mir_col = format!("{:?}", statement);
|
||||
let i_col = i.to_string();
|
||||
|
||||
self.results.seek_after(location);
|
||||
self.write_row_with_curr_diff(w, &i_col, &mir_col)?;
|
||||
self.prev_state.overwrite(self.results.get());
|
||||
}
|
||||
|
||||
// E: Terminator transfer function
|
||||
let terminator = body[block].terminator();
|
||||
let location = body.terminator_loc(block);
|
||||
|
||||
let mut mir_col = String::new();
|
||||
terminator.kind.fmt_head(&mut mir_col).unwrap();
|
||||
|
||||
self.results.seek_after(location);
|
||||
self.write_row_with_curr_diff(w, "T", &mir_col)?;
|
||||
self.prev_state.overwrite(self.results.get());
|
||||
|
||||
// F: Exit state
|
||||
if let mir::TerminatorKind::Call { destination: Some(_), .. } = &terminator.kind {
|
||||
self.write_row_with_curr_state(w, "", "(on unwind)")?;
|
||||
|
||||
self.results.seek_after_assume_call_returns(location);
|
||||
self.write_row_with_curr_diff(w, "", "(on successful return)")?;
|
||||
} else {
|
||||
self.write_row_with_curr_state(w, "", "(on exit)")?;
|
||||
}
|
||||
|
||||
write!(w, "</table>")
|
||||
}
|
||||
|
||||
fn write_row_with_curr_state(
|
||||
&mut self,
|
||||
w: &mut impl io::Write,
|
||||
i: &str,
|
||||
mir: &str,
|
||||
) -> io::Result<()> {
|
||||
let bg = self.toggle_background();
|
||||
|
||||
let mut out = Vec::new();
|
||||
write!(&mut out, "{{")?;
|
||||
pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
|
||||
write!(&mut out, "}}")?;
|
||||
|
||||
write!(
|
||||
w,
|
||||
r#"<tr>
|
||||
<td {fmt} align="right">{i}</td>
|
||||
<td {fmt} align="left">{mir}</td>
|
||||
<td {fmt} align="left">{state}</td>
|
||||
</tr>"#,
|
||||
fmt = &["sides=\"tl\"", bg.attr()].join(" "),
|
||||
i = i,
|
||||
mir = dot::escape_html(mir),
|
||||
state = dot::escape_html(str::from_utf8(&out).unwrap()),
|
||||
)
|
||||
}
|
||||
|
||||
fn write_row_with_curr_diff(
|
||||
&mut self,
|
||||
w: &mut impl io::Write,
|
||||
i: &str,
|
||||
mir: &str,
|
||||
) -> io::Result<()> {
|
||||
let bg = self.toggle_background();
|
||||
let analysis = self.results.analysis();
|
||||
|
||||
let diff = BitSetDiff::compute(&self.prev_state, self.results.get());
|
||||
|
||||
let mut set = Vec::new();
|
||||
pretty_print_state_elems(&mut set, analysis, diff.set.iter())?;
|
||||
|
||||
let mut clear = Vec::new();
|
||||
pretty_print_state_elems(&mut clear, analysis, diff.clear.iter())?;
|
||||
|
||||
write!(
|
||||
w,
|
||||
r#"<tr>
|
||||
<td {fmt} align="right">{i}</td>
|
||||
<td {fmt} align="left">{mir}</td>
|
||||
<td {fmt} align="left">"#,
|
||||
i = i,
|
||||
fmt = &["sides=\"tl\"", bg.attr()].join(" "),
|
||||
mir = dot::escape_html(mir),
|
||||
)?;
|
||||
|
||||
if !set.is_empty() {
|
||||
write!(
|
||||
w,
|
||||
r#"<font color="darkgreen">+{}</font>"#,
|
||||
dot::escape_html(str::from_utf8(&set).unwrap()),
|
||||
)?;
|
||||
}
|
||||
|
||||
if !set.is_empty() && !clear.is_empty() {
|
||||
write!(w, " ")?;
|
||||
}
|
||||
|
||||
if !clear.is_empty() {
|
||||
write!(
|
||||
w,
|
||||
r#"<font color="red">-{}</font>"#,
|
||||
dot::escape_html(str::from_utf8(&clear).unwrap()),
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(w, "</td></tr>")
|
||||
}
|
||||
}
|
||||
|
||||
/// The operations required to transform one `BitSet` into another.
|
||||
struct BitSetDiff<T: Idx> {
|
||||
set: HybridBitSet<T>,
|
||||
clear: HybridBitSet<T>,
|
||||
}
|
||||
|
||||
impl<T: Idx> BitSetDiff<T> {
|
||||
fn compute(from: &BitSet<T>, to: &BitSet<T>) -> Self {
|
||||
assert_eq!(from.domain_size(), to.domain_size());
|
||||
let len = from.domain_size();
|
||||
|
||||
let mut set = HybridBitSet::new_empty(len);
|
||||
let mut clear = HybridBitSet::new_empty(len);
|
||||
|
||||
// FIXME: This could be made faster if `BitSet::xor` were implemented.
|
||||
for i in (0..len).map(|i| T::new(i)) {
|
||||
match (from.contains(i), to.contains(i)) {
|
||||
(false, true) => set.insert(i),
|
||||
(true, false) => clear.insert(i),
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
|
||||
BitSetDiff {
|
||||
set,
|
||||
clear,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
|
||||
/// list.
|
||||
fn pretty_print_state_elems<A>(
|
||||
w: &mut impl io::Write,
|
||||
analysis: &A,
|
||||
elems: impl Iterator<Item = A::Idx>,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
A: Analysis<'tcx>,
|
||||
{
|
||||
let mut first = true;
|
||||
for idx in elems {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(w, ",")?;
|
||||
}
|
||||
|
||||
analysis.pretty_print_idx(w, idx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The background color used for zebra-striping the table.
|
||||
#[derive(Clone, Copy)]
|
||||
enum Background {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
impl Background {
|
||||
fn attr(self) -> &'static str {
|
||||
match self {
|
||||
Self::Dark => "bgcolor=\"#f0f0f0\"",
|
||||
Self::Light => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Not for Background {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self {
|
||||
match self {
|
||||
Self::Light => Self::Dark,
|
||||
Self::Dark => Self::Light,
|
||||
}
|
||||
}
|
||||
}
|
@ -1128,6 +1128,51 @@ Remember this solution is unsafe! You will have to ensure that accesses to the
|
||||
cell are synchronized.
|
||||
"##,
|
||||
|
||||
E0493: r##"
|
||||
A type with a `Drop` implementation was destructured when trying to initialize
|
||||
a static item.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0493
|
||||
enum DropType {
|
||||
A,
|
||||
}
|
||||
|
||||
impl Drop for DropType {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
field1: DropType,
|
||||
}
|
||||
|
||||
static FOO: Foo = Foo { ..Foo { field1: DropType::A } }; // error!
|
||||
```
|
||||
|
||||
The problem here is that if the given type or one of its fields implements the
|
||||
`Drop` trait, this `Drop` implementation cannot be called during the static
|
||||
type initialization which might cause a memory leak. To prevent this issue,
|
||||
you need to instantiate all the static type's fields by hand.
|
||||
|
||||
```
|
||||
enum DropType {
|
||||
A,
|
||||
}
|
||||
|
||||
impl Drop for DropType {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
field1: DropType,
|
||||
}
|
||||
|
||||
static FOO: Foo = Foo { field1: DropType::A }; // We initialize all fields
|
||||
// by hand.
|
||||
```
|
||||
"##,
|
||||
|
||||
E0499: r##"
|
||||
A variable was borrowed as mutable more than once. Erroneous code example:
|
||||
|
||||
@ -2454,7 +2499,6 @@ There are some known bugs that trigger this message.
|
||||
// E0299, // mismatched types between arms
|
||||
// E0471, // constant evaluation error (in pattern)
|
||||
// E0385, // {} in an aliasable location
|
||||
E0493, // destructors cannot be evaluated at compile-time
|
||||
E0521, // borrowed data escapes outside of closure
|
||||
E0526, // shuffle indices are not constant
|
||||
E0594, // cannot assign to {}
|
||||
|
@ -27,6 +27,9 @@ impl QualifSet {
|
||||
pub trait Qualif {
|
||||
const IDX: usize;
|
||||
|
||||
/// The name of the file used to debug the dataflow analysis that computes this qualif.
|
||||
const ANALYSIS_NAME: &'static str;
|
||||
|
||||
/// Whether this `Qualif` is cleared when a local is moved from.
|
||||
const IS_CLEARED_ON_MOVE: bool = false;
|
||||
|
||||
@ -207,6 +210,7 @@ pub struct HasMutInterior;
|
||||
|
||||
impl Qualif for HasMutInterior {
|
||||
const IDX: usize = 0;
|
||||
const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
|
||||
|
||||
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
|
||||
@ -264,6 +268,7 @@ pub struct NeedsDrop;
|
||||
|
||||
impl Qualif for NeedsDrop {
|
||||
const IDX: usize = 1;
|
||||
const ANALYSIS_NAME: &'static str = "flow_needs_drop";
|
||||
const IS_CLEARED_ON_MOVE: bool = true;
|
||||
|
||||
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
|
@ -208,7 +208,8 @@ where
|
||||
_qualif: PhantomData,
|
||||
};
|
||||
let results =
|
||||
dataflow::Engine::new(item.body, dead_unwinds, analysis).iterate_to_fixpoint();
|
||||
dataflow::Engine::new(item.tcx, item.body, item.def_id, dead_unwinds, analysis)
|
||||
.iterate_to_fixpoint();
|
||||
let cursor = dataflow::ResultsCursor::new(item.body, results);
|
||||
|
||||
let mut qualifs_in_any_value_of_ty = BitSet::new_empty(item.body.local_decls.len());
|
||||
@ -308,7 +309,7 @@ where
|
||||
{
|
||||
type Idx = Local;
|
||||
|
||||
const NAME: &'static str = "flow_sensitive_qualif";
|
||||
const NAME: &'static str = Q::ANALYSIS_NAME;
|
||||
|
||||
fn bits_per_block(&self, body: &mir::Body<'tcx>) -> usize {
|
||||
body.local_decls.len()
|
||||
|
@ -467,8 +467,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
||||
|
||||
self.qualifs.needs_drop.visit_statement(statement, location);
|
||||
self.qualifs.has_mut_interior.visit_statement(statement, location);
|
||||
debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
|
||||
debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
|
||||
|
||||
match statement.kind {
|
||||
StatementKind::Assign(..) => {
|
||||
@ -494,8 +492,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
|
||||
|
||||
self.qualifs.needs_drop.visit_terminator(terminator, location);
|
||||
self.qualifs.has_mut_interior.visit_terminator(terminator, location);
|
||||
debug!("needs_drop: {:?}", self.qualifs.needs_drop.get());
|
||||
debug!("has_mut_interior: {:?}", self.qualifs.has_mut_interior.get());
|
||||
|
||||
self.super_terminator(terminator, location);
|
||||
}
|
||||
|
@ -144,6 +144,25 @@ fn deprecated_function() {}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0550: r##"
|
||||
More than one `deprecated` attribute has been put on an item.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0550
|
||||
#[deprecated(note = "because why not?")]
|
||||
#[deprecated(note = "right?")] // error!
|
||||
fn the_banished() {}
|
||||
```
|
||||
|
||||
The `deprecated` attribute can only be present **once** on an item.
|
||||
|
||||
```
|
||||
#[deprecated(note = "because why not, right?")]
|
||||
fn the_banished() {} // ok!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0552: r##"
|
||||
A unrecognized representation attribute was used.
|
||||
|
||||
@ -435,7 +454,6 @@ features in the `-Z allow_features` flag.
|
||||
// rustc_deprecated attribute must be paired with either stable or unstable
|
||||
// attribute
|
||||
E0549,
|
||||
E0550, // multiple deprecated attributes
|
||||
E0551, // incorrect meta item
|
||||
E0553, // multiple rustc_const_unstable attributes
|
||||
// E0555, // replaced with a generic attribute input check
|
||||
|
@ -108,5 +108,5 @@ LL | let y = { static x: Box<isize> = box 3; x };
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0010, E0015, E0019, E0507.
|
||||
Some errors have detailed explanations: E0010, E0015, E0019, E0493, E0507.
|
||||
For more information about an error, try `rustc --explain E0010`.
|
||||
|
@ -24,3 +24,4 @@ LL | const Z2: () = { let mut x; x = None; x = Some(FakeNeedsDrop); };
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0493`.
|
||||
|
10
src/test/ui/consts/issue-64662.rs
Normal file
10
src/test/ui/consts/issue-64662.rs
Normal file
@ -0,0 +1,10 @@
|
||||
enum Foo {
|
||||
A = foo(), //~ ERROR: type annotations needed
|
||||
B = foo(), //~ ERROR: type annotations needed
|
||||
}
|
||||
|
||||
const fn foo<T>() -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
fn main() {}
|
15
src/test/ui/consts/issue-64662.stderr
Normal file
15
src/test/ui/consts/issue-64662.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/issue-64662.rs:2:9
|
||||
|
|
||||
LL | A = foo(),
|
||||
| ^^^ cannot infer type for `T`
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/issue-64662.rs:3:9
|
||||
|
|
||||
LL | B = foo(),
|
||||
| ^^^ cannot infer type for `T`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0282`.
|
@ -324,5 +324,5 @@ LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
|
||||
|
||||
error: aborting due to 37 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0515, E0723.
|
||||
For more information about an error, try `rustc --explain E0515`.
|
||||
Some errors have detailed explanations: E0493, E0515, E0723.
|
||||
For more information about an error, try `rustc --explain E0493`.
|
||||
|
@ -6,3 +6,4 @@ LL | const F: u32 = (U::X, 42).1;
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0493`.
|
||||
|
@ -54,5 +54,5 @@ LL | #[deprecated(since = "a", since = "b", note = "c")]
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0538, E0541, E0565.
|
||||
Some errors have detailed explanations: E0538, E0541, E0550, E0565.
|
||||
For more information about an error, try `rustc --explain E0538`.
|
||||
|
@ -6,3 +6,4 @@ LL | const F : Foo = (Foo { a : 0 }, Foo { a : 1 }).1;
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0493`.
|
||||
|
@ -68,4 +68,5 @@ LL | const EARLY_DROP_C_OPTION_CONSTANT: i32 = (HELPER, 0).1;
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0716`.
|
||||
Some errors have detailed explanations: E0493, E0716.
|
||||
For more information about an error, try `rustc --explain E0493`.
|
||||
|
Loading…
Reference in New Issue
Block a user