Auto merge of #70451 - Dylan-DPC:rollup-2g9oyht, r=Dylan-DPC

Rollup of 5 pull requests

Successful merges:

 - #69644 (Remove framework in `dataflow/mod.rs` in favor of "generic" one)
 - #69936 (Fix cycle error when emitting suggestion for mismatched `fn` type)
 - #70048 (Allow obtaining &mut OsStr)
 - #70344 (Decouple `rustc_hir::print` into `rustc_hir_pretty`)
 - #70435 (Add regression test for #66706)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-03-27 00:25:26 +00:00
commit 7b73d14b0b
67 changed files with 547 additions and 1443 deletions

View File

@ -3668,6 +3668,7 @@ dependencies = [
"rustc_errors",
"rustc_feature",
"rustc_hir",
"rustc_hir_pretty",
"rustc_interface",
"rustc_lint",
"rustc_metadata",
@ -3742,9 +3743,7 @@ dependencies = [
"lazy_static 1.4.0",
"log",
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_errors",
"rustc_index",
"rustc_macros",
"rustc_span",
@ -3753,6 +3752,18 @@ dependencies = [
"smallvec 1.0.0",
]
[[package]]
name = "rustc_hir_pretty"
version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_hir",
"rustc_span",
"rustc_target",
]
[[package]]
name = "rustc_incremental"
version = "0.0.0"
@ -3903,6 +3914,7 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_hir",
"rustc_hir_pretty",
"rustc_index",
"rustc_session",
"rustc_span",
@ -4087,6 +4099,7 @@ dependencies = [
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_hir",
"rustc_hir_pretty",
"rustc_parse",
"rustc_session",
"rustc_span",

View File

@ -10,7 +10,6 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, Definitions};
use rustc_hir::intravisit;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::print::Nested;
use rustc_hir::*;
use rustc_index::vec::IndexVec;
use rustc_span::hygiene::MacroKind;
@ -890,20 +889,18 @@ impl<'hir> Map<'hir> {
}
}
/// Get a representation of this `id` for debugging purposes.
/// NOTE: Do NOT use this in diagnostics!
pub fn node_to_string(&self, id: HirId) -> String {
hir_id_to_string(self, id, true)
}
pub fn hir_to_user_string(&self, id: HirId) -> String {
hir_id_to_string(self, id, false)
}
pub fn hir_to_pretty_string(&self, id: HirId) -> String {
print::to_string(self, |s| s.print_node(self.get(id)))
hir_id_to_string(self, id)
}
}
impl<'hir> intravisit::Map<'hir> for Map<'hir> {
fn find(&self, hir_id: HirId) -> Option<Node<'hir>> {
self.find(hir_id)
}
fn body(&self, id: BodyId) -> &'hir Body<'hir> {
self.body(id)
}
@ -982,23 +979,8 @@ pub(super) fn index_hir<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> &'tcx Indexe
tcx.arena.alloc(IndexedHir { crate_hash, map })
}
/// Identical to the `PpAnn` implementation for `hir::Crate`,
/// except it avoids creating a dependency on the whole crate.
impl<'hir> print::PpAnn for Map<'hir> {
fn nested(&self, state: &mut print::State<'_>, nested: print::Nested) {
match nested {
Nested::Item(id) => state.print_item(self.expect_item(id.id)),
Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)),
Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)),
Nested::Body(id) => state.print_expr(&self.body(id).value),
Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat),
}
}
}
fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String {
fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
let id_str = format!(" (hir_id={})", id);
let id_str = if include_id { &id_str[..] } else { "" };
let path_str = || {
// This functionality is used for debugging, try to use `TyCtxt` to get
@ -1019,6 +1001,9 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String {
})
};
let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default();
let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str);
match map.find(id) {
Some(Node::Item(item)) => {
let item_str = match item.kind {
@ -1069,22 +1054,20 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId, include_id: bool) -> String {
Some(Node::Field(ref field)) => {
format!("field {} in {}{}", field.ident, path_str(), id_str)
}
Some(Node::AnonConst(_)) => format!("const {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Expr(_)) => format!("expr {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Stmt(_)) => format!("stmt {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::PathSegment(_)) => {
format!("path segment {}{}", map.hir_to_pretty_string(id), id_str)
}
Some(Node::Ty(_)) => format!("type {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::TraitRef(_)) => format!("trait_ref {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Binding(_)) => format!("local {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Pat(_)) => format!("pat {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Param(_)) => format!("param {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Arm(_)) => format!("arm {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Block(_)) => format!("block {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Local(_)) => format!("local {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::AnonConst(_)) => node_str("const"),
Some(Node::Expr(_)) => node_str("expr"),
Some(Node::Stmt(_)) => node_str("stmt"),
Some(Node::PathSegment(_)) => node_str("path segment"),
Some(Node::Ty(_)) => node_str("type"),
Some(Node::TraitRef(_)) => node_str("trait ref"),
Some(Node::Binding(_)) => node_str("local"),
Some(Node::Pat(_)) => node_str("pat"),
Some(Node::Param(_)) => node_str("param"),
Some(Node::Arm(_)) => node_str("arm"),
Some(Node::Block(_)) => node_str("block"),
Some(Node::Local(_)) => node_str("local"),
Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str),
Some(Node::Lifetime(_)) => format!("lifetime {}{}", map.hir_to_pretty_string(id), id_str),
Some(Node::Lifetime(_)) => node_str("lifetime"),
Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str),
Some(Node::MacroDef(_)) => format!("macro {}{}", path_str(), id_str),

View File

@ -21,6 +21,7 @@ rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }
rustc_feature = { path = "../librustc_feature" }
rustc_hir = { path = "../librustc_hir" }
rustc_hir_pretty = { path = "../librustc_hir_pretty" }
rustc_metadata = { path = "../librustc_metadata" }
rustc_mir = { path = "../librustc_mir" }
rustc_parse = { path = "../librustc_parse" }

View File

@ -7,7 +7,7 @@ use rustc_ast::ast;
use rustc_ast_pretty::pprust;
use rustc_hir as hir;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::print as pprust_hir;
use rustc_hir_pretty as pprust_hir;
use rustc_mir::util::{write_mir_graphviz, write_mir_pretty};
use rustc_session::config::{Input, PpMode, PpSourceMode};
use rustc_session::Session;
@ -155,7 +155,7 @@ impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
if let Some(tcx) = self.tcx {
pprust_hir::PpAnn::nested(&tcx.hir(), state, nested)
pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
}
}
}
@ -228,7 +228,7 @@ impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
if let Some(ref tcx) = self.tcx {
pprust_hir::PpAnn::nested(&tcx.hir(), state, nested)
pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
}
}
fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
@ -334,7 +334,8 @@ impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
if let pprust_hir::Nested::Body(id) = nested {
self.tables.set(self.tcx.body_tables(id));
}
pprust_hir::PpAnn::nested(&self.tcx.hir(), state, nested);
let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
pprust_hir::PpAnn::nested(pp_ann, state, nested);
self.tables.set(old_tables);
}
fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {

View File

@ -10,13 +10,11 @@ path = "lib.rs"
doctest = false
[dependencies]
rustc_ast_pretty = { path = "../librustc_ast_pretty" }
rustc_target = { path = "../librustc_target" }
rustc_macros = { path = "../librustc_macros" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_index = { path = "../librustc_index" }
rustc_span = { path = "../librustc_span" }
rustc_errors = { path = "../librustc_errors" }
rustc_serialize = { path = "../libserialize", package = "serialize" }
rustc_ast = { path = "../librustc_ast" }
lazy_static = "1"

View File

@ -2,11 +2,6 @@ use crate::def::{DefKind, Namespace, Res};
use crate::def_id::DefId;
crate use crate::hir_id::HirId;
use crate::itemlikevisit;
use crate::print;
crate use BlockCheckMode::*;
crate use FnRetTy::*;
crate use UnsafeSource::*;
use rustc_ast::ast::{self, AsmDialect, CrateSugar, Ident, Name};
use rustc_ast::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy};
@ -16,7 +11,6 @@ use rustc_ast::node_id::NodeMap;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::{par_for_each_in, Send, Sync};
use rustc_errors::FatalError;
use rustc_macros::HashStable_Generic;
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::{kw, sym, Symbol};
@ -169,12 +163,7 @@ impl fmt::Display for Lifetime {
impl fmt::Debug for Lifetime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"lifetime({}: {})",
self.hir_id,
print::to_string(print::NO_ANN, |s| s.print_lifetime(self))
)
write!(f, "lifetime({}: {})", self.hir_id, self.name.ident())
}
}
@ -191,7 +180,7 @@ impl Lifetime {
/// A `Path` is essentially Rust's notion of a name; for instance,
/// `std::cmp::PartialEq`. It's represented as a sequence of identifiers,
/// along with a bunch of supporting information.
#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)]
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub struct Path<'hir> {
pub span: Span,
/// The resolution for the path.
@ -206,18 +195,6 @@ impl Path<'_> {
}
}
impl fmt::Debug for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "path({})", self)
}
}
impl fmt::Display for Path<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", print::to_string(print::NO_ANN, |s| s.print_path(self, false)))
}
}
/// A segment of a path: an identifier, an optional lifetime, and a set of
/// types.
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
@ -388,9 +365,9 @@ pub enum GenericBound<'hir> {
}
impl GenericBound<'_> {
pub fn trait_def_id(&self) -> Option<DefId> {
pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
match self {
GenericBound::Trait(data, _) => Some(data.trait_ref.trait_def_id()),
GenericBound::Trait(data, _) => Some(&data.trait_ref),
_ => None,
}
}
@ -758,7 +735,7 @@ pub struct Block<'hir> {
pub targeted_by_break: bool,
}
#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)]
#[derive(Debug, RustcEncodable, RustcDecodable, HashStable_Generic)]
pub struct Pat<'hir> {
#[stable_hasher(ignore)]
pub hir_id: HirId,
@ -766,17 +743,6 @@ pub struct Pat<'hir> {
pub span: Span,
}
impl fmt::Debug for Pat<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"pat({}: {})",
self.hir_id,
print::to_string(print::NO_ANN, |s| s.print_pat(self))
)
}
}
impl Pat<'_> {
// FIXME(#19596) this is a workaround, but there should be a better way
fn walk_short_(&self, it: &mut impl FnMut(&Pat<'_>) -> bool) -> bool {
@ -1118,26 +1084,15 @@ impl UnOp {
}
/// A statement.
#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)]
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub struct Stmt<'hir> {
pub hir_id: HirId,
pub kind: StmtKind<'hir>,
pub span: Span,
}
impl fmt::Debug for Stmt<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"stmt({}: {})",
self.hir_id,
print::to_string(print::NO_ANN, |s| s.print_stmt(self))
)
}
}
/// The contents of a statement.
#[derive(RustcEncodable, RustcDecodable, HashStable_Generic)]
#[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
pub enum StmtKind<'hir> {
/// A local (`let`) binding.
Local(&'hir Local<'hir>),
@ -1351,7 +1306,7 @@ pub struct AnonConst {
}
/// An expression.
#[derive(RustcEncodable, RustcDecodable)]
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Expr<'hir> {
pub hir_id: HirId,
pub kind: ExprKind<'hir>,
@ -1472,17 +1427,6 @@ impl Expr<'_> {
}
}
impl fmt::Debug for Expr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"expr({}: {})",
self.hir_id,
print::to_string(print::NO_ANN, |s| s.print_expr(self))
)
}
}
/// Checks if the specified expression is a built-in range literal.
/// (See: `LoweringContext::lower_expr()`).
///
@ -1965,19 +1909,13 @@ impl TypeBinding<'_> {
}
}
#[derive(RustcEncodable, RustcDecodable)]
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Ty<'hir> {
pub hir_id: HirId,
pub kind: TyKind<'hir>,
pub span: Span,
}
impl fmt::Debug for Ty<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "type({})", print::to_string(print::NO_ANN, |s| s.print_type(self)))
}
}
/// Not represented directly in the AST; referred to by name through a `ty_path`.
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
#[derive(HashStable_Generic)]
@ -2182,15 +2120,6 @@ pub enum FnRetTy<'hir> {
Return(&'hir Ty<'hir>),
}
impl fmt::Display for FnRetTy<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Return(ref ty) => print::to_string(print::NO_ANN, |s| s.print_type(ty)).fmt(f),
Self::DefaultReturn(_) => "()".fmt(f),
}
}
}
impl FnRetTy<'_> {
pub fn span(&self) -> Span {
match *self {
@ -2274,13 +2203,10 @@ pub struct TraitRef<'hir> {
impl TraitRef<'_> {
/// Gets the `DefId` of the referenced trait. It _must_ actually be a trait or trait alias.
pub fn trait_def_id(&self) -> DefId {
pub fn trait_def_id(&self) -> Option<DefId> {
match self.path.res {
Res::Def(DefKind::Trait, did) => did,
Res::Def(DefKind::TraitAlias, did) => did,
Res::Err => {
FatalError.raise();
}
Res::Def(DefKind::Trait | DefKind::TraitAlias, did) => Some(did),
Res::Err => None,
_ => unreachable!(),
}
}

View File

@ -121,6 +121,8 @@ impl<'a> FnKind<'a> {
/// An abstract representation of the HIR `rustc::hir::map::Map`.
pub trait Map<'hir> {
/// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found.
fn find(&self, hir_id: HirId) -> Option<Node<'hir>>;
fn body(&self, id: BodyId) -> &'hir Body<'hir>;
fn item(&self, id: HirId) -> &'hir Item<'hir>;
fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir>;
@ -132,6 +134,9 @@ pub trait Map<'hir> {
pub struct ErasedMap<'hir>(&'hir dyn Map<'hir>);
impl<'hir> Map<'hir> for ErasedMap<'hir> {
fn find(&self, _: HirId) -> Option<Node<'hir>> {
None
}
fn body(&self, id: BodyId) -> &'hir Body<'hir> {
self.0.body(id)
}

View File

@ -7,6 +7,7 @@
#![feature(const_fn)] // For the unsizing cast on `&[]`
#![feature(const_panic)]
#![feature(in_band_lifetimes)]
#![feature(or_patterns)]
#![feature(specialization)]
#![recursion_limit = "256"]
@ -23,7 +24,6 @@ pub mod intravisit;
pub mod itemlikevisit;
pub mod lang_items;
pub mod pat_util;
pub mod print;
mod stable_hash_impls;
mod target;
pub mod weak_lang_items;

View File

@ -0,0 +1,18 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_hir_pretty"
version = "0.0.0"
edition = "2018"
[lib]
name = "rustc_hir_pretty"
path = "lib.rs"
doctest = false
[dependencies]
rustc_ast_pretty = { path = "../librustc_ast_pretty" }
rustc_hir = { path = "../librustc_hir" }
rustc_target = { path = "../librustc_target" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_span = { path = "../librustc_span" }
rustc_ast = { path = "../librustc_ast" }

View File

@ -1,21 +1,26 @@
#![recursion_limit = "256"]
use rustc_ast::ast;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
use rustc_ast_pretty::pp::{self, Breaks};
use rustc_ast_pretty::pprust::{Comments, PrintState};
use rustc_hir as hir;
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node};
use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::{kw, IdentPrinter};
use rustc_span::{self, BytePos, FileName};
use rustc_target::spec::abi::Abi;
use crate::hir;
use crate::hir::{GenericArg, GenericParam, GenericParamKind, Node};
use crate::hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
use std::borrow::Cow;
use std::cell::Cell;
use std::vec;
pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: hir::HirId) -> String {
to_string(&map, |s| s.print_node(map.find(hir_id).unwrap()))
}
pub enum AnnNode<'a> {
Name(&'a ast::Name),
Block(&'a hir::Block<'a>),
@ -47,7 +52,7 @@ pub struct NoAnn;
impl PpAnn for NoAnn {}
pub const NO_ANN: &dyn PpAnn = &NoAnn;
impl PpAnn for hir::Crate<'a> {
impl PpAnn for hir::Crate<'_> {
fn try_fetch_item(&self, item: hir::HirId) -> Option<&hir::Item<'_>> {
Some(self.item(item))
}
@ -62,6 +67,20 @@ impl PpAnn for hir::Crate<'a> {
}
}
/// Identical to the `PpAnn` implementation for `hir::Crate`,
/// except it avoids creating a dependency on the whole crate.
impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> {
fn nested(&self, state: &mut State<'_>, nested: Nested) {
match nested {
Nested::Item(id) => state.print_item(self.item(id.id)),
Nested::TraitItem(id) => state.print_trait_item(self.trait_item(id)),
Nested::ImplItem(id) => state.print_impl_item(self.impl_item(id)),
Nested::Body(id) => state.print_expr(&self.body(id).value),
Nested::BodyParamPat(id, i) => state.print_pat(&self.body(id).params[i].pat),
}
}
}
pub struct State<'a> {
pub s: pp::Printer,
comments: Option<Comments<'a>>,
@ -1006,10 +1025,10 @@ impl<'a> State<'a> {
close_box: bool,
) {
match blk.rules {
hir::UnsafeBlock(..) => self.word_space("unsafe"),
hir::PushUnsafeBlock(..) => self.word_space("push_unsafe"),
hir::PopUnsafeBlock(..) => self.word_space("pop_unsafe"),
hir::DefaultBlock => (),
hir::BlockCheckMode::UnsafeBlock(..) => self.word_space("unsafe"),
hir::BlockCheckMode::PushUnsafeBlock(..) => self.word_space("push_unsafe"),
hir::BlockCheckMode::PopUnsafeBlock(..) => self.word_space("pop_unsafe"),
hir::BlockCheckMode::DefaultBlock => (),
}
self.maybe_print_comment(blk.span.lo());
self.ann.pre(self, AnnNode::Block(blk));
@ -1092,7 +1111,7 @@ impl<'a> State<'a> {
&mut self,
qpath: &hir::QPath<'_>,
fields: &[hir::Field<'_>],
wth: &Option<&'hir hir::Expr<'_>>,
wth: &Option<&hir::Expr<'_>>,
) {
self.print_qpath(qpath, true);
self.s.word("{");
@ -1848,7 +1867,8 @@ impl<'a> State<'a> {
self.print_block_unclosed(&blk);
// If it is a user-provided unsafe block, print a comma after it
if let hir::UnsafeBlock(hir::UserProvided) = blk.rules {
if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = blk.rules
{
self.s.word(",");
}
}
@ -1928,18 +1948,18 @@ impl<'a> State<'a> {
});
self.s.word("|");
if let hir::DefaultReturn(..) = decl.output {
if let hir::FnRetTy::DefaultReturn(..) = decl.output {
return;
}
self.space_if_not_bol();
self.word_space("->");
match decl.output {
hir::Return(ref ty) => {
hir::FnRetTy::Return(ref ty) => {
self.print_type(&ty);
self.maybe_print_comment(ty.span.lo())
}
hir::DefaultReturn(..) => unreachable!(),
hir::FnRetTy::DefaultReturn(..) => unreachable!(),
}
}
@ -2112,7 +2132,7 @@ impl<'a> State<'a> {
}
pub fn print_fn_output(&mut self, decl: &hir::FnDecl<'_>) {
if let hir::DefaultReturn(..) = decl.output {
if let hir::FnRetTy::DefaultReturn(..) = decl.output {
return;
}
@ -2120,13 +2140,13 @@ impl<'a> State<'a> {
self.ibox(INDENT_UNIT);
self.word_space("->");
match decl.output {
hir::DefaultReturn(..) => unreachable!(),
hir::Return(ref ty) => self.print_type(&ty),
hir::FnRetTy::DefaultReturn(..) => unreachable!(),
hir::FnRetTy::Return(ref ty) => self.print_type(&ty),
}
self.end();
match decl.output {
hir::Return(ref output) => self.maybe_print_comment(output.span.lo()),
hir::FnRetTy::Return(ref output) => self.maybe_print_comment(output.span.lo()),
_ => {}
}
}

View File

@ -19,6 +19,7 @@ rustc_attr = { path = "../librustc_attr" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }
rustc_hir = { path = "../librustc_hir" }
rustc_hir_pretty = { path = "../librustc_hir_pretty" }
rustc_target = { path = "../librustc_target" }
rustc_index = { path = "../librustc_index" }
rustc_serialize = { path = "../libserialize", package = "serialize" }

View File

@ -824,8 +824,10 @@ impl EncodeContext<'tcx> {
record!(self.per_def.kind[def_id] <- match trait_item.kind {
ty::AssocKind::Const => {
let rendered =
hir::print::to_string(&self.tcx.hir(), |s| s.print_trait_item(ast_item));
let rendered = rustc_hir_pretty::to_string(
&(&self.tcx.hir() as &dyn intravisit::Map<'_>),
|s| s.print_trait_item(ast_item)
);
let rendered_const = self.lazy(RenderedConst(rendered));
EntryKind::AssocConst(
@ -1044,8 +1046,11 @@ impl EncodeContext<'tcx> {
}
fn encode_rendered_const_for_body(&mut self, body_id: hir::BodyId) -> Lazy<RenderedConst> {
let body = self.tcx.hir().body(body_id);
let rendered = hir::print::to_string(&self.tcx.hir(), |s| s.print_expr(&body.value));
let hir = self.tcx.hir();
let body = hir.body(body_id);
let rendered = rustc_hir_pretty::to_string(&(&hir as &dyn intravisit::Map<'_>), |s| {
s.print_expr(&body.value)
});
let rendered_const = &RenderedConst(rendered);
self.lazy(rendered_const)
}

View File

@ -29,12 +29,12 @@ use std::mem;
use std::rc::Rc;
use crate::dataflow;
use crate::dataflow::generic::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError};
use crate::dataflow::Borrows;
use crate::dataflow::EverInitializedPlaces;
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults};
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use crate::transform::MirSource;
@ -298,7 +298,7 @@ fn do_mir_borrowck<'a, 'tcx>(
mbcx.report_move_errors(errors);
}
dataflow::generic::visit_results(
dataflow::visit_results(
&*body,
traversal::reverse_postorder(&*body).map(|(bb, _)| bb),
&results,
@ -509,7 +509,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
type FlowState = Flows<'cx, 'tcx>;
fn visit_statement(

View File

@ -21,9 +21,9 @@ use std::str::FromStr;
use self::mir_util::PassWhere;
use polonius_engine::{Algorithm, Output};
use crate::dataflow::generic::ResultsCursor;
use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData};
use crate::dataflow::MaybeInitializedPlaces;
use crate::dataflow::ResultsCursor;
use crate::transform::MirSource;
use crate::util as mir_util;
use crate::util::pretty;

View File

@ -3,9 +3,9 @@ use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
use std::rc::Rc;
use crate::dataflow::generic::ResultsCursor;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::MaybeInitializedPlaces;
use crate::dataflow::ResultsCursor;
use crate::borrow_check::{
constraints::OutlivesConstraintSet,

View File

@ -8,10 +8,10 @@ use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
use rustc_trait_selection::traits::query::type_op::TypeOp;
use std::rc::Rc;
use crate::dataflow::generic::ResultsCursor;
use crate::dataflow::indexes::MovePathIndex;
use crate::dataflow::move_paths::{HasMoveData, MoveData};
use crate::dataflow::MaybeInitializedPlaces;
use crate::dataflow::ResultsCursor;
use crate::borrow_check::{
region_infer::values::{self, PointIndex, RegionValueElements},

View File

@ -39,9 +39,9 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::{Fallible, NoSolution};
use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations};
use crate::dataflow::generic::ResultsCursor;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::MaybeInitializedPlaces;
use crate::dataflow::ResultsCursor;
use crate::transform::promote_consts::should_suggest_const_in_array_repeat_expressions_attribute;
use crate::borrow_check::{

View File

@ -1,169 +0,0 @@
//! A nice wrapper to consume dataflow results at several CFG
//! locations.
use rustc::mir::{BasicBlock, Location};
use rustc_index::bit_set::{BitIter, BitSet, HybridBitSet};
use crate::dataflow::{BitDenotation, DataflowResults, GenKillSet};
use std::borrow::Borrow;
use std::iter;
/// A trait for "cartesian products" of multiple FlowAtLocation.
///
/// There's probably a way to auto-impl this, but I think
/// it is cleaner to have manual visitor impls.
pub trait FlowsAtLocation {
/// Reset the state bitvector to represent the entry to block `bb`.
fn reset_to_entry_of(&mut self, bb: BasicBlock);
/// Reset the state bitvector to represent the exit of the
/// terminator of block `bb`.
///
/// **Important:** In the case of a `Call` terminator, these
/// effects do *not* include the result of storing the destination
/// of the call, since that is edge-dependent (in other words, the
/// effects don't apply to the unwind edge).
fn reset_to_exit_of(&mut self, bb: BasicBlock);
/// Builds gen and kill sets for statement at `loc`.
///
/// Note that invoking this method alone does not change the
/// `curr_state` -- you must invoke `apply_local_effect`
/// afterwards.
fn reconstruct_statement_effect(&mut self, loc: Location);
/// Builds gen and kill sets for terminator for `loc`.
///
/// Note that invoking this method alone does not change the
/// `curr_state` -- you must invoke `apply_local_effect`
/// afterwards.
fn reconstruct_terminator_effect(&mut self, loc: Location);
/// Apply current gen + kill sets to `flow_state`.
///
/// (`loc` parameters can be ignored if desired by
/// client. For the terminator, the `stmt_idx` will be the number
/// of statements in the block.)
fn apply_local_effect(&mut self, loc: Location);
}
/// Represents the state of dataflow at a particular
/// CFG location, both before and after it is
/// executed.
///
/// Data flow results are typically computed only as basic block
/// boundaries. A `FlowInProgress` allows you to reconstruct the
/// effects at any point in the control-flow graph by starting with
/// the state at the start of the basic block (`reset_to_entry_of`)
/// and then replaying the effects of statements and terminators
/// (e.g., via `reconstruct_statement_effect` and
/// `reconstruct_terminator_effect`; don't forget to call
/// `apply_local_effect`).
pub struct FlowAtLocation<'tcx, BD, DR = DataflowResults<'tcx, BD>>
where
BD: BitDenotation<'tcx>,
DR: Borrow<DataflowResults<'tcx, BD>>,
{
base_results: DR,
curr_state: BitSet<BD::Idx>,
stmt_trans: GenKillSet<BD::Idx>,
}
impl<'tcx, BD, DR> FlowAtLocation<'tcx, BD, DR>
where
BD: BitDenotation<'tcx>,
DR: Borrow<DataflowResults<'tcx, BD>>,
{
/// Iterate over each bit set in the current state.
pub fn each_state_bit<F>(&self, f: F)
where
F: FnMut(BD::Idx),
{
self.curr_state.iter().for_each(f)
}
/// Iterate over each `gen` bit in the current effect (invoke
/// `reconstruct_statement_effect` or
/// `reconstruct_terminator_effect` first).
pub fn each_gen_bit<F>(&self, f: F)
where
F: FnMut(BD::Idx),
{
self.stmt_trans.gen_set.iter().for_each(f)
}
pub fn new(results: DR) -> Self {
let bits_per_block = results.borrow().sets().bits_per_block();
let curr_state = BitSet::new_empty(bits_per_block);
let stmt_trans = GenKillSet::from_elem(HybridBitSet::new_empty(bits_per_block));
FlowAtLocation { base_results: results, curr_state, stmt_trans }
}
/// Access the underlying operator.
pub fn operator(&self) -> &BD {
self.base_results.borrow().operator()
}
pub fn contains(&self, x: BD::Idx) -> bool {
self.curr_state.contains(x)
}
/// Returns an iterator over the elements present in the current state.
pub fn iter_incoming(&self) -> iter::Peekable<BitIter<'_, BD::Idx>> {
self.curr_state.iter().peekable()
}
/// Creates a clone of the current state and applies the local
/// effects to the clone (leaving the state of self intact).
/// Invokes `f` with an iterator over the resulting state.
pub fn with_iter_outgoing<F>(&self, f: F)
where
F: FnOnce(BitIter<'_, BD::Idx>),
{
let mut curr_state = self.curr_state.clone();
self.stmt_trans.apply(&mut curr_state);
f(curr_state.iter());
}
/// Returns a bitset of the elements present in the current state.
pub fn as_dense(&self) -> &BitSet<BD::Idx> {
&self.curr_state
}
}
impl<'tcx, BD, DR> FlowsAtLocation for FlowAtLocation<'tcx, BD, DR>
where
BD: BitDenotation<'tcx>,
DR: Borrow<DataflowResults<'tcx, BD>>,
{
fn reset_to_entry_of(&mut self, bb: BasicBlock) {
self.curr_state.overwrite(self.base_results.borrow().sets().entry_set_for(bb.index()));
}
fn reset_to_exit_of(&mut self, bb: BasicBlock) {
self.reset_to_entry_of(bb);
let trans = self.base_results.borrow().sets().trans_for(bb.index());
trans.apply(&mut self.curr_state)
}
fn reconstruct_statement_effect(&mut self, loc: Location) {
self.stmt_trans.clear();
self.base_results.borrow().operator().before_statement_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);
self.base_results.borrow().operator().statement_effect(&mut self.stmt_trans, loc);
}
fn reconstruct_terminator_effect(&mut self, loc: Location) {
self.stmt_trans.clear();
self.base_results.borrow().operator().before_terminator_effect(&mut self.stmt_trans, loc);
self.stmt_trans.apply(&mut self.curr_state);
self.base_results.borrow().operator().terminator_effect(&mut self.stmt_trans, loc);
}
fn apply_local_effect(&mut self, _loc: Location) {
self.stmt_trans.apply(&mut self.curr_state)
}
}

View File

@ -1,26 +1,25 @@
//! A framework that can express both [gen-kill] and generic dataflow problems.
//!
//! There is another interface for dataflow in the compiler in `librustc_mir/dataflow/mod.rs`. The
//! interface in this module will eventually [replace that one][design-meeting].
//! To actually use this framework, you must implement either the `Analysis` or the
//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill
//! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
//! `impls` module contains several examples of gen/kill dataflow analyses.
//!
//! To actually use this framework, you must implement either the `Analysis` or the `GenKillAnalysis`
//! trait. If your transfer function can be expressed with only gen/kill operations, prefer
//! `GenKillAnalysis` since it will run faster while iterating to fixpoint. Create an `Engine` using
//! the appropriate constructor and call `iterate_to_fixpoint`. You can use a `ResultsCursor` to
//! inspect the fixpoint solution to your dataflow problem.
//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
//! fixpoint solution to your dataflow problem, or implement the `ResultsVisitor` interface and use
//! `visit_results`. The following example uses the `ResultsCursor` approach.
//!
//! ```ignore(cross-crate-imports)
//! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available.
//!
//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) {
//! let analysis = MyAnalysis::new();
//!
//! // If `MyAnalysis` implements `GenKillAnalysis`.
//! let results = Engine::new_gen_kill(tcx, body, did, analysis).iterate_to_fixpoint();
//!
//! // If `MyAnalysis` implements `Analysis`.
//! // let results = Engine::new_generic(tcx, body, did, analysis).iterate_to_fixpoint();
//!
//! let mut cursor = ResultsCursor::new(body, results);
//! let analysis = MyAnalysis::new()
//! .into_engine(tcx, body, did)
//! .iterate_to_fixpoint()
//! .into_results_cursor(body);
//!
//! // Print the dataflow state *after* each statement in the start block.
//! for (_, statement_index) in body.block_data[START_BLOCK].statements.iter_enumerated() {
//! cursor.seek_after(Location { block: START_BLOCK, statement_index });
//! let state = cursor.get();
@ -30,7 +29,6 @@
//! ```
//!
//! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems
//! [design-meeting]https://github.com/rust-lang/compiler-team/issues/202
use std::io;
@ -41,8 +39,6 @@ use rustc_hir::def_id::DefId;
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_index::vec::{Idx, IndexVec};
use crate::dataflow::BottomValue;
mod cursor;
mod engine;
mod graphviz;
@ -95,6 +91,47 @@ where
}
}
/// Parameterization for the precise form of data flow that is used.
///
/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
/// This also determines the semantics of the lattice `join` operator used to merge dataflow
/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
/// point.
///
/// This means, for propagation across the graph, that you either want to start at all-zeroes and
/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
/// as your merge when propagating.
pub trait BottomValue {
/// Specifies the initial value for each bit in the entry set for each basic block.
const BOTTOM_VALUE: bool;
/// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
///
/// It is almost certainly wrong to override this, since it automatically applies
/// * `inout_set & in_set` if `BOTTOM_VALUE == true`
/// * `inout_set | in_set` if `BOTTOM_VALUE == false`
///
/// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
/// For clarity, the above statement again from a different perspective:
/// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
/// `!BOTTOM_VALUE`.
///
/// There are situations where you want the opposite behaviour: propagate only if *all*
/// predecessor blocks's value is `!BOTTOM_VALUE`.
/// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
/// means that all code paths leading to the location must have set the bit, instead of any
/// code path leading there.
///
/// If you want this kind of "definitely set" analysis, you need to
/// 1. Invert `BOTTOM_VALUE`
/// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
/// 3. Override `join` to do the opposite from what it's doing now.
#[inline]
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
}
}
/// Define the domain of a dataflow problem.
///
/// This trait specifies the lattice on which this analysis operates. For now, this must be a

View File

@ -1,6 +1,6 @@
pub use super::*;
use crate::dataflow::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
use crate::dataflow::{AnalysisDomain, GenKill, GenKillAnalysis};
use rustc::mir::visit::Visitor;
use rustc::mir::*;
use rustc::ty::{ParamEnv, TyCtxt};

View File

@ -8,8 +8,8 @@ use rustc_index::bit_set::BitSet;
use crate::borrow_check::{
places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, ToRegionVid,
};
use crate::dataflow::generic::{self, GenKill};
use crate::dataflow::BottomValue;
use crate::dataflow::{self, GenKill};
use std::rc::Rc;
@ -226,7 +226,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
}
}
impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
type Idx = BorrowIndex;
const NAME: &'static str = "borrows";
@ -245,7 +245,7 @@ impl<'tcx> generic::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
}
}
impl<'tcx> generic::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
fn before_statement_effect(
&self,
trans: &mut impl GenKill<Self::Idx>,

View File

@ -12,9 +12,8 @@ use super::MoveDataParamEnv;
use crate::util::elaborate_drops::DropFlagState;
use super::generic::{AnalysisDomain, GenKill, GenKillAnalysis};
use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
use super::BottomValue;
use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
use super::drop_flag_effects_for_function_entry;
use super::drop_flag_effects_for_location;

View File

@ -1,7 +1,7 @@
pub use super::*;
use crate::dataflow::generic::{self as dataflow, GenKill, Results, ResultsRefCursor};
use crate::dataflow::BottomValue;
use crate::dataflow::{self, GenKill, Results, ResultsRefCursor};
use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use std::cell::RefCell;

View File

@ -1,35 +1,22 @@
use rustc::mir::traversal;
use rustc::mir::{self, BasicBlock, BasicBlockData, Body, Location, Statement, Terminator};
use rustc::ty::{self, TyCtxt};
use rustc::ty;
use rustc_ast::ast::{self, MetaItem};
use rustc_ast_pretty::pprust;
use rustc_data_structures::work_queue::WorkQueue;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_index::vec::Idx;
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use std::borrow::Borrow;
use std::fmt;
use std::io;
use std::path::PathBuf;
pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
pub(crate) use self::drop_flag_effects::*;
pub use self::impls::borrows::Borrows;
pub use self::impls::DefinitelyInitializedPlaces;
pub use self::impls::EverInitializedPlaces;
pub use self::impls::{MaybeBorrowedLocals, MaybeMutBorrowedLocals};
pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
pub use self::impls::{MaybeRequiresStorage, MaybeStorageLive};
pub use self::framework::{
visit_results, Analysis, AnalysisDomain, BorrowckFlowState, BorrowckResults, BottomValue,
Engine, GenKill, GenKillAnalysis, Results, ResultsCursor, ResultsRefCursor, ResultsVisitor,
};
pub use self::impls::{
borrows::Borrows, DefinitelyInitializedPlaces, EverInitializedPlaces, MaybeBorrowedLocals,
MaybeInitializedPlaces, MaybeMutBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive,
MaybeUninitializedPlaces,
};
use self::move_paths::MoveData;
mod at_location;
pub mod drop_flag_effects;
pub mod generic;
mod graphviz;
mod framework;
mod impls;
pub mod move_paths;
@ -40,74 +27,9 @@ pub(crate) mod indexes {
};
}
pub(crate) struct DataflowBuilder<'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
def_id: DefId,
flow_state: DataflowAnalysis<'a, 'tcx, BD>,
print_preflow_to: Option<String>,
print_postflow_to: Option<String>,
}
/// `DebugFormatted` encapsulates the "{:?}" rendering of some
/// arbitrary value. This way: you pay cost of allocating an extra
/// string (as well as that of rendering up-front); in exchange, you
/// don't have to hand over ownership of your value or deal with
/// borrowing it.
pub struct DebugFormatted(String);
impl DebugFormatted {
pub fn new(input: &dyn fmt::Debug) -> DebugFormatted {
DebugFormatted(format!("{:?}", input))
}
}
impl fmt::Debug for DebugFormatted {
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "{}", self.0)
}
}
pub trait Dataflow<'tcx, BD: BitDenotation<'tcx>> {
/// Sets up and runs the dataflow problem, using `p` to render results if
/// implementation so chooses.
fn dataflow<P>(&mut self, p: P)
where
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
let _ = p; // default implementation does not instrument process.
self.build_sets();
self.propagate();
}
/// Sets up the entry, gen, and kill sets for this instance of a dataflow problem.
fn build_sets(&mut self);
/// Finds a fixed-point solution to this instance of a dataflow problem.
fn propagate(&mut self);
}
impl<'a, 'tcx, BD> Dataflow<'tcx, BD> for DataflowBuilder<'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
fn dataflow<P>(&mut self, p: P)
where
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
self.flow_state.build_sets();
self.pre_dataflow_instrumentation(|c, i| p(c, i)).unwrap();
self.flow_state.propagate();
self.post_dataflow_instrumentation(|c, i| p(c, i)).unwrap();
}
fn build_sets(&mut self) {
self.flow_state.build_sets();
}
fn propagate(&mut self) {
self.flow_state.propagate();
}
pub struct MoveDataParamEnv<'tcx> {
pub(crate) move_data: MoveData<'tcx>,
pub(crate) param_env: ty::ParamEnv<'tcx>,
}
pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option<MetaItem> {
@ -124,811 +46,3 @@ pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Opti
}
None
}
pub struct MoveDataParamEnv<'tcx> {
pub(crate) move_data: MoveData<'tcx>,
pub(crate) param_env: ty::ParamEnv<'tcx>,
}
pub fn do_dataflow<'a, 'tcx, BD, P>(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
def_id: DefId,
attributes: &[ast::Attribute],
dead_unwinds: &BitSet<BasicBlock>,
bd: BD,
p: P,
) -> DataflowResults<'tcx, BD>
where
BD: BitDenotation<'tcx>,
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
let flow_state = DataflowAnalysis::new(body, dead_unwinds, bd);
flow_state.run(tcx, def_id, attributes, p)
}
impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
pub(crate) fn run<P>(
self,
tcx: TyCtxt<'tcx>,
def_id: DefId,
attributes: &[ast::Attribute],
p: P,
) -> DataflowResults<'tcx, BD>
where
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
if let Some(item) = has_rustc_mir_with(attrs, name) {
if let Some(s) = item.value_str() {
return Some(s.to_string());
} else {
let path = pprust::path_to_string(&item.path);
sess.span_err(item.span, &format!("{} attribute requires a path", path));
return None;
}
}
None
};
let print_preflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_preflow);
let print_postflow_to = name_found(tcx.sess, attributes, sym::borrowck_graphviz_postflow);
let mut mbcx =
DataflowBuilder { def_id, print_preflow_to, print_postflow_to, flow_state: self };
mbcx.dataflow(p);
mbcx.flow_state.results()
}
}
struct PropagationContext<'b, 'a, 'tcx, O>
where
O: BitDenotation<'tcx>,
{
builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
}
impl<'a, 'tcx, BD> DataflowAnalysis<'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
fn propagate(&mut self) {
let mut temp = BitSet::new_empty(self.flow_state.sets.bits_per_block);
let mut propcx = PropagationContext { builder: self };
propcx.walk_cfg(&mut temp);
}
fn build_sets(&mut self) {
// Build the transfer function for each block.
for (bb, data) in self.body.basic_blocks().iter_enumerated() {
let &mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = data;
let trans = self.flow_state.sets.trans_mut_for(bb.index());
for j_stmt in 0..statements.len() {
let location = Location { block: bb, statement_index: j_stmt };
self.flow_state.operator.before_statement_effect(trans, location);
self.flow_state.operator.statement_effect(trans, location);
}
if terminator.is_some() {
let location = Location { block: bb, statement_index: statements.len() };
self.flow_state.operator.before_terminator_effect(trans, location);
self.flow_state.operator.terminator_effect(trans, location);
}
}
// Initialize the flow state at entry to the start block.
let on_entry = self.flow_state.sets.entry_set_mut_for(mir::START_BLOCK.index());
self.flow_state.operator.start_block_effect(on_entry);
}
}
impl<'b, 'a, 'tcx, BD> PropagationContext<'b, 'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
fn walk_cfg(&mut self, in_out: &mut BitSet<BD::Idx>) {
let body = self.builder.body;
// Initialize the dirty queue in reverse post-order. This makes it more likely that the
// entry state for each basic block will have the effects of its predecessors applied
// before it is processed. In fact, for CFGs without back edges, this guarantees that
// dataflow will converge in exactly `N` iterations, where `N` is the number of basic
// blocks.
let mut dirty_queue: WorkQueue<mir::BasicBlock> =
WorkQueue::with_none(body.basic_blocks().len());
for (bb, _) in traversal::reverse_postorder(body) {
dirty_queue.insert(bb);
}
// Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will
// be processed after the ones added above.
for bb in body.basic_blocks().indices() {
dirty_queue.insert(bb);
}
while let Some(bb) = dirty_queue.pop() {
let (on_entry, trans) = self.builder.flow_state.sets.get_mut(bb.index());
debug_assert!(in_out.words().len() == on_entry.words().len());
in_out.overwrite(on_entry);
trans.apply(in_out);
let bb_data = &body[bb];
self.builder.propagate_bits_into_graph_successors_of(
in_out,
(bb, bb_data),
&mut dirty_queue,
);
}
}
}
fn dataflow_path(context: &str, path: &str) -> PathBuf {
let mut path = PathBuf::from(path);
let new_file_name = {
let orig_file_name = path.file_name().unwrap().to_str().unwrap();
format!("{}_{}", context, orig_file_name)
};
path.set_file_name(new_file_name);
path
}
impl<'a, 'tcx, BD> DataflowBuilder<'a, 'tcx, BD>
where
BD: BitDenotation<'tcx>,
{
fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
where
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
if let Some(ref path_str) = self.print_preflow_to {
let path = dataflow_path(BD::name(), path_str);
graphviz::print_borrowck_graph_to(self, &path, p)
} else {
Ok(())
}
}
fn post_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
where
P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
if let Some(ref path_str) = self.print_postflow_to {
let path = dataflow_path(BD::name(), path_str);
graphviz::print_borrowck_graph_to(self, &path, p)
} else {
Ok(())
}
}
}
/// DataflowResultsConsumer abstracts over walking the MIR with some
/// already constructed dataflow results.
///
/// It abstracts over the FlowState and also completely hides the
/// underlying flow analysis results, because it needs to handle cases
/// where we are combining the results of *multiple* flow analyses
/// (e.g., borrows + inits + uninits).
pub(crate) trait DataflowResultsConsumer<'a, 'tcx: 'a> {
type FlowState: FlowsAtLocation;
// Observation Hooks: override (at least one of) these to get analysis feedback.
fn visit_block_entry(&mut self, _bb: BasicBlock, _flow_state: &Self::FlowState) {}
fn visit_statement_entry(
&mut self,
_loc: Location,
_stmt: &'a Statement<'tcx>,
_flow_state: &Self::FlowState,
) {
}
fn visit_terminator_entry(
&mut self,
_loc: Location,
_term: &'a Terminator<'tcx>,
_flow_state: &Self::FlowState,
) {
}
// Main entry point: this drives the processing of results.
fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) {
let flow = flow_uninit;
for (bb, _) in traversal::reverse_postorder(self.body()) {
flow.reset_to_entry_of(bb);
self.process_basic_block(bb, flow);
}
}
fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
self.visit_block_entry(bb, flow_state);
let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = self.body()[bb];
let mut location = Location { block: bb, statement_index: 0 };
for stmt in statements.iter() {
flow_state.reconstruct_statement_effect(location);
self.visit_statement_entry(location, stmt, flow_state);
flow_state.apply_local_effect(location);
location.statement_index += 1;
}
if let Some(ref term) = *terminator {
flow_state.reconstruct_terminator_effect(location);
self.visit_terminator_entry(location, term, flow_state);
// We don't need to apply the effect of the terminator,
// since we are only visiting dataflow state on control
// flow entry to the various nodes. (But we still need to
// reconstruct the effect, because the visit method might
// inspect it.)
}
}
// Delegated Hooks: Provide access to the MIR and process the flow state.
fn body(&self) -> &'a Body<'tcx>;
}
/// Allows iterating dataflow results in a flexible and reasonably fast way.
pub struct DataflowResultsCursor<'mir, 'tcx, BD, DR = DataflowResults<'tcx, BD>>
where
BD: BitDenotation<'tcx>,
DR: Borrow<DataflowResults<'tcx, BD>>,
{
flow_state: FlowAtLocation<'tcx, BD, DR>,
// The statement (or terminator) whose effect has been reconstructed in
// flow_state.
curr_loc: Option<Location>,
body: &'mir Body<'tcx>,
}
pub type DataflowResultsRefCursor<'mir, 'tcx, BD> =
DataflowResultsCursor<'mir, 'tcx, BD, &'mir DataflowResults<'tcx, BD>>;
impl<'mir, 'tcx, BD, DR> DataflowResultsCursor<'mir, 'tcx, BD, DR>
where
BD: BitDenotation<'tcx>,
DR: Borrow<DataflowResults<'tcx, BD>>,
{
pub fn new(result: DR, body: &'mir Body<'tcx>) -> Self {
DataflowResultsCursor { flow_state: FlowAtLocation::new(result), curr_loc: None, body }
}
/// Seek to the given location in MIR. This method is fast if you are
/// traversing your MIR statements in order.
///
/// After calling `seek`, the current state will reflect all effects up to
/// and including the `before_statement_effect` of the statement at location
/// `loc`. The `statement_effect` of the statement at `loc` will be
/// available as the current effect (see e.g. `each_gen_bit`).
///
/// If `loc.statement_index` equals the number of statements in the block,
/// we will reconstruct the terminator effect in the same way as described
/// above.
pub fn seek(&mut self, loc: Location) {
if self.curr_loc.map(|cur| loc == cur).unwrap_or(false) {
return;
}
let start_index;
let should_reset = match self.curr_loc {
None => true,
Some(cur) if loc.block != cur.block || loc.statement_index < cur.statement_index => {
true
}
_ => false,
};
if should_reset {
self.flow_state.reset_to_entry_of(loc.block);
start_index = 0;
} else {
let curr_loc = self.curr_loc.unwrap();
start_index = curr_loc.statement_index;
// Apply the effect from the last seek to the current state.
self.flow_state.apply_local_effect(curr_loc);
}
for stmt in start_index..loc.statement_index {
let mut stmt_loc = loc;
stmt_loc.statement_index = stmt;
self.flow_state.reconstruct_statement_effect(stmt_loc);
self.flow_state.apply_local_effect(stmt_loc);
}
if loc.statement_index == self.body[loc.block].statements.len() {
self.flow_state.reconstruct_terminator_effect(loc);
} else {
self.flow_state.reconstruct_statement_effect(loc);
}
self.curr_loc = Some(loc);
}
/// Return whether the current state contains bit `x`.
pub fn contains(&self, x: BD::Idx) -> bool {
self.flow_state.contains(x)
}
/// Iterate over each `gen` bit in the current effect (invoke `seek` first).
pub fn each_gen_bit<F>(&self, f: F)
where
F: FnMut(BD::Idx),
{
self.flow_state.each_gen_bit(f)
}
pub fn get(&self) -> &BitSet<BD::Idx> {
self.flow_state.as_dense()
}
}
pub struct DataflowAnalysis<'a, 'tcx, O>
where
O: BitDenotation<'tcx>,
{
flow_state: DataflowState<'tcx, O>,
dead_unwinds: &'a BitSet<mir::BasicBlock>,
body: &'a Body<'tcx>,
}
impl<'a, 'tcx, O> DataflowAnalysis<'a, 'tcx, O>
where
O: BitDenotation<'tcx>,
{
pub fn results(self) -> DataflowResults<'tcx, O> {
DataflowResults(self.flow_state)
}
pub fn body(&self) -> &'a Body<'tcx> {
self.body
}
}
pub struct DataflowResults<'tcx, O>(pub(crate) DataflowState<'tcx, O>)
where
O: BitDenotation<'tcx>;
impl<'tcx, O: BitDenotation<'tcx>> DataflowResults<'tcx, O> {
pub fn sets(&self) -> &AllSets<O::Idx> {
&self.0.sets
}
pub fn operator(&self) -> &O {
&self.0.operator
}
}
/// State of a dataflow analysis; couples a collection of bit sets
/// with operator used to initialize and merge bits during analysis.
pub struct DataflowState<'tcx, O: BitDenotation<'tcx>> {
/// All the sets for the analysis. (Factored into its
/// own structure so that we can borrow it mutably
/// on its own separate from other fields.)
pub sets: AllSets<O::Idx>,
/// operator used to initialize, combine, and interpret bits.
pub(crate) operator: O,
}
impl<'tcx, O: BitDenotation<'tcx>> DataflowState<'tcx, O> {
pub(crate) fn interpret_set<'c, P>(
&self,
o: &'c O,
set: &BitSet<O::Idx>,
render_idx: &P,
) -> Vec<DebugFormatted>
where
P: Fn(&O, O::Idx) -> DebugFormatted,
{
set.iter().map(|i| render_idx(o, i)).collect()
}
pub(crate) fn interpret_hybrid_set<'c, P>(
&self,
o: &'c O,
set: &HybridBitSet<O::Idx>,
render_idx: &P,
) -> Vec<DebugFormatted>
where
P: Fn(&O, O::Idx) -> DebugFormatted,
{
set.iter().map(|i| render_idx(o, i)).collect()
}
}
/// A 2-tuple representing the "gen" and "kill" bitsets during
/// dataflow analysis.
///
/// It is best to ensure that the intersection of `gen_set` and
/// `kill_set` is empty; otherwise the results of the dataflow will
/// have a hidden dependency on what order the bits are generated and
/// killed during the iteration. (This is such a good idea that the
/// `fn gen` and `fn kill` methods that set their state enforce this
/// for you.)
#[derive(Debug, Clone, Copy)]
pub struct GenKill<T> {
pub(crate) gen_set: T,
pub(crate) kill_set: T,
}
pub type GenKillSet<T> = GenKill<HybridBitSet<T>>;
impl<T> GenKill<T> {
/// Creates a new tuple where `gen_set == kill_set == elem`.
pub(crate) fn from_elem(elem: T) -> Self
where
T: Clone,
{
GenKill { gen_set: elem.clone(), kill_set: elem }
}
}
impl<E: Idx> GenKillSet<E> {
pub fn clear(&mut self) {
self.gen_set.clear();
self.kill_set.clear();
}
pub fn gen(&mut self, e: E) {
self.gen_set.insert(e);
self.kill_set.remove(e);
}
pub fn gen_all(&mut self, i: impl IntoIterator<Item: Borrow<E>>) {
for j in i {
self.gen(*j.borrow());
}
}
pub fn kill(&mut self, e: E) {
self.gen_set.remove(e);
self.kill_set.insert(e);
}
pub fn kill_all(&mut self, i: impl IntoIterator<Item: Borrow<E>>) {
for j in i {
self.kill(*j.borrow());
}
}
/// Computes `(set gen) - kill` and assigns the result to `set`.
pub(crate) fn apply(&self, set: &mut BitSet<E>) {
set.union(&self.gen_set);
set.subtract(&self.kill_set);
}
}
#[derive(Debug)]
pub struct AllSets<E: Idx> {
/// Analysis bitwidth for each block.
bits_per_block: usize,
/// For each block, bits valid on entry to the block.
on_entry: Vec<BitSet<E>>,
/// The transfer function of each block expressed as the set of bits
/// generated and killed by executing the statements + terminator in the
/// block -- with one caveat. In particular, for *call terminators*, the
/// effect of storing the destination is not included, since that only takes
/// effect on the **success** edge (and not the unwind edge).
trans: Vec<GenKillSet<E>>,
}
impl<E: Idx> AllSets<E> {
pub fn bits_per_block(&self) -> usize {
self.bits_per_block
}
pub fn get_mut(&mut self, block_idx: usize) -> (&mut BitSet<E>, &mut GenKillSet<E>) {
(&mut self.on_entry[block_idx], &mut self.trans[block_idx])
}
pub fn trans_for(&self, block_idx: usize) -> &GenKillSet<E> {
&self.trans[block_idx]
}
pub fn trans_mut_for(&mut self, block_idx: usize) -> &mut GenKillSet<E> {
&mut self.trans[block_idx]
}
pub fn entry_set_for(&self, block_idx: usize) -> &BitSet<E> {
&self.on_entry[block_idx]
}
pub fn entry_set_mut_for(&mut self, block_idx: usize) -> &mut BitSet<E> {
&mut self.on_entry[block_idx]
}
pub fn gen_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
&self.trans_for(block_idx).gen_set
}
pub fn kill_set_for(&self, block_idx: usize) -> &HybridBitSet<E> {
&self.trans_for(block_idx).kill_set
}
}
/// Parameterization for the precise form of data flow that is used.
///
/// `BottomValue` determines whether the initial entry set for each basic block is empty or full.
/// This also determines the semantics of the lattice `join` operator used to merge dataflow
/// results, since dataflow works by starting at the bottom and moving monotonically to a fixed
/// point.
///
/// This means, for propagation across the graph, that you either want to start at all-zeroes and
/// then use Union as your merge when propagating, or you start at all-ones and then use Intersect
/// as your merge when propagating.
pub trait BottomValue {
/// Specifies the initial value for each bit in the entry set for each basic block.
const BOTTOM_VALUE: bool;
/// Merges `in_set` into `inout_set`, returning `true` if `inout_set` changed.
///
/// It is almost certainly wrong to override this, since it automatically applies
/// * `inout_set & in_set` if `BOTTOM_VALUE == true`
/// * `inout_set | in_set` if `BOTTOM_VALUE == false`
///
/// This means that if a bit is not `BOTTOM_VALUE`, it is propagated into all target blocks.
/// For clarity, the above statement again from a different perspective:
/// A bit in the block's entry set is `!BOTTOM_VALUE` if *any* predecessor block's bit value is
/// `!BOTTOM_VALUE`.
///
/// There are situations where you want the opposite behaviour: propagate only if *all*
/// predecessor blocks's value is `!BOTTOM_VALUE`.
/// E.g. if you want to know whether a bit is *definitely* set at a specific location. This
/// means that all code paths leading to the location must have set the bit, instead of any
/// code path leading there.
///
/// If you want this kind of "definitely set" analysis, you need to
/// 1. Invert `BOTTOM_VALUE`
/// 2. Reset the `entry_set` in `start_block_effect` to `!BOTTOM_VALUE`
/// 3. Override `join` to do the opposite from what it's doing now.
#[inline]
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
if !Self::BOTTOM_VALUE { inout_set.union(in_set) } else { inout_set.intersect(in_set) }
}
}
/// A specific flavor of dataflow analysis.
///
/// To run a dataflow analysis, one sets up an initial state for the
/// `START_BLOCK` via `start_block_effect` and a transfer function (`trans`)
/// for each block individually. The entry set for all other basic blocks is
/// initialized to `Self::BOTTOM_VALUE`. The dataflow analysis then
/// iteratively modifies the various entry sets (but leaves the the transfer
/// function unchanged). `BottomValue::join` is used to merge the bitsets from
/// two blocks (e.g. when two blocks' terminator jumps to a single block, that
/// target block's state is the merged state of both incoming blocks).
pub trait BitDenotation<'tcx>: BottomValue {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
/// A name describing the dataflow analysis that this
/// `BitDenotation` is supporting. The name should be something
/// suitable for plugging in as part of a filename (i.e., avoid
/// space-characters or other things that tend to look bad on a
/// file system, like slashes or periods). It is also better for
/// the name to be reasonably short, again because it will be
/// plugged into a filename.
fn name() -> &'static str;
/// Size of each bitvector allocated for each block in the analysis.
fn bits_per_block(&self) -> usize;
/// Mutates the entry set according to the effects that
/// have been established *prior* to entering the start
/// block. This can't access the gen/kill sets, because
/// these won't be accounted for correctly.
///
/// (For example, establishing the call arguments.)
fn start_block_effect(&self, entry_set: &mut BitSet<Self::Idx>);
/// Similar to `statement_effect`, except it applies
/// *just before* the statement rather than *just after* it.
///
/// This matters for "dataflow at location" APIs, because the
/// before-statement effect is visible while visiting the
/// statement, while the after-statement effect only becomes
/// visible at the next statement.
///
/// Both the before-statement and after-statement effects are
/// applied, in that order, before moving for the next
/// statement.
fn before_statement_effect(&self, _trans: &mut GenKillSet<Self::Idx>, _location: Location) {}
/// Mutates the block-sets (the flow sets for the given
/// basic block) according to the effects of evaluating statement.
///
/// This is used, in particular, for building up the
/// "transfer-function" representing the overall-effect of the
/// block, represented via GEN and KILL sets.
///
/// The statement is identified as `bb_data[idx_stmt]`, where
/// `bb_data` is the sequence of statements identified by `bb` in
/// the MIR.
fn statement_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location);
/// Similar to `terminator_effect`, except it applies
/// *just before* the terminator rather than *just after* it.
///
/// This matters for "dataflow at location" APIs, because the
/// before-terminator effect is visible while visiting the
/// terminator, while the after-terminator effect only becomes
/// visible at the terminator's successors.
///
/// Both the before-terminator and after-terminator effects are
/// applied, in that order, before moving for the next
/// terminator.
fn before_terminator_effect(&self, _trans: &mut GenKillSet<Self::Idx>, _location: Location) {}
/// Mutates the block-sets (the flow sets for the given
/// basic block) according to the effects of evaluating
/// the terminator.
///
/// This is used, in particular, for building up the
/// "transfer-function" representing the overall-effect of the
/// block, represented via GEN and KILL sets.
///
/// The effects applied here cannot depend on which branch the
/// terminator took.
fn terminator_effect(&self, trans: &mut GenKillSet<Self::Idx>, location: Location);
/// Mutates the block-sets according to the (flow-dependent)
/// effect of a successful return from a Call terminator.
///
/// If basic-block BB_x ends with a call-instruction that, upon
/// successful return, flows to BB_y, then this method will be
/// called on the exit flow-state of BB_x in order to set up the
/// entry flow-state of BB_y.
///
/// This is used, in particular, as a special case during the
/// "propagate" loop where all of the basic blocks are repeatedly
/// visited. Since the effects of a Call terminator are
/// flow-dependent, the current MIR cannot encode them via just
/// GEN and KILL sets attached to the block, and so instead we add
/// this extra machinery to represent the flow-dependent effect.
//
// FIXME: right now this is a bit of a wart in the API. It might
// be better to represent this as an additional gen- and
// kill-sets associated with each edge coming out of the basic
// block.
fn propagate_call_return(
&self,
in_out: &mut BitSet<Self::Idx>,
call_bb: mir::BasicBlock,
dest_bb: mir::BasicBlock,
dest_place: &mir::Place<'tcx>,
);
}
impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D>
where
D: BitDenotation<'tcx>,
{
pub fn new(
body: &'a Body<'tcx>,
dead_unwinds: &'a BitSet<mir::BasicBlock>,
denotation: D,
) -> Self {
let bits_per_block = denotation.bits_per_block();
let num_blocks = body.basic_blocks().len();
let on_entry = if D::BOTTOM_VALUE {
vec![BitSet::new_filled(bits_per_block); num_blocks]
} else {
vec![BitSet::new_empty(bits_per_block); num_blocks]
};
let nop = GenKill::from_elem(HybridBitSet::new_empty(bits_per_block));
DataflowAnalysis {
body,
dead_unwinds,
flow_state: DataflowState {
sets: AllSets { bits_per_block, on_entry, trans: vec![nop; num_blocks] },
operator: denotation,
},
}
}
}
impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D>
where
D: BitDenotation<'tcx>,
{
/// Propagates the bits of `in_out` into all the successors of `bb`,
/// using bitwise operator denoted by `self.operator`.
///
/// For most blocks, this is entirely uniform. However, for blocks
/// that end with a call terminator, the effect of the call on the
/// dataflow state may depend on whether the call returned
/// successfully or unwound.
///
/// To reflect this, the `propagate_call_return` method of the
/// `BitDenotation` mutates `in_out` when propagating `in_out` via
/// a call terminator; such mutation is performed *last*, to
/// ensure its side-effects do not leak elsewhere (e.g., into
/// unwind target).
fn propagate_bits_into_graph_successors_of(
&mut self,
in_out: &mut BitSet<D::Idx>,
(bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData<'tcx>),
dirty_list: &mut WorkQueue<mir::BasicBlock>,
) {
match bb_data.terminator().kind {
mir::TerminatorKind::Return
| mir::TerminatorKind::Resume
| mir::TerminatorKind::Abort
| mir::TerminatorKind::GeneratorDrop
| mir::TerminatorKind::Unreachable => {}
mir::TerminatorKind::Goto { target }
| mir::TerminatorKind::Assert { target, cleanup: None, .. }
| mir::TerminatorKind::Yield { resume: target, drop: None, .. }
| mir::TerminatorKind::Drop { target, location: _, unwind: None }
| mir::TerminatorKind::DropAndReplace { target, value: _, location: _, unwind: None } =>
{
self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
}
mir::TerminatorKind::Yield { resume: target, drop: Some(drop), .. } => {
self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
self.propagate_bits_into_entry_set_for(in_out, drop, dirty_list);
}
mir::TerminatorKind::Assert { target, cleanup: Some(unwind), .. }
| mir::TerminatorKind::Drop { target, location: _, unwind: Some(unwind) }
| mir::TerminatorKind::DropAndReplace {
target,
value: _,
location: _,
unwind: Some(unwind),
} => {
self.propagate_bits_into_entry_set_for(in_out, target, dirty_list);
if !self.dead_unwinds.contains(bb) {
self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
}
}
mir::TerminatorKind::SwitchInt { ref targets, .. } => {
for target in targets {
self.propagate_bits_into_entry_set_for(in_out, *target, dirty_list);
}
}
mir::TerminatorKind::Call { cleanup, ref destination, .. } => {
if let Some(unwind) = cleanup {
if !self.dead_unwinds.contains(bb) {
self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
}
}
if let Some((ref dest_place, dest_bb)) = *destination {
// N.B.: This must be done *last*, after all other
// propagation, as documented in comment above.
self.flow_state.operator.propagate_call_return(in_out, bb, dest_bb, dest_place);
self.propagate_bits_into_entry_set_for(in_out, dest_bb, dirty_list);
}
}
mir::TerminatorKind::FalseEdges { real_target, imaginary_target } => {
self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
self.propagate_bits_into_entry_set_for(in_out, imaginary_target, dirty_list);
}
mir::TerminatorKind::FalseUnwind { real_target, unwind } => {
self.propagate_bits_into_entry_set_for(in_out, real_target, dirty_list);
if let Some(unwind) = unwind {
if !self.dead_unwinds.contains(bb) {
self.propagate_bits_into_entry_set_for(in_out, unwind, dirty_list);
}
}
}
}
}
fn propagate_bits_into_entry_set_for(
&mut self,
in_out: &BitSet<D::Idx>,
bb: mir::BasicBlock,
dirty_queue: &mut WorkQueue<mir::BasicBlock>,
) {
let entry_set = self.flow_state.sets.entry_set_mut_for(bb.index());
let set_changed = self.flow_state.operator.join(entry_set, &in_out);
if set_changed {
dirty_queue.insert(bb);
}
}
}

View File

@ -9,7 +9,7 @@ use rustc_index::bit_set::BitSet;
use std::marker::PhantomData;
use super::{qualifs, Item, Qualif};
use crate::dataflow::{self as old_dataflow, generic as dataflow};
use crate::dataflow;
/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
/// `FlowSensitiveAnalysis`.
@ -165,7 +165,7 @@ where
}
}
impl<Q> old_dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
impl<Q> dataflow::BottomValue for FlowSensitiveAnalysis<'_, '_, '_, Q> {
const BOTTOM_VALUE: bool = false;
}

View File

@ -22,8 +22,8 @@ use super::qualifs::{self, HasMutInterior, NeedsDrop};
use super::resolver::FlowSensitiveAnalysis;
use super::{is_lang_panic_fn, ConstKind, Item, Qualif};
use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::dataflow::generic::{self as dataflow, Analysis};
use crate::dataflow::MaybeMutBorrowedLocals;
use crate::dataflow::{self, Analysis};
// We are using `MaybeMutBorrowedLocals` as a proxy for whether an item may have been mutated
// through a pointer prior to the given point. This is okay even though `MaybeMutBorrowedLocals`

View File

@ -1,9 +1,9 @@
use crate::dataflow;
use crate::dataflow::generic::{Analysis, ResultsCursor};
use crate::dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
use crate::dataflow::on_lookup_result_bits;
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits};
use crate::dataflow::{Analysis, ResultsCursor};
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use crate::transform::{MirPass, MirSource};
use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind};

View File

@ -49,7 +49,7 @@
//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
use crate::dataflow::generic::{self as dataflow, Analysis};
use crate::dataflow::{self, Analysis};
use crate::dataflow::{MaybeBorrowedLocals, MaybeRequiresStorage, MaybeStorageLive};
use crate::transform::no_landing_pads::no_landing_pads;
use crate::transform::simplify;

View File

@ -9,11 +9,11 @@ use rustc::ty::{self, Ty, TyCtxt};
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::BitSet;
use crate::dataflow::generic::{Analysis, Results, ResultsCursor};
use crate::dataflow::move_paths::{HasMoveData, MoveData};
use crate::dataflow::move_paths::{LookupResult, MovePathIndex};
use crate::dataflow::MaybeMutBorrowedLocals;
use crate::dataflow::MoveDataParamEnv;
use crate::dataflow::{Analysis, Results, ResultsCursor};
use crate::dataflow::{
DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};

View File

@ -903,10 +903,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode {
debug!(
"compute: using id for body, {}",
self.ir.tcx.hir().hir_to_pretty_string(body.hir_id)
);
debug!("compute: using id for body, {:?}", body);
// the fallthrough exit is only for those cases where we do not
// explicitly return:
@ -979,7 +976,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}
fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
debug!("propagate_through_expr: {}", self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id));
debug!("propagate_through_expr: {:?}", expr);
match expr.kind {
// Interesting cases with control flow or which gen/kill
@ -990,10 +987,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ),
hir::ExprKind::Closure(..) => {
debug!(
"{} is an ExprKind::Closure",
self.ir.tcx.hir().hir_to_pretty_string(expr.hir_id)
);
debug!("{:?} is an ExprKind::Closure", expr);
// the construction of a closure itself is not important,
// but we have to consider the closed over variables.
@ -1344,11 +1338,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let mut first_merge = true;
let ln = self.live_node(expr.hir_id, expr.span);
self.init_empty(ln, succ);
debug!(
"propagate_through_loop: using id for loop body {} {}",
expr.hir_id,
self.ir.tcx.hir().hir_to_pretty_string(body.hir_id)
);
debug!("propagate_through_loop: using id for loop body {} {:?}", expr.hir_id, body);
self.break_ln.insert(expr.hir_id, succ);

View File

@ -1320,14 +1320,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
let is_local_static =
if let DefKind::Static = kind { def_id.is_local() } else { false };
if !self.item_is_accessible(def_id) && !is_local_static {
let name = match *qpath {
hir::QPath::Resolved(_, ref path) => path.to_string(),
hir::QPath::TypeRelative(_, ref segment) => segment.ident.to_string(),
let sess = self.tcx.sess;
let sm = sess.source_map();
let name = match qpath {
hir::QPath::Resolved(_, path) => sm.span_to_snippet(path.span).ok(),
hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()),
};
let kind = kind.descr(def_id);
self.tcx
.sess
.struct_span_err(span, &format!("{} `{}` is private", kind, name))
let msg = match name {
Some(name) => format!("{} `{}` is private", kind, name),
None => format!("{} is private", kind),
};
sess.struct_span_err(span, &msg)
.span_label(span, &format!("private {}", kind))
.emit();
return;

View File

@ -11,13 +11,14 @@ path = "lib.rs"
[dependencies]
log = "0.4"
rustc = { path = "../librustc" }
rustc_ast = { path = "../librustc_ast" }
rustc_ast_pretty = { path = "../librustc_ast_pretty" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_session = { path = "../librustc_session" }
rustc_hir = { path = "../librustc_hir" }
rustc_hir_pretty = { path = "../librustc_hir_pretty" }
rustc_parse = { path = "../librustc_parse" }
serde_json = "1"
rustc_ast = { path = "../librustc_ast" }
rustc_session = { path = "../librustc_session" }
rustc_span = { path = "../librustc_span" }
rls-data = "0.19"
rls-span = "0.5"

View File

@ -404,14 +404,15 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) {
Some(Node::Item(item)) => match item.kind {
hir::ItemKind::Impl { ref self_ty, .. } => {
let hir = self.tcx.hir();
let mut qualname = String::from("<");
qualname.push_str(&self.tcx.hir().hir_to_pretty_string(self_ty.hir_id));
qualname.push_str(&rustc_hir_pretty::id_to_string(&hir, self_ty.hir_id));
let trait_id = self.tcx.trait_id_of_impl(impl_id);
let mut docs = String::new();
let mut attrs = vec![];
let hir_id = self.tcx.hir().node_to_hir_id(id);
if let Some(Node::ImplItem(item)) = self.tcx.hir().find(hir_id) {
if let Some(Node::ImplItem(item)) = hir.find(hir.node_to_hir_id(id)) {
docs = self.docs_for_attrs(&item.attrs);
attrs = item.attrs.to_vec();
}

View File

@ -1581,7 +1581,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
for param in generics.params {
if param.span == *span
&& !param.bounds.iter().any(|bound| {
bound.trait_def_id() == self.tcx.lang_items().sized_trait()
bound.trait_ref().and_then(|trait_ref| trait_ref.trait_def_id())
== self.tcx.lang_items().sized_trait()
})
{
let (span, separator) = match param.bounds {

View File

@ -15,7 +15,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{self, Obligation, ObligationCause};
use rustc::ty::subst::{InternalSubsts, Subst};
use rustc::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
use rustc_errors::Applicability;
use rustc_errors::{Applicability, FatalError};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
@ -170,6 +170,24 @@ fn object_safety_violations_for_trait(
violations
}
fn sized_trait_bound_spans<'tcx>(
tcx: TyCtxt<'tcx>,
bounds: hir::GenericBounds<'tcx>,
) -> impl 'tcx + Iterator<Item = Span> {
bounds.iter().filter_map(move |b| match b {
hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None)
if trait_has_sized_self(
tcx,
trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
) =>
{
// Fetch spans for supertraits that are `Sized`: `trait T: Super`
Some(trait_ref.span)
}
_ => None,
})
}
fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> {
tcx.hir()
.get_if_local(trait_def_id)
@ -189,33 +207,14 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]>
{
// Fetch spans for trait bounds that are Sized:
// `trait T where Self: Pred`
Some(pred.bounds.iter().filter_map(|b| match b {
hir::GenericBound::Trait(
trait_ref,
hir::TraitBoundModifier::None,
) if trait_has_sized_self(
tcx,
trait_ref.trait_ref.trait_def_id(),
) =>
{
Some(trait_ref.span)
}
_ => None,
}))
Some(sized_trait_bound_spans(tcx, pred.bounds))
}
_ => None,
}
})
.flatten()
.chain(bounds.iter().filter_map(|b| match b {
hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None)
if trait_has_sized_self(tcx, trait_ref.trait_ref.trait_def_id()) =>
{
// Fetch spans for supertraits that are `Sized`: `trait T: Super`
Some(trait_ref.span)
}
_ => None,
}))
// Fetch spans for supertraits that are `Sized`: `trait T: Super`.
.chain(sized_trait_bound_spans(tcx, bounds))
.collect::<SmallVec<[Span; 1]>>(),
),
_ => None,

View File

@ -16,12 +16,11 @@ use rustc::ty::{GenericParamDef, GenericParamDefKind};
use rustc_ast::ast;
use rustc_ast::util::lev_distance::find_best_match_for_name;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, FatalError};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_generics, Visitor};
use rustc_hir::print;
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{Constness, GenericArg, GenericArgs};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS};
use rustc_session::parse::feature_err;
@ -991,7 +990,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.ast_path_to_mono_trait_ref(
trait_ref.path.span,
trait_ref.trait_def_id(),
trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
self_ty,
trait_ref.path.segments.last().unwrap(),
)
@ -1007,7 +1006,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
bounds: &mut Bounds<'tcx>,
speculative: bool,
) -> Result<(), GenericArgCountMismatch> {
let trait_def_id = trait_ref.trait_def_id();
let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
@ -1118,6 +1117,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
if !self.tcx().features().unboxed_closures
&& trait_segment.generic_args().parenthesized != trait_def.paren_sugar
{
let sess = &self.tcx().sess.parse_sess;
// For now, require that parenthetical notation be used only with `Fn()` etc.
let (msg, sugg) = if trait_def.paren_sugar {
(
@ -1132,7 +1132,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.and_then(|args| args.args.get(0))
.and_then(|arg| match arg {
hir::GenericArg::Type(ty) => {
Some(print::to_string(print::NO_ANN, |s| s.print_type(ty)))
sess.source_map().span_to_snippet(ty.span).ok()
}
_ => None,
})
@ -1143,7 +1143,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.iter()
.filter_map(|b| match (b.ident.as_str() == "Output", &b.kind) {
(true, hir::TypeBindingKind::Equality { ty }) => {
Some(print::to_string(print::NO_ANN, |s| s.print_type(ty)))
sess.source_map().span_to_snippet(ty.span).ok()
}
_ => None,
})
@ -1154,7 +1154,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
} else {
("parenthetical notation is only stable when used with `Fn`-family traits", None)
};
let sess = &self.tcx().sess.parse_sess;
let mut err = feature_err(sess, sym::unboxed_closures, span, msg);
if let Some(sugg) = sugg {
let msg = "use parenthetical notation instead";

View File

@ -245,11 +245,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
// check that the `if` expr without `else` is the fn body's expr
if expr.span == span {
return self.get_fn_decl(hir_id).map(|(fn_decl, _)| {
(
fn_decl.output.span(),
format!("expected `{}` because of this return type", fn_decl.output),
)
return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| {
let span = fn_decl.output.span();
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
Some((span, format!("expected `{}` because of this return type", snippet)))
});
}
}

View File

@ -265,7 +265,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let &ty::Adt(adt_def, ..) = t {
if adt_def.is_enum() {
if let hir::ExprKind::Call(ref expr, _) = call_expr.kind {
unit_variant = Some(self.tcx.hir().hir_to_pretty_string(expr.hir_id))
unit_variant =
self.tcx.sess.source_map().span_to_snippet(expr.span).ok();
}
}
}
@ -335,16 +336,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(call_expr.span, "call expression requires function");
if let Some(span) = self.tcx.hir().res_span(def) {
let callee_ty = callee_ty.to_string();
let label = match (unit_variant, inner_callee_path) {
(Some(path), _) => format!("`{}` defined here", path),
(_, Some(hir::QPath::Resolved(_, path))) => format!(
"`{}` defined here returns `{}`",
path,
callee_ty.to_string()
),
_ => format!("`{}` defined here", callee_ty.to_string()),
(Some(path), _) => Some(format!("`{}` defined here", path)),
(_, Some(hir::QPath::Resolved(_, path))) => {
self.tcx.sess.source_map().span_to_snippet(path.span).ok().map(
|p| format!("`{}` defined here returns `{}`", p, callee_ty),
)
}
_ => Some(format!("`{}` defined here", callee_ty)),
};
err.span_label(span, label);
if let Some(label) = label {
err.span_label(span, label);
}
}
err.emit();
} else {

View File

@ -1402,9 +1402,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
{
// Are of this `impl Trait`'s traits object safe?
is_object_safe = bounds.iter().all(|bound| {
bound.trait_def_id().map_or(false, |def_id| {
fcx.tcx.object_safety_violations(def_id).is_empty()
})
bound
.trait_ref()
.and_then(|t| t.trait_def_id())
.map_or(false, |def_id| {
fcx.tcx.object_safety_violations(def_id).is_empty()
})
})
}
}

View File

@ -9,7 +9,7 @@ use rustc::ty::{self, AssocItem, Ty};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, print, Node};
use rustc_hir::{is_range_literal, Node};
use rustc_span::symbol::sym;
use rustc_span::Span;
@ -198,13 +198,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.peekable();
if compatible_variants.peek().is_some() {
let expr_text =
self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_else(|_| {
print::to_string(print::NO_ANN, |s| s.print_expr(expr))
});
let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
let msg = "try using a variant of the expected enum";
err.span_suggestions(expr.span, msg, suggestions, Applicability::MaybeIncorrect);
if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
let msg = "try using a variant of the expected enum";
err.span_suggestions(
expr.span,
msg,
suggestions,
Applicability::MaybeIncorrect,
);
}
}
}
}

View File

@ -475,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tcx.types.err
}
Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => {
report_unexpected_variant_res(tcx, res, expr.span, qpath);
report_unexpected_variant_res(tcx, res, expr.span);
tcx.types.err
}
_ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
@ -696,10 +696,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self,
&cause,
&mut |db| {
db.span_label(
fn_decl.output.span(),
format!("expected `{}` because of this return type", fn_decl.output,),
);
let span = fn_decl.output.span();
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
db.span_label(
span,
format!("expected `{}` because of this return type", snippet),
);
}
},
true,
);
@ -1668,20 +1671,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let (Some(len), Ok(user_index)) =
(len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
{
let base = self
.tcx
.sess
.source_map()
.span_to_snippet(base.span)
.unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id));
let help = "instead of using tuple indexing, use array indexing";
let suggestion = format!("{}[{}]", base, field);
let applicability = if len < user_index {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
err.span_suggestion(expr.span, help, suggestion, applicability);
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
let help = "instead of using tuple indexing, use array indexing";
let suggestion = format!("{}[{}]", base, field);
let applicability = if len < user_index {
Applicability::MachineApplicable
} else {
Applicability::MaybeIncorrect
};
err.span_suggestion(expr.span, help, suggestion, applicability);
}
}
}
@ -1692,15 +1691,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base: &hir::Expr<'_>,
field: ast::Ident,
) {
let base = self
.tcx
.sess
.source_map()
.span_to_snippet(base.span)
.unwrap_or_else(|_| self.tcx.hir().hir_to_pretty_string(base.hir_id));
let msg = format!("`{}` is a raw pointer; try dereferencing it", base);
let suggestion = format!("(*{}).{}", base, field);
err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect);
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
let msg = format!("`{}` is a raw pointer; try dereferencing it", base);
let suggestion = format!("(*{}).{}", base, field);
err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect);
}
}
fn no_such_field_err<T: Display>(

View File

@ -1061,7 +1061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let trait_def_ids: FxHashSet<DefId> = param
.bounds
.iter()
.filter_map(|bound| bound.trait_def_id())
.filter_map(|bound| Some(bound.trait_ref()?.trait_def_id()?))
.collect();
if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
err.span_suggestions(

View File

@ -2677,14 +2677,14 @@ pub fn check_enum<'tcx>(
check_transparent(tcx, sp, def_id);
}
fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span, qpath: &QPath<'_>) {
fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, span: Span) {
struct_span_err!(
tcx.sess,
span,
E0533,
"expected unit struct, unit variant or constant, found {} `{}`",
"expected unit struct, unit variant or constant, found {}{}",
res.descr(),
hir::print::to_string(&tcx.hir(), |s| s.print_qpath(qpath, false))
tcx.sess.source_map().span_to_snippet(span).map_or(String::new(), |s| format!(" `{}`", s)),
)
.emit();
}

View File

@ -492,36 +492,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(span, ty.to_string());
if let FnDef(def_id, _) = ty.kind {
let source_map = self.tcx.sess.source_map();
let hir_id = match self.tcx.hir().as_local_hir_id(def_id) {
Some(hir_id) => hir_id,
None => return false,
};
if !self.tcx.has_typeck_tables(def_id) {
return false;
}
let fn_sig = {
match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) {
Some(f) => *f,
None => {
bug!("No fn-sig entry for def_id={:?}", def_id);
}
}
};
// We're emitting a suggestion, so we can just ignore regions
let fn_sig = *self.tcx.fn_sig(def_id).skip_binder();
let other_ty = if let FnDef(def_id, _) = other_ty.kind {
let hir_id = match self.tcx.hir().as_local_hir_id(def_id) {
Some(hir_id) => hir_id,
None => return false,
};
if !self.tcx.has_typeck_tables(def_id) {
return false;
}
match self.tcx.typeck_tables_of(def_id).liberated_fn_sigs().get(hir_id) {
Some(f) => f.clone().output(),
None => {
bug!("No fn-sig entry for def_id={:?}", def_id);
}
}
// We're emitting a suggestion, so we can just ignore regions
self.tcx.fn_sig(def_id).skip_binder().output()
} else {
other_ty
};

View File

@ -171,9 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
}
PatKind::Path(ref qpath) => {
self.check_pat_path(pat, path_res.unwrap(), qpath, expected, ti)
}
PatKind::Path(_) => self.check_pat_path(pat, path_res.unwrap(), expected, ti),
PatKind::Struct(ref qpath, fields, etc) => {
self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
}
@ -694,7 +692,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
pat: &Pat<'_>,
path_resolution: (Res, Option<Ty<'tcx>>, &'b [hir::PathSegment<'b>]),
qpath: &hir::QPath<'_>,
expected: Ty<'tcx>,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
@ -707,17 +704,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.set_tainted_by_errors();
return tcx.types.err;
}
Res::Def(DefKind::AssocFn, _)
| Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _)
| Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => {
report_unexpected_variant_res(tcx, res, pat.span, qpath);
Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => {
report_unexpected_variant_res(tcx, res, pat.span);
return tcx.types.err;
}
Res::Def(DefKind::Ctor(_, CtorKind::Const), _)
| Res::SelfCtor(..)
| Res::Def(DefKind::Const, _)
| Res::Def(DefKind::AssocConst, _)
| Res::Def(DefKind::ConstParam, _) => {} // OK
Res::SelfCtor(..)
| Res::Def(
DefKind::Ctor(_, CtorKind::Const)
| DefKind::Const
| DefKind::AssocConst
| DefKind::ConstParam,
_,
) => {} // OK
_ => bug!("unexpected pattern resolution: {:?}", res),
}
@ -791,14 +789,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
let report_unexpected_res = |res: Res| {
let sm = tcx.sess.source_map();
let path_str = sm
.span_to_snippet(sm.span_until_char(pat.span, '('))
.map_or(String::new(), |s| format!(" `{}`", s.trim_end()));
let msg = format!(
"expected tuple struct or tuple variant, found {} `{}`",
"expected tuple struct or tuple variant, found {}{}",
res.descr(),
hir::print::to_string(&tcx.hir(), |s| s.print_qpath(qpath, false)),
path_str
);
let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{}", msg);
match (res, &pat.kind) {
(Res::Def(DefKind::Fn, _), _) | (Res::Def(DefKind::AssocFn, _), _) => {
match res {
Res::Def(DefKind::Fn | DefKind::AssocFn, _) => {
err.span_label(pat.span, "`fn` calls are not allowed in patterns");
err.help(
"for more information, visit \

View File

@ -5,7 +5,6 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::print::visibility_qualified;
use rustc_session::lint;
use rustc_span::Span;
@ -176,16 +175,13 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
None => format!("use {};", item.ident.name),
};
let replacement = visibility_qualified(&item.vis, base_replacement);
let msg = "`extern crate` is not idiomatic in the new edition";
let help = format!("convert it to a `{}`", visibility_qualified(&item.vis, "use"));
lint.build(msg)
let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default();
let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
lint.build("`extern crate` is not idiomatic in the new edition")
.span_suggestion_short(
extern_crate.span,
&help,
replacement,
&format!("convert it to a `{}`", add_vis("use".to_string())),
add_vis(base_replacement),
Applicability::MachineApplicable,
)
.emit();

View File

@ -2146,13 +2146,18 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
{
let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| {
if ty.is_simd() {
let snip = tcx
.sess
.source_map()
.span_to_snippet(ast_ty.span)
.map_or(String::new(), |s| format!(" `{}`", s));
tcx.sess
.struct_span_err(
ast_ty.span,
&format!(
"use of SIMD type `{}` in FFI is highly experimental and \
"use of SIMD type{} in FFI is highly experimental and \
may result in invalid code",
tcx.hir().hir_to_pretty_string(ast_ty.hir_id)
snip
),
)
.help("add `#![feature(simd_ffi)]` to the crate attributes to enable")

View File

@ -482,8 +482,8 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>)
}
pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String {
if let Some(node_id) = cx.tcx.hir().as_local_hir_id(did) {
cx.tcx.hir().hir_to_pretty_string(node_id)
if let Some(hir_id) = cx.tcx.hir().as_local_hir_id(did) {
rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id)
} else {
cx.tcx.rendered_const(did)
}

View File

@ -578,7 +578,7 @@ pub fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String {
None
};
snippet.unwrap_or_else(|| cx.tcx.hir().hir_to_pretty_string(body.hir_id))
snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&cx.tcx.hir(), body.hir_id))
}
/// Given a type Path, resolve it to a Type using the TyCtxt

View File

@ -26,6 +26,7 @@ extern crate rustc_errors;
extern crate rustc_expand;
extern crate rustc_feature;
extern crate rustc_hir;
extern crate rustc_hir_pretty;
extern crate rustc_index;
extern crate rustc_infer;
extern crate rustc_interface;

View File

@ -910,7 +910,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirCollector<'a, 'hir> {
fn visit_item(&mut self, item: &'hir hir::Item) {
let name = if let hir::ItemKind::Impl { ref self_ty, .. } = item.kind {
self.map.hir_to_pretty_string(self_ty.hir_id)
rustc_hir_pretty::id_to_string(&self.map, self_ty.hir_id)
} else {
item.ident.to_string()
};

View File

@ -379,6 +379,14 @@ impl ops::Index<ops::RangeFull> for OsString {
}
}
#[stable(feature = "mut_osstr", since = "1.44.0")]
impl ops::IndexMut<ops::RangeFull> for OsString {
#[inline]
fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr {
OsStr::from_inner_mut(self.inner.as_mut_slice())
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for OsString {
type Target = OsStr;
@ -389,6 +397,14 @@ impl ops::Deref for OsString {
}
}
#[stable(feature = "mut_osstr", since = "1.44.0")]
impl ops::DerefMut for OsString {
#[inline]
fn deref_mut(&mut self) -> &mut OsStr {
&mut self[..]
}
}
#[stable(feature = "osstring_default", since = "1.9.0")]
impl Default for OsString {
/// Constructs an empty `OsString`.
@ -509,9 +525,20 @@ impl OsStr {
#[inline]
fn from_inner(inner: &Slice) -> &OsStr {
// Safety: OsStr is just a wrapper of Slice,
// therefore converting &Slice to &OsStr is safe.
unsafe { &*(inner as *const Slice as *const OsStr) }
}
#[inline]
fn from_inner_mut(inner: &mut Slice) -> &mut OsStr {
// Safety: OsStr is just a wrapper of Slice,
// therefore converting &mut Slice to &mut OsStr is safe.
// Any method that mutates OsStr must be careful not to
// break platform-specific encoding, in particular Wtf8 on Windows.
unsafe { &mut *(inner as *mut Slice as *mut OsStr) }
}
/// Yields a [`&str`] slice if the `OsStr` is valid Unicode.
///
/// This conversion may entail doing a check for UTF-8 validity.

View File

@ -77,9 +77,21 @@ impl Buf {
}
pub fn as_slice(&self) -> &Slice {
// Safety: Slice is just a wrapper for Wtf8,
// and self.inner.as_slice() returns &Wtf8.
// Therefore, transmuting &Wtf8 to &Slice is safe.
unsafe { mem::transmute(self.inner.as_slice()) }
}
pub fn as_mut_slice(&mut self) -> &mut Slice {
// Safety: Slice is just a wrapper for Wtf8,
// and self.inner.as_mut_slice() returns &mut Wtf8.
// Therefore, transmuting &mut Wtf8 to &mut Slice is safe.
// Additionally, care should be taken to ensure the slice
// is always valid Wtf8.
unsafe { mem::transmute(self.inner.as_mut_slice()) }
}
pub fn into_string(self) -> Result<String, Buf> {
self.inner.into_string().map_err(|buf| Buf { inner: buf })
}

View File

@ -106,9 +106,20 @@ impl Buf {
#[inline]
pub fn as_slice(&self) -> &Slice {
// Safety: Slice just wraps [u8],
// and &*self.inner is &[u8], therefore
// transmuting &[u8] to &Slice is safe.
unsafe { mem::transmute(&*self.inner) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut Slice {
// Safety: Slice just wraps [u8],
// and &mut *self.inner is &mut [u8], therefore
// transmuting &mut [u8] to &mut Slice is safe.
unsafe { mem::transmute(&mut *self.inner) }
}
pub fn into_string(self) -> Result<String, Buf> {
String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() })
}

View File

@ -0,0 +1,16 @@
fn first() {
second == 1 //~ ERROR binary operation
//~^ ERROR mismatched types
}
fn second() {
first == 1 //~ ERROR binary operation
//~^ ERROR mismatched types
}
fn bar() {
bar == 1 //~ ERROR binary operation
//~^ ERROR mismatched types
}
fn main() {}

View File

@ -0,0 +1,55 @@
error[E0369]: binary operation `==` cannot be applied to type `fn() {second}`
--> $DIR/issue-66667-function-cmp-cycle.rs:2:12
|
LL | second == 1
| ------ ^^ - {integer}
| |
| fn() {second}
error[E0308]: mismatched types
--> $DIR/issue-66667-function-cmp-cycle.rs:2:15
|
LL | second == 1
| ^ expected fn item, found integer
|
= note: expected fn item `fn() {second}`
found type `{integer}`
error[E0369]: binary operation `==` cannot be applied to type `fn() {first}`
--> $DIR/issue-66667-function-cmp-cycle.rs:7:11
|
LL | first == 1
| ----- ^^ - {integer}
| |
| fn() {first}
error[E0308]: mismatched types
--> $DIR/issue-66667-function-cmp-cycle.rs:7:14
|
LL | first == 1
| ^ expected fn item, found integer
|
= note: expected fn item `fn() {first}`
found type `{integer}`
error[E0369]: binary operation `==` cannot be applied to type `fn() {bar}`
--> $DIR/issue-66667-function-cmp-cycle.rs:12:9
|
LL | bar == 1
| --- ^^ - {integer}
| |
| fn() {bar}
error[E0308]: mismatched types
--> $DIR/issue-66667-function-cmp-cycle.rs:12:12
|
LL | bar == 1
| ^ expected fn item, found integer
|
= note: expected fn item `fn() {bar}`
found type `{integer}`
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0308, E0369.
For more information about an error, try `rustc --explain E0308`.

View File

@ -0,0 +1,13 @@
fn a() {
[0; [|_: _ &_| ()].len()]
//~^ ERROR expected `,`, found `&`
//~| ERROR type annotations needed
//~| ERROR mismatched types
}
fn b() {
[0; [|f @ &ref _| {} ; 0 ].len() ];
//~^ ERROR expected identifier, found reserved identifier `_`
}
fn main() {}

View File

@ -0,0 +1,32 @@
error: expected `,`, found `&`
--> $DIR/issue-66706.rs:2:16
|
LL | [0; [|_: _ &_| ()].len()]
| -^ expected `,`
| |
| help: missing `,`
error: expected identifier, found reserved identifier `_`
--> $DIR/issue-66706.rs:9:20
|
LL | [0; [|f @ &ref _| {} ; 0 ].len() ];
| ^ expected identifier, found reserved identifier
error[E0282]: type annotations needed
--> $DIR/issue-66706.rs:2:11
|
LL | [0; [|_: _ &_| ()].len()]
| ^ consider giving this closure parameter a type
error[E0308]: mismatched types
--> $DIR/issue-66706.rs:2:5
|
LL | fn a() {
| - help: try adding a return type: `-> [{integer}; _]`
LL | [0; [|_: _ &_| ()].len()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found array `[{integer}; _]`
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.

View File

@ -4,13 +4,13 @@ error[E0533]: expected unit struct, unit variant or constant, found associated f
LL | Foo::bar => {}
| ^^^^^^^^
error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar`
error[E0533]: expected unit struct, unit variant or constant, found associated function `<Foo>::bar`
--> $DIR/method-path-in-pattern.rs:19:9
|
LL | <Foo>::bar => {}
| ^^^^^^^^^^
error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::trait_bar`
error[E0533]: expected unit struct, unit variant or constant, found associated function `<Foo>::trait_bar`
--> $DIR/method-path-in-pattern.rs:23:9
|
LL | <Foo>::trait_bar => {}
@ -22,7 +22,7 @@ error[E0533]: expected unit struct, unit variant or constant, found associated f
LL | if let Foo::bar = 0u32 {}
| ^^^^^^^^
error[E0533]: expected unit struct, unit variant or constant, found associated function `Foo::bar`
error[E0533]: expected unit struct, unit variant or constant, found associated function `<Foo>::bar`
--> $DIR/method-path-in-pattern.rs:28:12
|
LL | if let <Foo>::bar = 0u32 {}

View File

@ -21,9 +21,9 @@ mod priv_trait {
Pub.method();
//~^ ERROR type `for<'r> fn(&'r Self) {<Self as priv_trait::PrivTr>::method}` is private
<Pub as PrivTr>::CONST;
//~^ ERROR associated constant `PrivTr::CONST` is private
//~^ ERROR associated constant `<Pub as PrivTr>::CONST` is private
let _: <Pub as PrivTr>::AssocTy;
//~^ ERROR associated type `PrivTr::AssocTy` is private
//~^ ERROR associated type `<Pub as PrivTr>::AssocTy` is private
pub type InSignatureTy = <Pub as PrivTr>::AssocTy;
//~^ ERROR trait `priv_trait::PrivTr` is private
pub trait InSignatureTr: PrivTr {}
@ -115,7 +115,7 @@ mod priv_parent_substs {
<Priv as PubTr<_>>::CONST;
//~^ ERROR type `priv_parent_substs::Priv` is private
let _: <Pub as PubTr>::AssocTy; // FIXME no longer an error?!
let _: <Pub as PubTr>::AssocTy; // FIXME no longer an error?!
let _: <Pub as PubTr<_>>::AssocTy;
//~^ ERROR type `priv_parent_substs::Priv` is private
let _: <Priv as PubTr<_>>::AssocTy;

View File

@ -31,7 +31,7 @@ LL | priv_trait::mac!();
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: associated constant `PrivTr::CONST` is private
error: associated constant `<Pub as PrivTr>::CONST` is private
--> $DIR/associated-item-privacy-trait.rs:23:9
|
LL | <Pub as PrivTr>::CONST;
@ -42,7 +42,7 @@ LL | priv_trait::mac!();
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: associated type `PrivTr::AssocTy` is private
error: associated type `<Pub as PrivTr>::AssocTy` is private
--> $DIR/associated-item-privacy-trait.rs:25:16
|
LL | let _: <Pub as PrivTr>::AssocTy;

View File

@ -1,4 +1,4 @@
error[E0533]: expected unit struct, unit variant or constant, found associated function `<<S as Tr>::A>::f<u8>`
error[E0533]: expected unit struct, unit variant or constant, found associated function `<S as Tr>::A::f::<u8>`
--> $DIR/qualified-path-params.rs:20:9
|
LL | <S as Tr>::A::f::<u8> => {}