2019-02-08 14:53:55 +01:00
|
|
|
//! A classic liveness analysis based on dataflow over the AST. Computes,
|
2014-11-25 21:17:11 -05:00
|
|
|
//! for each local variable in a function, whether that variable is live
|
2019-02-08 14:53:55 +01:00
|
|
|
//! at a given point. Program execution points are identified by their
|
|
|
|
//! IDs.
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
|
|
|
//! # Basic idea
|
|
|
|
//!
|
2019-02-08 14:53:55 +01:00
|
|
|
//! The basic model is that each local variable is assigned an index. We
|
2014-11-25 21:17:11 -05:00
|
|
|
//! represent sets of local variables using a vector indexed by this
|
2019-02-08 14:53:55 +01:00
|
|
|
//! index. The value in the vector is either 0, indicating the variable
|
|
|
|
//! is dead, or the ID of an expression that uses the variable.
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
2019-02-08 14:53:55 +01:00
|
|
|
//! We conceptually walk over the AST in reverse execution order. If we
|
|
|
|
//! find a use of a variable, we add it to the set of live variables. If
|
2014-11-25 21:17:11 -05:00
|
|
|
//! we find an assignment to a variable, we remove it from the set of live
|
2019-02-08 14:53:55 +01:00
|
|
|
//! variables. When we have to merge two flows, we take the union of
|
|
|
|
//! those two flows -- if the variable is live on both paths, we simply
|
|
|
|
//! pick one ID. In the event of loops, we continue doing this until a
|
2014-11-25 21:17:11 -05:00
|
|
|
//! fixed point is reached.
|
|
|
|
//!
|
|
|
|
//! ## Checking initialization
|
|
|
|
//!
|
2019-02-08 14:53:55 +01:00
|
|
|
//! At the function entry point, all variables must be dead. If this is
|
|
|
|
//! not the case, we can report an error using the ID found in the set of
|
2014-11-25 21:17:11 -05:00
|
|
|
//! live variables, which identifies a use of the variable which is not
|
|
|
|
//! dominated by an assignment.
|
|
|
|
//!
|
|
|
|
//! ## Checking moves
|
|
|
|
//!
|
|
|
|
//! After each explicit move, the variable must be dead.
|
|
|
|
//!
|
|
|
|
//! ## Computing last uses
|
|
|
|
//!
|
|
|
|
//! Any use of the variable where the variable is dead afterwards is a
|
|
|
|
//! last use.
|
|
|
|
//!
|
|
|
|
//! # Implementation details
|
|
|
|
//!
|
|
|
|
//! The actual implementation contains two (nested) walks over the AST.
|
|
|
|
//! The outer walk has the job of building up the ir_maps instance for the
|
2019-02-08 14:53:55 +01:00
|
|
|
//! enclosing function. On the way down the tree, it identifies those AST
|
2014-11-25 21:17:11 -05:00
|
|
|
//! nodes and variable IDs that will be needed for the liveness analysis
|
2019-02-08 14:53:55 +01:00
|
|
|
//! and assigns them contiguous IDs. The liveness ID for an AST node is
|
|
|
|
//! called a `live_node` (it's a newtype'd `u32`) and the ID for a variable
|
|
|
|
//! is called a `variable` (another newtype'd `u32`).
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
|
|
|
//! On the way back up the tree, as we are about to exit from a function
|
2019-02-08 14:53:55 +01:00
|
|
|
//! declaration we allocate a `liveness` instance. Now that we know
|
2014-11-25 21:17:11 -05:00
|
|
|
//! precisely how many nodes and variables we need, we can allocate all
|
2019-02-08 14:53:55 +01:00
|
|
|
//! the various arrays that we will need to precisely the right size. We then
|
2014-11-25 21:17:11 -05:00
|
|
|
//! perform the actual propagation on the `liveness` instance.
|
|
|
|
//!
|
|
|
|
//! This propagation is encoded in the various `propagate_through_*()`
|
2019-02-08 14:53:55 +01:00
|
|
|
//! methods. It effectively does a reverse walk of the AST; whenever we
|
2014-11-25 21:17:11 -05:00
|
|
|
//! reach a loop node, we iterate until a fixed point is reached.
|
|
|
|
//!
|
2018-09-21 20:42:49 +10:00
|
|
|
//! ## The `RWU` struct
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
|
|
|
//! At each live node `N`, we track three pieces of information for each
|
2018-09-21 20:42:49 +10:00
|
|
|
//! variable `V` (these are encapsulated in the `RWU` struct):
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
|
|
|
//! - `reader`: the `LiveNode` ID of some node which will read the value
|
2019-02-08 14:53:55 +01:00
|
|
|
//! that `V` holds on entry to `N`. Formally: a node `M` such
|
2014-11-25 21:17:11 -05:00
|
|
|
//! that there exists a path `P` from `N` to `M` where `P` does not
|
2019-02-08 14:53:55 +01:00
|
|
|
//! write `V`. If the `reader` is `invalid_node()`, then the current
|
2014-11-25 21:17:11 -05:00
|
|
|
//! value will never be read (the variable is dead, essentially).
|
|
|
|
//!
|
|
|
|
//! - `writer`: the `LiveNode` ID of some node which will write the
|
2019-02-08 14:53:55 +01:00
|
|
|
//! variable `V` and which is reachable from `N`. Formally: a node `M`
|
2014-11-25 21:17:11 -05:00
|
|
|
//! such that there exists a path `P` from `N` to `M` and `M` writes
|
2019-02-08 14:53:55 +01:00
|
|
|
//! `V`. If the `writer` is `invalid_node()`, then there is no writer
|
2014-11-25 21:17:11 -05:00
|
|
|
//! of `V` that follows `N`.
|
|
|
|
//!
|
2019-02-08 14:53:55 +01:00
|
|
|
//! - `used`: a boolean value indicating whether `V` is *used*. We
|
2014-11-25 21:17:11 -05:00
|
|
|
//! distinguish a *read* from a *use* in that a *use* is some read that
|
2019-02-08 14:53:55 +01:00
|
|
|
//! is not just used to generate a new value. For example, `x += 1` is
|
|
|
|
//! a read but not a use. This is used to generate better warnings.
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
2020-05-22 00:00:00 +00:00
|
|
|
//! ## Special nodes and variables
|
2014-11-25 21:17:11 -05:00
|
|
|
//!
|
2020-05-22 00:00:00 +00:00
|
|
|
//! We generate various special nodes for various, well, special purposes.
|
|
|
|
//! These are described in the `Specials` struct.
|
2018-08-22 23:05:26 +01:00
|
|
|
|
2014-11-06 00:05:53 -08:00
|
|
|
use self::LiveNodeKind::*;
|
|
|
|
use self::VarKind::*;
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2020-04-27 23:26:11 +05:30
|
|
|
use rustc_ast::InlineAsmOptions;
|
2019-09-15 03:41:21 +02:00
|
|
|
use rustc_data_structures::fx::FxIndexMap;
|
2020-01-09 11:18:47 +01:00
|
|
|
use rustc_errors::Applicability;
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
|
|
|
use rustc_hir::def::*;
|
2020-06-27 13:09:54 +02:00
|
|
|
use rustc_hir::def_id::LocalDefId;
|
2020-01-07 18:12:06 +01:00
|
|
|
use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet, Node};
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::hir::map::Map;
|
|
|
|
use rustc_middle::ty::query::Providers;
|
|
|
|
use rustc_middle::ty::{self, TyCtxt};
|
2020-03-11 12:49:08 +01:00
|
|
|
use rustc_session::lint;
|
2020-09-26 00:00:00 +00:00
|
|
|
use rustc_span::symbol::{kw, sym, Symbol};
|
2019-12-31 20:15:40 +03:00
|
|
|
use rustc_span::Span;
|
2020-01-05 02:37:57 +01:00
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
use std::collections::VecDeque;
|
2020-04-04 17:25:45 +02:00
|
|
|
use std::fmt;
|
2015-03-11 15:24:14 -07:00
|
|
|
use std::io;
|
2019-12-22 17:42:04 -05:00
|
|
|
use std::io::prelude::*;
|
2015-03-11 15:24:14 -07:00
|
|
|
use std::rc::Rc;
|
2015-07-31 00:04:06 -07:00
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
2018-05-22 15:27:58 +10:00
|
|
|
struct Variable(u32);
|
librustc: Make `Copy` opt-in.
This change makes the compiler no longer infer whether types (structures
and enumerations) implement the `Copy` trait (and thus are implicitly
copyable). Rather, you must implement `Copy` yourself via `impl Copy for
MyType {}`.
A new warning has been added, `missing_copy_implementations`, to warn
you if a non-generic public type has been added that could have
implemented `Copy` but didn't.
For convenience, you may *temporarily* opt out of this behavior by using
`#![feature(opt_out_copy)]`. Note though that this feature gate will never be
accepted and will be removed by the time that 1.0 is released, so you should
transition your code away from using it.
This breaks code like:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
Change this code to:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
impl Copy for Point2D {}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
This is the backwards-incompatible part of #13231.
Part of RFC #3.
[breaking-change]
2014-12-05 17:01:33 -08:00
|
|
|
|
2018-05-22 15:27:58 +10:00
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
struct LiveNode(u32);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2013-11-01 18:06:31 -07:00
|
|
|
impl Variable {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn get(&self) -> usize {
|
|
|
|
self.0 as usize
|
|
|
|
}
|
2013-11-01 18:06:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LiveNode {
|
2019-12-22 17:42:04 -05:00
|
|
|
fn get(&self) -> usize {
|
|
|
|
self.0 as usize
|
|
|
|
}
|
2013-07-02 12:47:32 -07:00
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
2014-03-06 15:46:26 +02:00
|
|
|
enum LiveNodeKind {
|
2019-05-04 03:57:46 +03:00
|
|
|
UpvarNode(Span),
|
2013-08-31 18:13:04 +02:00
|
|
|
ExprNode(Span),
|
|
|
|
VarDefNode(Span),
|
2020-05-22 00:00:00 +00:00
|
|
|
ClosureNode,
|
2019-12-22 17:42:04 -05:00
|
|
|
ExitNode,
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
|
2020-02-22 16:07:05 +02:00
|
|
|
let sm = tcx.sess.source_map();
|
2012-10-18 12:20:18 -07:00
|
|
|
match lnk {
|
2020-02-22 16:07:05 +02:00
|
|
|
UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)),
|
|
|
|
ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)),
|
|
|
|
VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)),
|
2020-05-22 00:00:00 +00:00
|
|
|
ClosureNode => "Closure node".to_owned(),
|
2018-10-02 18:05:06 +02:00
|
|
|
ExitNode => "Exit node".to_owned(),
|
2012-10-18 12:20:18 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 23:35:39 +03:00
|
|
|
impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
|
2020-01-07 17:25:33 +01:00
|
|
|
type Map = Map<'tcx>;
|
|
|
|
|
2020-02-09 15:32:00 +01:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
|
|
NestedVisitorMap::OnlyBodies(self.tcx.hir())
|
2016-10-28 22:58:32 +02:00
|
|
|
}
|
2016-11-28 14:00:26 -05:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn visit_fn(
|
|
|
|
&mut self,
|
|
|
|
fk: FnKind<'tcx>,
|
2019-12-01 16:08:58 +01:00
|
|
|
fd: &'tcx hir::FnDecl<'tcx>,
|
2019-12-22 17:42:04 -05:00
|
|
|
b: hir::BodyId,
|
|
|
|
s: Span,
|
|
|
|
id: HirId,
|
|
|
|
) {
|
2014-11-15 17:26:15 -05:00
|
|
|
visit_fn(self, fk, fd, b, s, id);
|
2013-08-14 16:29:40 +02:00
|
|
|
}
|
2018-04-04 18:16:44 -07:00
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
2019-12-22 17:42:04 -05:00
|
|
|
visit_local(self, l);
|
|
|
|
}
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
2019-12-22 17:42:04 -05:00
|
|
|
visit_expr(self, ex);
|
|
|
|
}
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
|
2019-12-22 17:42:04 -05:00
|
|
|
visit_arm(self, a);
|
|
|
|
}
|
2013-08-14 16:29:40 +02:00
|
|
|
}
|
|
|
|
|
2020-06-27 13:09:54 +02:00
|
|
|
fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
|
2019-05-27 20:33:59 +03:00
|
|
|
tcx.hir().visit_item_likes_in_module(
|
|
|
|
module_def_id,
|
|
|
|
&mut IrMaps::new(tcx, module_def_id).as_deep_visitor(),
|
|
|
|
);
|
2018-06-08 19:14:03 +02:00
|
|
|
}
|
|
|
|
|
2020-07-05 23:00:14 +03:00
|
|
|
pub fn provide(providers: &mut Providers) {
|
2019-12-22 17:42:04 -05:00
|
|
|
*providers = Providers { check_mod_liveness, ..*providers };
|
2018-06-08 19:14:03 +02:00
|
|
|
}
|
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl fmt::Debug for LiveNode {
|
2018-08-29 22:02:42 -07:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2014-05-10 14:05:06 -07:00
|
|
|
write!(f, "ln({})", self.get())
|
2014-02-19 18:56:33 -08:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl fmt::Debug for Variable {
|
2018-08-29 22:02:42 -07:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2014-05-10 14:05:06 -07:00
|
|
|
write!(f, "v({})", self.get())
|
2014-02-19 18:56:33 -08:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ______________________________________________________________________
|
|
|
|
// Creating ir_maps
|
|
|
|
//
|
|
|
|
// This is the first pass and the one that drives the main
|
|
|
|
// computation. It walks up and down the IR once. On the way down,
|
|
|
|
// we count for each function the number of variables as well as
|
|
|
|
// liveness nodes. A liveness node is basically an expression or
|
|
|
|
// capture clause that does something of interest: either it has
|
|
|
|
// interesting control flow or it uses/defines a local variable.
|
|
|
|
//
|
|
|
|
// On the way back up, at each function node we create liveness sets
|
|
|
|
// (we now know precisely how big to make our various vectors and so
|
|
|
|
// forth) and then do the data-flow propagation to compute the set
|
|
|
|
// of live variables at each program point.
|
|
|
|
//
|
|
|
|
// Finally, we run back over the IR one last time and, using the
|
|
|
|
// computed liveness, check various safety conditions. For example,
|
|
|
|
// there must be no live nodes at the definition site for a variable
|
|
|
|
// unless it has an initializer. Similarly, each non-mutable local
|
|
|
|
// variable must not be assigned if there is some successor
|
|
|
|
// assignment. And so forth.
|
|
|
|
|
2013-05-31 15:17:22 -07:00
|
|
|
impl LiveNode {
|
2014-03-06 15:46:26 +02:00
|
|
|
fn is_valid(&self) -> bool {
|
2018-05-22 15:27:58 +10:00
|
|
|
self.0 != u32::MAX
|
2013-05-31 15:17:22 -07:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn invalid_node() -> LiveNode {
|
|
|
|
LiveNode(u32::MAX)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
struct CaptureInfo {
|
2012-10-08 11:49:01 -07:00
|
|
|
ln: LiveNode,
|
2019-12-22 17:42:04 -05:00
|
|
|
var_hid: HirId,
|
2012-10-08 11:49:01 -07:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2014-03-06 15:46:26 +02:00
|
|
|
struct LocalInfo {
|
2018-05-20 20:19:34 -07:00
|
|
|
id: HirId,
|
2020-04-19 13:00:18 +02:00
|
|
|
name: Symbol,
|
2018-01-31 20:56:01 -08:00
|
|
|
is_shorthand: bool,
|
2012-05-25 00:14:40 -07:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2014-03-06 15:46:26 +02:00
|
|
|
enum VarKind {
|
2020-04-19 13:00:18 +02:00
|
|
|
Param(HirId, Symbol),
|
2012-08-23 09:14:19 -07:00
|
|
|
Local(LocalInfo),
|
2020-05-21 00:00:00 +00:00
|
|
|
Upvar(HirId, Symbol),
|
2012-08-23 09:14:19 -07:00
|
|
|
}
|
|
|
|
|
2019-06-11 22:03:44 +03:00
|
|
|
struct IrMaps<'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2020-06-27 13:09:54 +02:00
|
|
|
body_owner: LocalDefId,
|
2018-05-20 20:19:34 -07:00
|
|
|
live_node_map: HirIdMap<LiveNode>,
|
|
|
|
variable_map: HirIdMap<Variable>,
|
2019-02-24 09:33:17 +01:00
|
|
|
capture_info_map: HirIdMap<Rc<Vec<CaptureInfo>>>,
|
2014-03-06 15:46:26 +02:00
|
|
|
var_kinds: Vec<VarKind>,
|
|
|
|
lnks: Vec<LiveNodeKind>,
|
2012-08-23 09:14:19 -07:00
|
|
|
}
|
|
|
|
|
2019-06-11 22:03:44 +03:00
|
|
|
impl IrMaps<'tcx> {
|
2020-06-27 13:09:54 +02:00
|
|
|
fn new(tcx: TyCtxt<'tcx>, body_owner: LocalDefId) -> IrMaps<'tcx> {
|
2014-05-28 20:36:05 +01:00
|
|
|
IrMaps {
|
2017-07-03 11:19:51 -07:00
|
|
|
tcx,
|
2019-05-27 20:33:59 +03:00
|
|
|
body_owner,
|
2018-07-21 22:15:11 +03:00
|
|
|
live_node_map: HirIdMap::default(),
|
|
|
|
variable_map: HirIdMap::default(),
|
|
|
|
capture_info_map: Default::default(),
|
2014-05-28 20:36:05 +01:00
|
|
|
var_kinds: Vec::new(),
|
|
|
|
lnks: Vec::new(),
|
|
|
|
}
|
2012-08-23 09:14:19 -07:00
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn add_live_node(&mut self, lnk: LiveNodeKind) -> LiveNode {
|
2020-09-26 00:00:00 +00:00
|
|
|
let ln = LiveNode(self.lnks.len() as u32);
|
2014-03-06 15:46:26 +02:00
|
|
|
self.lnks.push(lnk);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
debug!("{:?} is of kind {}", ln, live_node_kind_to_string(lnk, self.tcx));
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
ln
|
|
|
|
}
|
|
|
|
|
2018-05-20 20:19:34 -07:00
|
|
|
fn add_live_node_for_node(&mut self, hir_id: HirId, lnk: LiveNodeKind) {
|
2012-05-19 05:52:01 -07:00
|
|
|
let ln = self.add_live_node(lnk);
|
2018-05-20 20:19:34 -07:00
|
|
|
self.live_node_map.insert(hir_id, ln);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-05-20 20:19:34 -07:00
|
|
|
debug!("{:?} is node {:?}", ln, hir_id);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn add_variable(&mut self, vk: VarKind) -> Variable {
|
2020-09-26 00:00:00 +00:00
|
|
|
let v = Variable(self.var_kinds.len() as u32);
|
2014-03-06 15:46:26 +02:00
|
|
|
self.var_kinds.push(vk);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2012-08-06 12:34:08 -07:00
|
|
|
match vk {
|
2020-05-21 00:00:00 +00:00
|
|
|
Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => {
|
2014-03-06 15:46:26 +02:00
|
|
|
self.variable_map.insert(node_id, v);
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2012-05-25 00:14:40 -07:00
|
|
|
}
|
|
|
|
|
2014-12-20 00:09:35 -08:00
|
|
|
debug!("{:?} is {:?}", v, vk);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
v
|
|
|
|
}
|
|
|
|
|
2018-05-20 20:19:34 -07:00
|
|
|
fn variable(&self, hir_id: HirId, span: Span) -> Variable {
|
|
|
|
match self.variable_map.get(&hir_id) {
|
2016-03-25 18:31:27 +01:00
|
|
|
Some(&var) => var,
|
|
|
|
None => {
|
2018-05-20 20:19:34 -07:00
|
|
|
span_bug!(span, "no variable registered for id {:?}", hir_id);
|
2016-03-25 18:31:27 +01:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-26 00:00:00 +00:00
|
|
|
fn variable_name(&self, var: Variable) -> Symbol {
|
2014-10-14 23:05:01 -07:00
|
|
|
match self.var_kinds[var.get()] {
|
2020-09-26 00:00:00 +00:00
|
|
|
Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name,
|
2012-05-25 00:14:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-31 20:56:01 -08:00
|
|
|
fn variable_is_shorthand(&self, var: Variable) -> bool {
|
|
|
|
match self.var_kinds[var.get()] {
|
|
|
|
Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
|
2020-05-21 00:00:00 +00:00
|
|
|
Param(..) | Upvar(..) => false,
|
2018-01-31 20:56:01 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-24 09:33:17 +01:00
|
|
|
fn set_captures(&mut self, hir_id: HirId, cs: Vec<CaptureInfo>) {
|
|
|
|
self.capture_info_map.insert(hir_id, Rc::new(cs));
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn lnk(&self, ln: LiveNode) -> LiveNodeKind {
|
2014-10-14 23:05:01 -07:00
|
|
|
self.lnks[ln.get()]
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-16 12:33:21 +03:00
|
|
|
fn visit_fn<'tcx>(
|
2019-06-12 00:11:55 +03:00
|
|
|
ir: &mut IrMaps<'tcx>,
|
|
|
|
fk: FnKind<'tcx>,
|
2019-12-01 16:08:58 +01:00
|
|
|
decl: &'tcx hir::FnDecl<'tcx>,
|
2019-06-12 00:11:55 +03:00
|
|
|
body_id: hir::BodyId,
|
|
|
|
sp: Span,
|
|
|
|
id: hir::HirId,
|
|
|
|
) {
|
2020-05-20 00:00:00 +00:00
|
|
|
debug!("visit_fn {:?}", id);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// swap in a new set of IR maps for this function body:
|
2019-06-27 11:28:14 +02:00
|
|
|
let def_id = ir.tcx.hir().local_def_id(id);
|
2020-06-27 13:09:54 +02:00
|
|
|
let mut fn_maps = IrMaps::new(ir.tcx, def_id);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-04-04 18:16:44 -07:00
|
|
|
// Don't run unused pass for #[derive()]
|
|
|
|
if let FnKind::Method(..) = fk {
|
2019-02-06 14:16:11 +01:00
|
|
|
let parent = ir.tcx.hir().get_parent_item(id);
|
2019-06-24 09:58:49 +02:00
|
|
|
if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) {
|
2020-07-30 11:27:50 +10:00
|
|
|
if i.attrs.iter().any(|a| ir.tcx.sess.check_name(a, sym::automatically_derived)) {
|
2018-04-04 18:16:44 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-11 22:03:44 +03:00
|
|
|
debug!("creating fn_maps: {:p}", &fn_maps);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-12-04 13:45:36 +01:00
|
|
|
let body = ir.tcx.hir().body(body_id);
|
2016-12-20 22:46:11 +02:00
|
|
|
|
2020-05-21 00:00:00 +00:00
|
|
|
if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) {
|
|
|
|
for (&var_hir_id, _upvar) in upvars {
|
|
|
|
let var_name = ir.tcx.hir().name(var_hir_id);
|
|
|
|
fn_maps.add_variable(Upvar(var_hir_id, var_name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-29 11:09:23 +01:00
|
|
|
for param in body.params {
|
2019-09-26 16:18:31 +01:00
|
|
|
let is_shorthand = match param.pat.kind {
|
2020-01-05 02:37:57 +01:00
|
|
|
rustc_hir::PatKind::Struct(..) => true,
|
2019-03-02 15:16:53 -08:00
|
|
|
_ => false,
|
|
|
|
};
|
2019-08-27 13:24:32 +02:00
|
|
|
param.pat.each_binding(|_bm, hir_id, _x, ident| {
|
2019-03-02 15:16:53 -08:00
|
|
|
let var = if is_shorthand {
|
2019-12-22 17:42:04 -05:00
|
|
|
Local(LocalInfo { id: hir_id, name: ident.name, is_shorthand: true })
|
2019-03-02 15:16:53 -08:00
|
|
|
} else {
|
2019-08-27 13:24:32 +02:00
|
|
|
Param(hir_id, ident.name)
|
2019-03-02 15:16:53 -08:00
|
|
|
};
|
|
|
|
fn_maps.add_variable(var);
|
2013-11-21 15:42:55 -08:00
|
|
|
})
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// gather up the various local variables, significant expressions,
|
|
|
|
// and so forth:
|
2016-10-28 22:58:32 +02:00
|
|
|
intravisit::walk_fn(&mut fn_maps, fk, decl, body_id, sp, id);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// compute liveness
|
2020-04-17 15:17:01 +01:00
|
|
|
let mut lsets = Liveness::new(&mut fn_maps, def_id);
|
2020-05-22 00:00:00 +00:00
|
|
|
let entry_ln = lsets.compute(fk, &body, sp, id);
|
|
|
|
lsets.log_liveness(entry_ln, id);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// check for various error conditions
|
2016-12-21 12:32:59 +02:00
|
|
|
lsets.visit_body(body);
|
2020-05-22 00:00:00 +00:00
|
|
|
lsets.warn_about_unused_upvars(entry_ln);
|
2016-12-20 22:46:11 +02:00
|
|
|
lsets.warn_about_unused_args(body, entry_ln);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn add_from_pat(ir: &mut IrMaps<'_>, pat: &hir::Pat<'_>) {
|
2018-05-18 00:07:31 -07:00
|
|
|
// For struct patterns, take note of which fields used shorthand
|
|
|
|
// (`x` rather than `x: x`).
|
2018-07-21 22:15:11 +03:00
|
|
|
let mut shorthand_field_ids = HirIdSet::default();
|
2018-05-18 00:07:31 -07:00
|
|
|
let mut pats = VecDeque::new();
|
|
|
|
pats.push_back(pat);
|
|
|
|
while let Some(pat) = pats.pop_front() {
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::PatKind::*;
|
2019-09-26 16:18:31 +01:00
|
|
|
match &pat.kind {
|
2019-09-15 03:55:34 +02:00
|
|
|
Binding(.., inner_pat) => {
|
2018-05-18 00:07:31 -07:00
|
|
|
pats.extend(inner_pat.iter());
|
|
|
|
}
|
2019-09-15 03:55:34 +02:00
|
|
|
Struct(_, fields, _) => {
|
|
|
|
let ids = fields.iter().filter(|f| f.is_shorthand).map(|f| f.pat.hir_id);
|
|
|
|
shorthand_field_ids.extend(ids);
|
2018-05-18 00:07:31 -07:00
|
|
|
}
|
2019-09-15 03:55:34 +02:00
|
|
|
Ref(inner_pat, _) | Box(inner_pat) => {
|
2018-05-18 00:07:31 -07:00
|
|
|
pats.push_back(inner_pat);
|
|
|
|
}
|
2019-09-15 03:55:34 +02:00
|
|
|
TupleStruct(_, inner_pats, _) | Tuple(inner_pats, _) | Or(inner_pats) => {
|
2018-05-18 00:07:31 -07:00
|
|
|
pats.extend(inner_pats.iter());
|
|
|
|
}
|
2019-09-15 03:55:34 +02:00
|
|
|
Slice(pre_pats, inner_pat, post_pats) => {
|
2018-05-18 00:07:31 -07:00
|
|
|
pats.extend(pre_pats.iter());
|
|
|
|
pats.extend(inner_pat.iter());
|
|
|
|
pats.extend(post_pats.iter());
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 03:55:34 +02:00
|
|
|
pat.each_binding(|_, hir_id, _, ident| {
|
2018-06-10 19:33:30 +03:00
|
|
|
ir.add_live_node_for_node(hir_id, VarDefNode(ident.span));
|
2014-03-06 15:46:26 +02:00
|
|
|
ir.add_variable(Local(LocalInfo {
|
2018-05-20 20:19:34 -07:00
|
|
|
id: hir_id,
|
2018-06-10 19:33:30 +03:00
|
|
|
name: ident.name,
|
2019-12-22 17:42:04 -05:00
|
|
|
is_shorthand: shorthand_field_ids.contains(&hir_id),
|
2012-08-23 09:14:19 -07:00
|
|
|
}));
|
2013-11-21 15:42:55 -08:00
|
|
|
});
|
2018-05-18 00:07:31 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_local<'tcx>(ir: &mut IrMaps<'tcx>, local: &'tcx hir::Local<'tcx>) {
|
2018-05-18 00:07:31 -07:00
|
|
|
add_from_pat(ir, &local.pat);
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_local(ir, local);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_arm<'tcx>(ir: &mut IrMaps<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
|
2019-09-15 03:55:34 +02:00
|
|
|
add_from_pat(ir, &arm.pat);
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_arm(ir, arm);
|
2012-08-24 11:04:07 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2019-12-22 17:42:04 -05:00
|
|
|
// live nodes required for uses or definitions of variables:
|
|
|
|
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
|
|
|
debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
|
2020-05-21 00:00:00 +00:00
|
|
|
if let Res::Local(_var_hir_id) = path.res {
|
|
|
|
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
2019-05-28 18:06:01 +03:00
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
intravisit::walk_expr(ir, expr);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Closure(..) => {
|
|
|
|
// Interesting control flow (for loops can contain labeled
|
|
|
|
// breaks or continues)
|
|
|
|
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
|
|
|
|
|
|
|
// Make a live_node for each captured variable, with the span
|
|
|
|
// being the location that the variable is used. This results
|
|
|
|
// in better error messages than just pointing at the closure
|
|
|
|
// construction site.
|
|
|
|
let mut call_caps = Vec::new();
|
|
|
|
let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
|
2020-05-23 19:29:49 -04:00
|
|
|
if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) {
|
2020-05-21 00:00:00 +00:00
|
|
|
call_caps.extend(upvars.iter().map(|(&var_id, upvar)| {
|
|
|
|
let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
|
|
|
|
CaptureInfo { ln: upvar_ln, var_hid: var_id }
|
2019-12-22 17:42:04 -05:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
ir.set_captures(expr.hir_id, call_caps);
|
|
|
|
let old_body_owner = ir.body_owner;
|
2020-06-27 13:09:54 +02:00
|
|
|
ir.body_owner = closure_def_id;
|
2019-12-22 17:42:04 -05:00
|
|
|
intravisit::walk_expr(ir, expr);
|
|
|
|
ir.body_owner = old_body_owner;
|
|
|
|
}
|
|
|
|
|
|
|
|
// live nodes required for interesting control flow:
|
|
|
|
hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => {
|
|
|
|
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
|
|
|
intravisit::walk_expr(ir, expr);
|
|
|
|
}
|
|
|
|
hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => {
|
|
|
|
ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
|
|
|
intravisit::walk_expr(ir, expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, live nodes are not required:
|
|
|
|
hir::ExprKind::Index(..)
|
|
|
|
| hir::ExprKind::Field(..)
|
|
|
|
| hir::ExprKind::Array(..)
|
|
|
|
| hir::ExprKind::Call(..)
|
|
|
|
| hir::ExprKind::MethodCall(..)
|
|
|
|
| hir::ExprKind::Tup(..)
|
|
|
|
| hir::ExprKind::Binary(..)
|
|
|
|
| hir::ExprKind::AddrOf(..)
|
|
|
|
| hir::ExprKind::Cast(..)
|
|
|
|
| hir::ExprKind::DropTemps(..)
|
|
|
|
| hir::ExprKind::Unary(..)
|
|
|
|
| hir::ExprKind::Break(..)
|
|
|
|
| hir::ExprKind::Continue(_)
|
|
|
|
| hir::ExprKind::Lit(_)
|
|
|
|
| hir::ExprKind::Ret(..)
|
|
|
|
| hir::ExprKind::Block(..)
|
|
|
|
| hir::ExprKind::Assign(..)
|
|
|
|
| hir::ExprKind::AssignOp(..)
|
|
|
|
| hir::ExprKind::Struct(..)
|
|
|
|
| hir::ExprKind::Repeat(..)
|
2020-02-13 11:00:55 +00:00
|
|
|
| hir::ExprKind::InlineAsm(..)
|
2020-01-14 13:40:42 +00:00
|
|
|
| hir::ExprKind::LlvmInlineAsm(..)
|
2019-12-22 17:42:04 -05:00
|
|
|
| hir::ExprKind::Box(..)
|
|
|
|
| hir::ExprKind::Yield(..)
|
|
|
|
| hir::ExprKind::Type(..)
|
|
|
|
| hir::ExprKind::Err
|
2020-08-04 14:34:24 +01:00
|
|
|
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
|
|
|
|
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => {
|
2019-12-22 17:42:04 -05:00
|
|
|
intravisit::walk_expr(ir, expr);
|
2019-05-04 03:47:16 +03:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ______________________________________________________________________
|
|
|
|
// Computing liveness sets
|
|
|
|
//
|
|
|
|
// Actually we compute just a bit more than just liveness, but we use
|
|
|
|
// the same basic propagation framework in all cases.
|
|
|
|
|
2018-09-21 20:42:49 +10:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
struct RWU {
|
|
|
|
reader: LiveNode,
|
|
|
|
writer: LiveNode,
|
2019-12-22 17:42:04 -05:00
|
|
|
used: bool,
|
2018-09-21 20:42:49 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
|
|
|
|
/// very large, so it uses a more compact representation that takes advantage
|
|
|
|
/// of the fact that when the number of `RWU`s is large, most of them have an
|
|
|
|
/// invalid reader and an invalid writer.
|
|
|
|
struct RWUTable {
|
|
|
|
/// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
|
|
|
|
/// an index into `unpacked_rwus`. In the common cases, this compacts the
|
|
|
|
/// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
|
|
|
|
/// in 96.
|
|
|
|
///
|
2018-11-27 02:59:49 +00:00
|
|
|
/// More compact representations are possible -- e.g., use only 2 bits per
|
2018-09-21 20:42:49 +10:00
|
|
|
/// packed `RWU` and make the secondary table a HashMap that maps from
|
|
|
|
/// indices to `RWU`s -- but this one strikes a good balance between size
|
|
|
|
/// and speed.
|
|
|
|
packed_rwus: Vec<u32>,
|
|
|
|
unpacked_rwus: Vec<RWU>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`.
|
|
|
|
const INV_INV_FALSE: u32 = u32::MAX;
|
|
|
|
|
|
|
|
// A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`.
|
|
|
|
const INV_INV_TRUE: u32 = u32::MAX - 1;
|
|
|
|
|
|
|
|
impl RWUTable {
|
|
|
|
fn new(num_rwus: usize) -> RWUTable {
|
2019-12-22 17:42:04 -05:00
|
|
|
Self { packed_rwus: vec![INV_INV_FALSE; num_rwus], unpacked_rwus: vec![] }
|
2018-09-21 20:42:49 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&self, idx: usize) -> RWU {
|
|
|
|
let packed_rwu = self.packed_rwus[idx];
|
|
|
|
match packed_rwu {
|
|
|
|
INV_INV_FALSE => RWU { reader: invalid_node(), writer: invalid_node(), used: false },
|
|
|
|
INV_INV_TRUE => RWU { reader: invalid_node(), writer: invalid_node(), used: true },
|
|
|
|
_ => self.unpacked_rwus[packed_rwu as usize],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_reader(&self, idx: usize) -> LiveNode {
|
|
|
|
let packed_rwu = self.packed_rwus[idx];
|
|
|
|
match packed_rwu {
|
|
|
|
INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
|
|
|
|
_ => self.unpacked_rwus[packed_rwu as usize].reader,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_writer(&self, idx: usize) -> LiveNode {
|
|
|
|
let packed_rwu = self.packed_rwus[idx];
|
|
|
|
match packed_rwu {
|
|
|
|
INV_INV_FALSE | INV_INV_TRUE => invalid_node(),
|
|
|
|
_ => self.unpacked_rwus[packed_rwu as usize].writer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_used(&self, idx: usize) -> bool {
|
|
|
|
let packed_rwu = self.packed_rwus[idx];
|
|
|
|
match packed_rwu {
|
|
|
|
INV_INV_FALSE => false,
|
|
|
|
INV_INV_TRUE => true,
|
|
|
|
_ => self.unpacked_rwus[packed_rwu as usize].used,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn copy_packed(&mut self, dst_idx: usize, src_idx: usize) {
|
|
|
|
self.packed_rwus[dst_idx] = self.packed_rwus[src_idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assign_unpacked(&mut self, idx: usize, rwu: RWU) {
|
|
|
|
if rwu.reader == invalid_node() && rwu.writer == invalid_node() {
|
|
|
|
// When we overwrite an indexing entry in `self.packed_rwus` with
|
|
|
|
// `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
|
|
|
|
// from `self.unpacked_rwus`; it's not worth the effort, and we
|
|
|
|
// can't have entries shifting around anyway.
|
2019-12-22 17:42:04 -05:00
|
|
|
self.packed_rwus[idx] = if rwu.used { INV_INV_TRUE } else { INV_INV_FALSE }
|
2018-09-21 20:42:49 +10:00
|
|
|
} else {
|
|
|
|
// Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
|
|
|
|
// point to it.
|
|
|
|
self.packed_rwus[idx] = self.unpacked_rwus.len() as u32;
|
|
|
|
self.unpacked_rwus.push(rwu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assign_inv_inv(&mut self, idx: usize) {
|
2019-12-22 17:42:04 -05:00
|
|
|
self.packed_rwus[idx] = if self.get_used(idx) { INV_INV_TRUE } else { INV_INV_FALSE };
|
2018-09-21 20:42:49 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-30 09:38:44 -04:00
|
|
|
#[derive(Copy, Clone)]
|
2014-03-06 15:46:26 +02:00
|
|
|
struct Specials {
|
2020-05-22 00:00:00 +00:00
|
|
|
/// A live node representing a point of execution before closure entry &
|
|
|
|
/// after closure exit. Used to calculate liveness of captured variables
|
|
|
|
/// through calls to the same closure. Used for Fn & FnMut closures only.
|
|
|
|
closure_ln: LiveNode,
|
|
|
|
/// A live node representing every 'exit' from the function, whether it be
|
|
|
|
/// by explicit return, panic, or other means.
|
2012-08-23 09:14:19 -07:00
|
|
|
exit_ln: LiveNode,
|
2013-02-04 14:02:01 -08:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2015-02-27 15:36:53 +01:00
|
|
|
const ACC_READ: u32 = 1;
|
|
|
|
const ACC_WRITE: u32 = 2;
|
|
|
|
const ACC_USE: u32 = 4;
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-06-14 19:39:39 +03:00
|
|
|
struct Liveness<'a, 'tcx> {
|
2019-06-11 22:03:44 +03:00
|
|
|
ir: &'a mut IrMaps<'tcx>,
|
2020-07-17 08:47:04 +00:00
|
|
|
typeck_results: &'a ty::TypeckResults<'tcx>,
|
2020-03-16 18:51:55 +01:00
|
|
|
param_env: ty::ParamEnv<'tcx>,
|
2012-09-06 19:40:15 -07:00
|
|
|
s: Specials,
|
2014-03-06 15:46:26 +02:00
|
|
|
successors: Vec<LiveNode>,
|
2018-09-21 20:42:49 +10:00
|
|
|
rwu_table: RWUTable,
|
2017-02-28 11:05:03 -08:00
|
|
|
|
2012-10-18 12:20:18 -07:00
|
|
|
// mappings from loop node ID to LiveNode
|
|
|
|
// ("break" label should map to loop node ID,
|
|
|
|
// it probably doesn't now)
|
2019-03-07 12:43:27 +01:00
|
|
|
break_ln: HirIdMap<LiveNode>,
|
|
|
|
cont_ln: HirIdMap<LiveNode>,
|
2012-08-23 09:14:19 -07:00
|
|
|
}
|
|
|
|
|
2014-04-22 15:56:37 +03:00
|
|
|
impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
2020-04-17 15:17:01 +01:00
|
|
|
fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> {
|
2017-01-06 21:54:24 +02:00
|
|
|
let specials = Specials {
|
2020-05-22 00:00:00 +00:00
|
|
|
closure_ln: ir.add_live_node(ClosureNode),
|
2017-01-06 21:54:24 +02:00
|
|
|
exit_ln: ir.add_live_node(ExitNode),
|
|
|
|
};
|
|
|
|
|
2020-07-17 08:47:04 +00:00
|
|
|
let typeck_results = ir.tcx.typeck(def_id);
|
2020-03-16 18:51:55 +01:00
|
|
|
let param_env = ir.tcx.param_env(def_id);
|
2017-01-06 21:54:24 +02:00
|
|
|
|
2020-09-26 00:00:00 +00:00
|
|
|
let num_live_nodes = ir.lnks.len();
|
|
|
|
let num_vars = ir.var_kinds.len();
|
2017-01-06 21:54:24 +02:00
|
|
|
|
2014-05-28 20:36:05 +01:00
|
|
|
Liveness {
|
2017-07-03 11:19:51 -07:00
|
|
|
ir,
|
2020-07-17 08:47:04 +00:00
|
|
|
typeck_results,
|
2020-03-16 18:51:55 +01:00
|
|
|
param_env,
|
2014-05-28 20:36:05 +01:00
|
|
|
s: specials,
|
2015-07-08 22:52:55 +02:00
|
|
|
successors: vec![invalid_node(); num_live_nodes],
|
2018-09-21 20:42:49 +10:00
|
|
|
rwu_table: RWUTable::new(num_live_nodes * num_vars),
|
2018-07-21 22:15:11 +03:00
|
|
|
break_ln: Default::default(),
|
|
|
|
cont_ln: Default::default(),
|
2014-05-28 20:36:05 +01:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2018-05-20 20:19:34 -07:00
|
|
|
fn live_node(&self, hir_id: HirId, span: Span) -> LiveNode {
|
|
|
|
match self.ir.live_node_map.get(&hir_id) {
|
2019-12-22 17:42:04 -05:00
|
|
|
Some(&ln) => ln,
|
|
|
|
None => {
|
|
|
|
// This must be a mismatch between the ir_map construction
|
|
|
|
// above and the propagation code below; the two sets of
|
|
|
|
// code have to agree about which AST nodes are worth
|
|
|
|
// creating liveness nodes for.
|
|
|
|
span_bug!(span, "no live node registered for node {:?}", hir_id);
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-20 20:19:34 -07:00
|
|
|
fn variable(&self, hir_id: HirId, span: Span) -> Variable {
|
|
|
|
self.ir.variable(hir_id, span)
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn define_bindings_in_pat(&mut self, pat: &hir::Pat<'_>, mut succ: LiveNode) -> LiveNode {
|
2019-09-15 03:41:21 +02:00
|
|
|
// In an or-pattern, only consider the first pattern; any later patterns
|
|
|
|
// must have the same bindings, and we also consider the first pattern
|
|
|
|
// to be the "authoritative" set of ids.
|
|
|
|
pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
|
|
|
|
let ln = self.live_node(hir_id, pat_sp);
|
|
|
|
let var = self.variable(hir_id, ident.span);
|
|
|
|
self.init_from_succ(ln, succ);
|
|
|
|
self.define(ln, var);
|
2012-08-24 11:04:07 -07:00
|
|
|
succ = ln;
|
2013-11-21 15:42:55 -08:00
|
|
|
});
|
2012-08-24 11:04:07 -07:00
|
|
|
succ
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:43:55 +01:00
|
|
|
fn idx(&self, ln: LiveNode, var: Variable) -> usize {
|
2020-09-26 00:00:00 +00:00
|
|
|
ln.get() * self.ir.var_kinds.len() + var.get()
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2018-09-21 20:42:49 +10:00
|
|
|
fn live_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
|
2013-03-28 18:39:09 -07:00
|
|
|
assert!(ln.is_valid());
|
2018-09-21 20:42:49 +10:00
|
|
|
let reader = self.rwu_table.get_reader(self.idx(ln, var));
|
|
|
|
if reader.is_valid() { Some(self.ir.lnk(reader)) } else { None }
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2018-09-21 20:42:49 +10:00
|
|
|
// Is this variable live on entry to any of its successor nodes?
|
2019-12-22 17:42:04 -05:00
|
|
|
fn live_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
|
2014-10-14 23:05:01 -07:00
|
|
|
let successor = self.successors[ln.get()];
|
2013-12-22 14:08:42 -08:00
|
|
|
self.live_on_entry(successor, var)
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn used_on_entry(&self, ln: LiveNode, var: Variable) -> bool {
|
2013-03-28 18:39:09 -07:00
|
|
|
assert!(ln.is_valid());
|
2018-09-21 20:42:49 +10:00
|
|
|
self.rwu_table.get_used(self.idx(ln, var))
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn assigned_on_entry(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
|
2013-03-28 18:39:09 -07:00
|
|
|
assert!(ln.is_valid());
|
2018-09-21 20:42:49 +10:00
|
|
|
let writer = self.rwu_table.get_writer(self.idx(ln, var));
|
|
|
|
if writer.is_valid() { Some(self.ir.lnk(writer)) } else { None }
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn assigned_on_exit(&self, ln: LiveNode, var: Variable) -> Option<LiveNodeKind> {
|
2014-10-14 23:05:01 -07:00
|
|
|
let successor = self.successors[ln.get()];
|
2013-12-22 14:08:42 -08:00
|
|
|
self.assigned_on_entry(successor, var)
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn indices2<F>(&mut self, ln: LiveNode, succ_ln: LiveNode, mut op: F)
|
|
|
|
where
|
2015-02-27 01:43:55 +01:00
|
|
|
F: FnMut(&mut Liveness<'a, 'tcx>, usize, usize),
|
2014-12-08 20:26:43 -05:00
|
|
|
{
|
2015-01-25 10:58:43 +00:00
|
|
|
let node_base_idx = self.idx(ln, Variable(0));
|
|
|
|
let succ_base_idx = self.idx(succ_ln, Variable(0));
|
2020-09-26 00:00:00 +00:00
|
|
|
for var_idx in 0..self.ir.var_kinds.len() {
|
2014-03-06 15:46:26 +02:00
|
|
|
op(self, node_base_idx + var_idx, succ_base_idx + var_idx);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn write_vars<F>(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()>
|
|
|
|
where
|
2020-05-20 00:00:00 +00:00
|
|
|
F: FnMut(usize) -> bool,
|
2014-12-08 20:26:43 -05:00
|
|
|
{
|
2012-10-18 12:20:18 -07:00
|
|
|
let node_base_idx = self.idx(ln, Variable(0));
|
2020-09-26 00:00:00 +00:00
|
|
|
for var_idx in 0..self.ir.var_kinds.len() {
|
2012-05-19 05:52:01 -07:00
|
|
|
let idx = node_base_idx + var_idx;
|
2020-05-20 00:00:00 +00:00
|
|
|
if test(idx) {
|
2018-05-22 15:27:58 +10:00
|
|
|
write!(wr, " {:?}", Variable(var_idx as u32))?;
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
2014-01-29 18:42:19 -08:00
|
|
|
Ok(())
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-01-29 18:42:19 -08:00
|
|
|
#[allow(unused_must_use)]
|
2014-05-22 16:57:53 -07:00
|
|
|
fn ln_str(&self, ln: LiveNode) -> String {
|
2014-11-11 16:01:29 -05:00
|
|
|
let mut wr = Vec::new();
|
2014-01-15 13:25:09 -08:00
|
|
|
{
|
2018-02-23 09:53:00 -08:00
|
|
|
let wr = &mut wr as &mut dyn Write;
|
2014-12-20 00:09:35 -08:00
|
|
|
write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
|
2020-05-20 00:00:00 +00:00
|
|
|
self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid());
|
2014-03-06 15:46:26 +02:00
|
|
|
write!(wr, " writes");
|
2020-05-20 00:00:00 +00:00
|
|
|
self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid());
|
|
|
|
write!(wr, " uses");
|
|
|
|
self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));
|
|
|
|
|
2014-12-20 00:09:35 -08:00
|
|
|
write!(wr, " precedes {:?}]", self.successors[ln.get()]);
|
2014-01-15 13:25:09 -08:00
|
|
|
}
|
2014-11-11 16:01:29 -05:00
|
|
|
String::from_utf8(wr).unwrap()
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) {
|
|
|
|
// hack to skip the loop unless debug! is enabled:
|
|
|
|
debug!(
|
|
|
|
"^^ liveness computation results for body {} (entry={:?})",
|
|
|
|
{
|
2020-09-26 00:00:00 +00:00
|
|
|
for ln_idx in 0..self.ir.lnks.len() {
|
2020-05-22 00:00:00 +00:00
|
|
|
debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
|
|
|
|
}
|
|
|
|
hir_id
|
|
|
|
},
|
|
|
|
entry_ln
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
2014-10-23 08:42:21 -07:00
|
|
|
self.successors[ln.get()] = succ_ln;
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-09-21 20:42:49 +10:00
|
|
|
// It is not necessary to initialize the RWUs here because they are all
|
|
|
|
// set to INV_INV_FALSE when they are created, and the sets only grow
|
|
|
|
// during iterations.
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
fn init_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode) {
|
2012-05-19 05:52:01 -07:00
|
|
|
// more efficient version of init_empty() / merge_from_succ()
|
2014-10-23 08:42:21 -07:00
|
|
|
self.successors[ln.get()] = succ_ln;
|
2013-12-22 14:08:42 -08:00
|
|
|
|
2014-03-06 15:46:26 +02:00
|
|
|
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
2018-09-21 20:42:49 +10:00
|
|
|
this.rwu_table.copy_packed(idx, succ_idx);
|
2012-06-26 13:55:56 -07:00
|
|
|
});
|
2019-12-22 17:42:04 -05:00
|
|
|
debug!("init_from_succ(ln={}, succ={})", self.ln_str(ln), self.ln_str(succ_ln));
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn merge_from_succ(&mut self, ln: LiveNode, succ_ln: LiveNode, first_merge: bool) -> bool {
|
|
|
|
if ln == succ_ln {
|
|
|
|
return false;
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2020-02-03 12:11:11 +11:00
|
|
|
let mut any_changed = false;
|
2014-03-06 15:46:26 +02:00
|
|
|
self.indices2(ln, succ_ln, |this, idx, succ_idx| {
|
2020-02-03 13:32:59 +11:00
|
|
|
// This is a special case, pulled out from the code below, where we
|
|
|
|
// don't have to do anything. It occurs about 60-70% of the time.
|
|
|
|
if this.rwu_table.packed_rwus[succ_idx] == INV_INV_FALSE {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-03 12:11:11 +11:00
|
|
|
let mut changed = false;
|
2018-09-21 20:42:49 +10:00
|
|
|
let mut rwu = this.rwu_table.get(idx);
|
|
|
|
let succ_rwu = this.rwu_table.get(succ_idx);
|
|
|
|
if succ_rwu.reader.is_valid() && !rwu.reader.is_valid() {
|
|
|
|
rwu.reader = succ_rwu.reader;
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if succ_rwu.writer.is_valid() && !rwu.writer.is_valid() {
|
|
|
|
rwu.writer = succ_rwu.writer;
|
|
|
|
changed = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if succ_rwu.used && !rwu.used {
|
|
|
|
rwu.used = true;
|
2012-05-23 20:53:49 -07:00
|
|
|
changed = true;
|
|
|
|
}
|
2018-09-21 20:42:49 +10:00
|
|
|
|
|
|
|
if changed {
|
|
|
|
this.rwu_table.assign_unpacked(idx, rwu);
|
2020-02-03 12:11:11 +11:00
|
|
|
any_changed = true;
|
2018-09-21 20:42:49 +10:00
|
|
|
}
|
2013-11-21 15:42:55 -08:00
|
|
|
});
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
debug!(
|
|
|
|
"merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
|
|
|
|
ln,
|
|
|
|
self.ln_str(succ_ln),
|
|
|
|
first_merge,
|
2020-02-03 12:11:11 +11:00
|
|
|
any_changed
|
2019-12-22 17:42:04 -05:00
|
|
|
);
|
2020-03-20 15:03:11 +01:00
|
|
|
any_changed
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Indicates that a local variable was *defined*; we know that no
|
|
|
|
// uses of the variable can precede the definition (resolve checks
|
|
|
|
// this) so we just clear out all the data.
|
2014-03-06 15:46:26 +02:00
|
|
|
fn define(&mut self, writer: LiveNode, var: Variable) {
|
2012-05-19 05:52:01 -07:00
|
|
|
let idx = self.idx(writer, var);
|
2018-09-21 20:42:49 +10:00
|
|
|
self.rwu_table.assign_inv_inv(idx);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
debug!("{:?} defines {:?} (idx={}): {}", writer, var, idx, self.ln_str(writer));
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Either read, write, or both depending on the acc bitset
|
2015-02-27 01:43:55 +01:00
|
|
|
fn acc(&mut self, ln: LiveNode, var: Variable, acc: u32) {
|
2019-12-22 17:42:04 -05:00
|
|
|
debug!("{:?} accesses[{:x}] {:?}: {}", ln, acc, var, self.ln_str(ln));
|
2014-03-06 15:46:26 +02:00
|
|
|
|
2012-05-23 20:53:49 -07:00
|
|
|
let idx = self.idx(ln, var);
|
2018-09-21 20:42:49 +10:00
|
|
|
let mut rwu = self.rwu_table.get(idx);
|
2012-05-23 20:53:49 -07:00
|
|
|
|
2012-10-18 12:20:18 -07:00
|
|
|
if (acc & ACC_WRITE) != 0 {
|
2018-09-21 20:42:49 +10:00
|
|
|
rwu.reader = invalid_node();
|
|
|
|
rwu.writer = ln;
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// Important: if we both read/write, must do read second
|
|
|
|
// or else the write will override.
|
2012-10-18 12:20:18 -07:00
|
|
|
if (acc & ACC_READ) != 0 {
|
2018-09-21 20:42:49 +10:00
|
|
|
rwu.reader = ln;
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
|
2012-10-18 12:20:18 -07:00
|
|
|
if (acc & ACC_USE) != 0 {
|
2018-09-21 20:42:49 +10:00
|
|
|
rwu.used = true;
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
2018-09-21 20:42:49 +10:00
|
|
|
|
|
|
|
self.rwu_table.assign_unpacked(idx, rwu);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
fn compute(
|
|
|
|
&mut self,
|
|
|
|
fk: FnKind<'_>,
|
|
|
|
body: &hir::Body<'_>,
|
|
|
|
span: Span,
|
|
|
|
id: hir::HirId,
|
|
|
|
) -> LiveNode {
|
|
|
|
debug!("compute: using id for body, {:?}", body.value);
|
2012-10-18 12:20:18 -07:00
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
// # Liveness of captured variables
|
|
|
|
//
|
|
|
|
// When computing the liveness for captured variables we take into
|
|
|
|
// account how variable is captured (ByRef vs ByValue) and what is the
|
|
|
|
// closure kind (Generator / FnOnce vs Fn / FnMut).
|
|
|
|
//
|
|
|
|
// Variables captured by reference are assumed to be used on the exit
|
|
|
|
// from the closure.
|
|
|
|
//
|
|
|
|
// In FnOnce closures, variables captured by value are known to be dead
|
|
|
|
// on exit since it is impossible to call the closure again.
|
|
|
|
//
|
|
|
|
// In Fn / FnMut closures, variables captured by value are live on exit
|
|
|
|
// if they are live on the entry to the closure, since only the closure
|
|
|
|
// itself can access them on subsequent calls.
|
2020-05-21 00:00:00 +00:00
|
|
|
|
|
|
|
if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
|
2020-05-22 00:00:00 +00:00
|
|
|
// Mark upvars captured by reference as used after closure exits.
|
2020-05-21 00:00:00 +00:00
|
|
|
for (&var_hir_id, upvar) in upvars.iter().rev() {
|
2020-05-22 00:00:00 +00:00
|
|
|
let upvar_id = ty::UpvarId {
|
|
|
|
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
2020-06-27 13:09:54 +02:00
|
|
|
closure_expr_id: self.ir.body_owner,
|
2020-05-22 00:00:00 +00:00
|
|
|
};
|
2020-07-17 08:47:04 +00:00
|
|
|
match self.typeck_results.upvar_capture(upvar_id) {
|
2020-05-22 00:00:00 +00:00
|
|
|
ty::UpvarCapture::ByRef(_) => {
|
|
|
|
let var = self.variable(var_hir_id, upvar.span);
|
|
|
|
self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
|
|
|
|
}
|
2020-08-25 23:58:58 -04:00
|
|
|
ty::UpvarCapture::ByValue(_) => {}
|
2020-05-22 00:00:00 +00:00
|
|
|
}
|
2020-05-21 00:00:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
let succ = self.propagate_through_expr(&body.value, self.s.exit_ln);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
match fk {
|
|
|
|
FnKind::Method(..) | FnKind::ItemFn(..) => return succ,
|
|
|
|
FnKind::Closure(..) => {}
|
|
|
|
}
|
|
|
|
|
2020-07-17 08:47:04 +00:00
|
|
|
let ty = self.typeck_results.node_type(id);
|
2020-08-03 00:49:11 +02:00
|
|
|
match ty.kind() {
|
2020-05-22 00:00:00 +00:00
|
|
|
ty::Closure(_def_id, substs) => match substs.as_closure().kind() {
|
|
|
|
ty::ClosureKind::Fn => {}
|
|
|
|
ty::ClosureKind::FnMut => {}
|
|
|
|
ty::ClosureKind::FnOnce => return succ,
|
2019-12-22 17:42:04 -05:00
|
|
|
},
|
2020-05-22 00:00:00 +00:00
|
|
|
ty::Generator(..) => return succ,
|
|
|
|
_ => {
|
|
|
|
span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,);
|
|
|
|
}
|
|
|
|
};
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
// Propagate through calls to the closure.
|
|
|
|
let mut first_merge = true;
|
|
|
|
loop {
|
|
|
|
self.init_from_succ(self.s.closure_ln, succ);
|
|
|
|
for param in body.params {
|
|
|
|
param.pat.each_binding(|_bm, hir_id, _x, ident| {
|
|
|
|
let var = self.variable(hir_id, ident.span);
|
|
|
|
self.define(self.s.closure_ln, var);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if !self.merge_from_succ(self.s.exit_ln, self.s.closure_ln, first_merge) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
first_merge = false;
|
|
|
|
assert_eq!(succ, self.propagate_through_expr(&body.value, self.s.exit_ln));
|
|
|
|
}
|
|
|
|
|
|
|
|
succ
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode {
|
2017-03-22 11:40:29 -04:00
|
|
|
if blk.targeted_by_break {
|
2019-03-07 12:43:27 +01:00
|
|
|
self.break_ln.insert(blk.hir_id, succ);
|
2017-02-28 11:05:03 -08:00
|
|
|
}
|
Fix clippy warnings
Fixes clippy::{cone_on_copy, filter_next, redundant_closure, single_char_pattern, len_zero,redundant_field_names, useless_format, identity_conversion, map_clone, into_iter_on_ref, needless_return, option_as_ref_deref, unused_unit, unnecessary_mut_passed}
2020-05-11 13:01:37 +02:00
|
|
|
let succ = self.propagate_through_opt_expr(blk.expr.as_deref(), succ);
|
2019-12-22 17:42:04 -05:00
|
|
|
blk.stmts.iter().rev().fold(succ, |succ, stmt| self.propagate_through_stmt(stmt, succ))
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode {
|
2019-09-26 17:34:50 +01:00
|
|
|
match stmt.kind {
|
2019-01-17 10:39:24 +11:00
|
|
|
hir::StmtKind::Local(ref local) => {
|
|
|
|
// Note: we mark the variable as defined regardless of whether
|
|
|
|
// there is an initializer. Initially I had thought to only mark
|
|
|
|
// the live variable as defined if it was initialized, and then we
|
|
|
|
// could check for uninit variables just by scanning what is live
|
|
|
|
// at the start of the function. But that doesn't work so well for
|
|
|
|
// immutable variables defined in a loop:
|
|
|
|
// loop { let x; x = 5; }
|
|
|
|
// because the "assignment" loops back around and generates an error.
|
|
|
|
//
|
|
|
|
// So now we just check that variables defined w/o an
|
|
|
|
// initializer are not live at the point of their
|
|
|
|
// initialization, which is mildly more complex than checking
|
|
|
|
// once at the func header but otherwise equivalent.
|
|
|
|
|
Fix clippy warnings
Fixes clippy::{cone_on_copy, filter_next, redundant_closure, single_char_pattern, len_zero,redundant_field_names, useless_format, identity_conversion, map_clone, into_iter_on_ref, needless_return, option_as_ref_deref, unused_unit, unnecessary_mut_passed}
2020-05-11 13:01:37 +02:00
|
|
|
let succ = self.propagate_through_opt_expr(local.init.as_deref(), succ);
|
2019-01-17 10:39:24 +11:00
|
|
|
self.define_bindings_in_pat(&local.pat, succ)
|
2014-03-06 15:46:26 +02:00
|
|
|
}
|
2019-01-17 10:39:24 +11:00
|
|
|
hir::StmtKind::Item(..) => succ,
|
2019-01-17 09:45:02 +11:00
|
|
|
hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
|
2016-02-09 22:00:20 +01:00
|
|
|
self.propagate_through_expr(&expr, succ)
|
2014-03-06 15:46:26 +02:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_exprs(&mut self, exprs: &[Expr<'_>], succ: LiveNode) -> LiveNode {
|
2019-12-22 17:42:04 -05:00
|
|
|
exprs.iter().rev().fold(succ, |succ, expr| self.propagate_through_expr(&expr, succ))
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_opt_expr(
|
|
|
|
&mut self,
|
|
|
|
opt_expr: Option<&Expr<'_>>,
|
|
|
|
succ: LiveNode,
|
|
|
|
) -> LiveNode {
|
2014-10-24 21:14:37 +02:00
|
|
|
opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_expr(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
|
2020-03-24 02:44:41 +01:00
|
|
|
debug!("propagate_through_expr: {:?}", expr);
|
2012-10-18 12:20:18 -07:00
|
|
|
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2018-10-02 18:29:48 +02:00
|
|
|
// Interesting cases with control flow or which gen/kill
|
|
|
|
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
|
|
|
self.access_path(expr.hir_id, path, succ, ACC_READ | ACC_USE)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ),
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-01-03 23:20:44 +09:00
|
|
|
hir::ExprKind::Closure(..) => {
|
2020-03-24 02:44:41 +01:00
|
|
|
debug!("{:?} is an ExprKind::Closure", expr);
|
2012-10-18 12:20:18 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// the construction of a closure itself is not important,
|
|
|
|
// but we have to consider the closed over variables.
|
2019-12-22 17:42:04 -05:00
|
|
|
let caps = self
|
|
|
|
.ir
|
|
|
|
.capture_info_map
|
|
|
|
.get(&expr.hir_id)
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_else(|| span_bug!(expr.span, "no registered caps"));
|
2018-10-02 18:05:06 +02:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
caps.iter().rev().fold(succ, |succ, cap| {
|
|
|
|
self.init_from_succ(cap.ln, succ);
|
|
|
|
let var = self.variable(cap.var_hid, expr.span);
|
|
|
|
self.acc(cap.ln, var, ACC_READ | ACC_USE);
|
|
|
|
cap.ln
|
|
|
|
})
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Note that labels have been resolved, so we don't need to look
|
|
|
|
// at the label ident
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Loop(ref blk, _, _) => self.propagate_through_loop(expr, &blk, succ),
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
hir::ExprKind::Match(ref e, arms, _) => {
|
2018-10-02 18:29:48 +02:00
|
|
|
//
|
|
|
|
// (e)
|
|
|
|
// |
|
|
|
|
// v
|
|
|
|
// (expr)
|
|
|
|
// / | \
|
|
|
|
// | | |
|
|
|
|
// v v v
|
|
|
|
// (..arms..)
|
|
|
|
// | | |
|
|
|
|
// v v v
|
|
|
|
// ( succ )
|
|
|
|
//
|
|
|
|
//
|
|
|
|
let ln = self.live_node(expr.hir_id, expr.span);
|
|
|
|
self.init_empty(ln, succ);
|
|
|
|
let mut first_merge = true;
|
|
|
|
for arm in arms {
|
2018-10-02 18:05:06 +02:00
|
|
|
let body_succ = self.propagate_through_expr(&arm.body, succ);
|
|
|
|
|
|
|
|
let guard_succ = self.propagate_through_opt_expr(
|
2019-11-30 15:08:22 +01:00
|
|
|
arm.guard.as_ref().map(|hir::Guard::If(e)| *e),
|
2019-12-22 17:42:04 -05:00
|
|
|
body_succ,
|
2018-10-02 18:05:06 +02:00
|
|
|
);
|
2019-09-15 03:41:21 +02:00
|
|
|
let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
|
2018-10-02 18:29:48 +02:00
|
|
|
self.merge_from_succ(ln, arm_succ, first_merge);
|
|
|
|
first_merge = false;
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2018-10-02 18:29:48 +02:00
|
|
|
self.propagate_through_expr(&e, ln)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Ret(ref o_e) => {
|
|
|
|
// ignore succ and subst exit_ln:
|
|
|
|
let exit_ln = self.s.exit_ln;
|
|
|
|
self.propagate_through_opt_expr(o_e.as_ref().map(|e| &**e), exit_ln)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Break(label, ref opt_expr) => {
|
|
|
|
// Find which label this break jumps to
|
|
|
|
let target = match label.target_id {
|
2019-03-07 12:43:27 +01:00
|
|
|
Ok(hir_id) => self.break_ln.get(&hir_id),
|
2018-05-14 19:07:05 +02:00
|
|
|
Err(err) => span_bug!(expr.span, "loop scope error: {}", err),
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
|
|
|
.cloned();
|
2012-08-14 19:20:56 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Now that we know the label we're going to,
|
|
|
|
// look it up in the break loop nodes table
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
match target {
|
|
|
|
Some(b) => self.propagate_through_opt_expr(opt_expr.as_ref().map(|e| &**e), b),
|
2020-06-25 15:16:38 +01:00
|
|
|
None => span_bug!(expr.span, "`break` to unknown label"),
|
2018-10-02 18:29:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hir::ExprKind::Continue(label) => {
|
|
|
|
// Find which label this expr continues to
|
2019-12-22 17:42:04 -05:00
|
|
|
let sc = label
|
|
|
|
.target_id
|
|
|
|
.unwrap_or_else(|err| span_bug!(expr.span, "loop scope error: {}", err));
|
2017-02-15 23:28:59 -08:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Now that we know the label we're going to,
|
|
|
|
// look it up in the continue loop nodes table
|
2019-12-22 17:42:04 -05:00
|
|
|
self.cont_ln
|
|
|
|
.get(&sc)
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_else(|| span_bug!(expr.span, "continue to unknown label"))
|
2018-10-02 18:29:48 +02:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 21:08:53 +00:00
|
|
|
hir::ExprKind::Assign(ref l, ref r, _) => {
|
2018-01-29 01:49:29 +02:00
|
|
|
// see comment on places in
|
|
|
|
// propagate_through_place_components()
|
2018-10-02 18:29:48 +02:00
|
|
|
let succ = self.write_place(&l, succ, ACC_WRITE);
|
|
|
|
let succ = self.propagate_through_place_components(&l, succ);
|
|
|
|
self.propagate_through_expr(&r, succ)
|
2016-03-04 22:52:34 +01:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::AssignOp(_, ref l, ref r) => {
|
|
|
|
// an overloaded assign op is like a method call
|
2020-07-17 08:47:04 +00:00
|
|
|
if self.typeck_results.is_method_call(expr) {
|
2018-10-02 18:29:48 +02:00
|
|
|
let succ = self.propagate_through_expr(&l, succ);
|
|
|
|
self.propagate_through_expr(&r, succ)
|
|
|
|
} else {
|
|
|
|
// see comment on places in
|
|
|
|
// propagate_through_place_components()
|
2019-12-22 17:42:04 -05:00
|
|
|
let succ = self.write_place(&l, succ, ACC_WRITE | ACC_READ);
|
2018-10-02 18:29:48 +02:00
|
|
|
let succ = self.propagate_through_expr(&r, succ);
|
|
|
|
self.propagate_through_place_components(&l, succ)
|
|
|
|
}
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Uninteresting cases: just propagate in rev exec order
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Array(ref exprs) => self.propagate_through_exprs(exprs, succ),
|
2012-07-23 16:39:18 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Struct(_, ref fields, ref with_expr) => {
|
|
|
|
let succ = self.propagate_through_opt_expr(with_expr.as_ref().map(|e| &**e), succ);
|
2019-12-22 17:42:04 -05:00
|
|
|
fields
|
|
|
|
.iter()
|
|
|
|
.rev()
|
|
|
|
.fold(succ, |succ, field| self.propagate_through_expr(&field.expr, succ))
|
2018-10-02 18:29:48 +02:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Call(ref f, ref args) => {
|
2020-03-18 20:27:59 +02:00
|
|
|
let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id();
|
2020-03-16 18:51:55 +01:00
|
|
|
let succ = if self.ir.tcx.is_ty_uninhabited_from(
|
|
|
|
m,
|
2020-07-17 08:47:04 +00:00
|
|
|
self.typeck_results.expr_ty(expr),
|
2020-03-16 18:51:55 +01:00
|
|
|
self.param_env,
|
|
|
|
) {
|
2018-10-02 18:29:48 +02:00
|
|
|
self.s.exit_ln
|
|
|
|
} else {
|
|
|
|
succ
|
|
|
|
};
|
|
|
|
let succ = self.propagate_through_exprs(args, succ);
|
|
|
|
self.propagate_through_expr(&f, succ)
|
|
|
|
}
|
2012-11-30 11:18:25 -08:00
|
|
|
|
2020-06-09 15:34:23 -04:00
|
|
|
hir::ExprKind::MethodCall(.., ref args, _) => {
|
2020-03-18 20:27:59 +02:00
|
|
|
let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id();
|
2020-03-16 18:51:55 +01:00
|
|
|
let succ = if self.ir.tcx.is_ty_uninhabited_from(
|
|
|
|
m,
|
2020-07-17 08:47:04 +00:00
|
|
|
self.typeck_results.expr_ty(expr),
|
2020-03-16 18:51:55 +01:00
|
|
|
self.param_env,
|
|
|
|
) {
|
2018-10-02 18:29:48 +02:00
|
|
|
self.s.exit_ln
|
|
|
|
} else {
|
|
|
|
succ
|
|
|
|
};
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
self.propagate_through_exprs(args, succ)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Tup(ref exprs) => self.propagate_through_exprs(exprs, succ),
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Binary(op, ref l, ref r) if op.node.is_lazy() => {
|
|
|
|
let r_succ = self.propagate_through_expr(&r, succ);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
let ln = self.live_node(expr.hir_id, expr.span);
|
|
|
|
self.init_from_succ(ln, succ);
|
|
|
|
self.merge_from_succ(ln, r_succ, false);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
self.propagate_through_expr(&l, ln)
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Index(ref l, ref r) | hir::ExprKind::Binary(_, ref l, ref r) => {
|
2018-10-02 18:29:48 +02:00
|
|
|
let r_succ = self.propagate_through_expr(&r, succ);
|
|
|
|
self.propagate_through_expr(&l, r_succ)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Box(ref e)
|
|
|
|
| hir::ExprKind::AddrOf(_, _, ref e)
|
|
|
|
| hir::ExprKind::Cast(ref e, _)
|
|
|
|
| hir::ExprKind::Type(ref e, _)
|
|
|
|
| hir::ExprKind::DropTemps(ref e)
|
|
|
|
| hir::ExprKind::Unary(_, ref e)
|
|
|
|
| hir::ExprKind::Yield(ref e, _)
|
|
|
|
| hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(&e, succ),
|
2018-10-02 18:29:48 +02:00
|
|
|
|
2020-02-13 11:00:55 +00:00
|
|
|
hir::ExprKind::InlineAsm(ref asm) => {
|
|
|
|
// Handle non-returning asm
|
|
|
|
let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) {
|
|
|
|
self.s.exit_ln
|
|
|
|
} else {
|
|
|
|
succ
|
|
|
|
};
|
|
|
|
|
|
|
|
// Do a first pass for writing outputs only
|
|
|
|
for op in asm.operands.iter().rev() {
|
|
|
|
match op {
|
|
|
|
hir::InlineAsmOperand::In { .. }
|
|
|
|
| hir::InlineAsmOperand::Const { .. }
|
|
|
|
| hir::InlineAsmOperand::Sym { .. } => {}
|
|
|
|
hir::InlineAsmOperand::Out { expr, .. } => {
|
|
|
|
if let Some(expr) = expr {
|
|
|
|
succ = self.write_place(expr, succ, ACC_WRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::InOut { expr, .. } => {
|
|
|
|
succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE);
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::SplitInOut { out_expr, .. } => {
|
|
|
|
if let Some(expr) = out_expr {
|
|
|
|
succ = self.write_place(expr, succ, ACC_WRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then do a second pass for inputs
|
|
|
|
let mut succ = succ;
|
|
|
|
for op in asm.operands.iter().rev() {
|
|
|
|
match op {
|
|
|
|
hir::InlineAsmOperand::In { expr, .. }
|
|
|
|
| hir::InlineAsmOperand::Const { expr, .. }
|
|
|
|
| hir::InlineAsmOperand::Sym { expr, .. } => {
|
|
|
|
succ = self.propagate_through_expr(expr, succ)
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::Out { expr, .. } => {
|
|
|
|
if let Some(expr) = expr {
|
|
|
|
succ = self.propagate_through_place_components(expr, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::InOut { expr, .. } => {
|
|
|
|
succ = self.propagate_through_place_components(expr, succ);
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
|
|
|
if let Some(expr) = out_expr {
|
|
|
|
succ = self.propagate_through_place_components(expr, succ);
|
|
|
|
}
|
|
|
|
succ = self.propagate_through_expr(in_expr, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
succ
|
|
|
|
}
|
|
|
|
|
2020-01-14 13:40:42 +00:00
|
|
|
hir::ExprKind::LlvmInlineAsm(ref asm) => {
|
2019-11-18 14:43:34 +01:00
|
|
|
let ia = &asm.inner;
|
2019-11-30 15:08:22 +01:00
|
|
|
let outputs = asm.outputs_exprs;
|
|
|
|
let inputs = asm.inputs_exprs;
|
2018-10-02 18:29:48 +02:00
|
|
|
let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
|
2019-11-18 14:43:34 +01:00
|
|
|
// see comment on places
|
|
|
|
// in propagate_through_place_components()
|
|
|
|
if o.is_indirect {
|
|
|
|
self.propagate_through_expr(output, succ)
|
|
|
|
} else {
|
2019-12-22 17:42:04 -05:00
|
|
|
let acc = if o.is_rw { ACC_WRITE | ACC_READ } else { ACC_WRITE };
|
2019-11-18 14:43:34 +01:00
|
|
|
let succ = self.write_place(output, succ, acc);
|
|
|
|
self.propagate_through_place_components(output, succ)
|
|
|
|
}
|
|
|
|
});
|
2016-03-09 22:17:02 +02:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Inputs are executed first. Propagate last because of rev order
|
|
|
|
self.propagate_through_exprs(inputs, succ)
|
|
|
|
}
|
2013-03-12 17:53:25 -07:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Lit(..)
|
|
|
|
| hir::ExprKind::Err
|
2020-08-04 14:34:24 +01:00
|
|
|
| hir::ExprKind::Path(hir::QPath::TypeRelative(..))
|
|
|
|
| hir::ExprKind::Path(hir::QPath::LangItem(..)) => succ,
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Note that labels have been resolved, so we don't need to look
|
|
|
|
// at the label ident
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Block(ref blk, _) => self.propagate_through_block(&blk, succ),
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn propagate_through_place_components(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
|
2018-01-29 01:49:29 +02:00
|
|
|
// # Places
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
|
|
|
// In general, the full flow graph structure for an
|
|
|
|
// assignment/move/etc can be handled in one of two ways,
|
|
|
|
// depending on whether what is being assigned is a "tracked
|
2012-10-08 11:49:01 -07:00
|
|
|
// value" or not. A tracked value is basically a local
|
|
|
|
// variable or argument.
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
|
|
|
// The two kinds of graphs are:
|
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// Tracked place Untracked place
|
2012-05-19 05:52:01 -07:00
|
|
|
// ----------------------++-----------------------
|
|
|
|
// ||
|
|
|
|
// | || |
|
|
|
|
// v || v
|
|
|
|
// (rvalue) || (rvalue)
|
|
|
|
// | || |
|
2012-05-25 00:14:40 -07:00
|
|
|
// v || v
|
2018-01-29 01:49:29 +02:00
|
|
|
// (write of place) || (place components)
|
2012-05-19 05:52:01 -07:00
|
|
|
// | || |
|
|
|
|
// v || v
|
|
|
|
// (succ) || (succ)
|
|
|
|
// ||
|
|
|
|
// ----------------------++-----------------------
|
|
|
|
//
|
|
|
|
// I will cover the two cases in turn:
|
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// # Tracked places
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// A tracked place is a local variable/argument `x`. In
|
2012-05-19 05:52:01 -07:00
|
|
|
// these cases, the link_node where the write occurs is linked
|
2018-01-29 01:49:29 +02:00
|
|
|
// to node id of `x`. The `write_place()` routine generates
|
2012-10-08 11:49:01 -07:00
|
|
|
// the contents of this node. There are no subcomponents to
|
|
|
|
// consider.
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// # Non-tracked places
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// These are places like `x[5]` or `x.f`. In that case, we
|
2012-05-19 05:52:01 -07:00
|
|
|
// basically ignore the value which is written to but generate
|
|
|
|
// reads for the components---`x` in these two examples. The
|
|
|
|
// components reads are generated by
|
2018-01-29 01:49:29 +02:00
|
|
|
// `propagate_through_place_components()` (this fn).
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// # Illegal places
|
2012-05-19 05:52:01 -07:00
|
|
|
//
|
2018-01-29 01:49:29 +02:00
|
|
|
// It is still possible to observe assignments to non-places;
|
2012-05-19 05:52:01 -07:00
|
|
|
// these errors are detected in the later pass borrowck. We
|
|
|
|
// just ignore such cases and treat them as reads.
|
|
|
|
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2018-07-11 20:05:29 +08:00
|
|
|
hir::ExprKind::Path(_) => succ,
|
|
|
|
hir::ExprKind::Field(ref e, _) => self.propagate_through_expr(&e, succ),
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => self.propagate_through_expr(expr, succ),
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 01:49:29 +02:00
|
|
|
// see comment on propagate_through_place()
|
2019-11-30 15:08:22 +01:00
|
|
|
fn write_place(&mut self, expr: &Expr<'_>, succ: LiveNode, acc: u32) -> LiveNode {
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
|
|
|
self.access_path(expr.hir_id, path, succ, acc)
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// We do not track other places, so just propagate through
|
|
|
|
// to their subcomponents. Also, it may happen that
|
|
|
|
// non-places occur here, because those are detected in the
|
|
|
|
// later pass borrowck.
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => succ,
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn access_var(
|
|
|
|
&mut self,
|
|
|
|
hir_id: HirId,
|
|
|
|
var_hid: HirId,
|
|
|
|
succ: LiveNode,
|
|
|
|
acc: u32,
|
|
|
|
span: Span,
|
|
|
|
) -> LiveNode {
|
2018-05-20 20:19:34 -07:00
|
|
|
let ln = self.live_node(hir_id, span);
|
2016-12-26 14:34:03 +01:00
|
|
|
if acc != 0 {
|
|
|
|
self.init_from_succ(ln, succ);
|
2018-05-20 20:19:34 -07:00
|
|
|
let var = self.variable(var_hid, span);
|
2016-12-26 14:34:03 +01:00
|
|
|
self.acc(ln, var, acc);
|
|
|
|
}
|
|
|
|
ln
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
fn access_path(
|
|
|
|
&mut self,
|
|
|
|
hir_id: HirId,
|
2019-12-01 16:08:58 +01:00
|
|
|
path: &hir::Path<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
succ: LiveNode,
|
|
|
|
acc: u32,
|
|
|
|
) -> LiveNode {
|
2019-04-20 19:36:05 +03:00
|
|
|
match path.res {
|
2020-05-21 00:00:00 +00:00
|
|
|
Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span),
|
2019-12-22 17:42:04 -05:00
|
|
|
_ => succ,
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-19 17:21:28 +02:00
|
|
|
fn propagate_through_loop(
|
|
|
|
&mut self,
|
2019-11-30 15:08:22 +01:00
|
|
|
expr: &Expr<'_>,
|
|
|
|
body: &hir::Block<'_>,
|
2019-12-22 17:42:04 -05:00
|
|
|
succ: LiveNode,
|
2019-06-19 17:21:28 +02:00
|
|
|
) -> LiveNode {
|
2012-05-19 05:52:01 -07:00
|
|
|
/*
|
|
|
|
We model control flow like this:
|
|
|
|
|
2019-06-26 14:51:01 +02:00
|
|
|
(expr) <-+
|
|
|
|
| |
|
|
|
|
v |
|
|
|
|
(body) --+
|
2012-05-19 05:52:01 -07:00
|
|
|
|
2019-06-26 14:51:01 +02:00
|
|
|
Note that a `continue` expression targeting the `loop` will have a successor of `expr`.
|
|
|
|
Meanwhile, a `break` expression will have a successor of `succ`.
|
2012-05-19 05:52:01 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
// first iteration:
|
|
|
|
let mut first_merge = true;
|
2018-05-20 20:19:34 -07:00
|
|
|
let ln = self.live_node(expr.hir_id, expr.span);
|
2012-05-19 05:52:01 -07:00
|
|
|
self.init_empty(ln, succ);
|
2020-03-24 02:44:41 +01:00
|
|
|
debug!("propagate_through_loop: using id for loop body {} {:?}", expr.hir_id, body);
|
2012-10-18 12:20:18 -07:00
|
|
|
|
2019-03-07 12:43:27 +01:00
|
|
|
self.break_ln.insert(expr.hir_id, succ);
|
2017-02-28 11:05:03 -08:00
|
|
|
|
2019-06-19 17:21:28 +02:00
|
|
|
self.cont_ln.insert(expr.hir_id, ln);
|
2019-01-03 21:49:56 +09:00
|
|
|
|
2019-06-19 17:21:28 +02:00
|
|
|
let body_ln = self.propagate_through_block(body, ln);
|
2012-05-19 05:52:01 -07:00
|
|
|
|
|
|
|
// repeat until fixed point is reached:
|
|
|
|
while self.merge_from_succ(ln, body_ln, first_merge) {
|
|
|
|
first_merge = false;
|
2019-06-19 17:21:28 +02:00
|
|
|
assert_eq!(body_ln, self.propagate_through_block(body, ln));
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-06-19 17:21:28 +02:00
|
|
|
ln
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// _______________________________________________________________________
|
|
|
|
// Checking for error conditions
|
|
|
|
|
2017-01-06 21:54:24 +02:00
|
|
|
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
2020-03-11 12:05:32 +01:00
|
|
|
type Map = intravisit::ErasedMap<'tcx>;
|
2020-01-07 17:25:33 +01:00
|
|
|
|
2020-02-09 15:32:00 +01:00
|
|
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
2017-01-06 21:54:24 +02:00
|
|
|
NestedVisitorMap::None
|
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
2019-09-15 03:41:21 +02:00
|
|
|
self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
|
|
|
|
if local.init.is_some() {
|
|
|
|
self.warn_about_dead_assign(spans, hir_id, ln, var);
|
|
|
|
}
|
|
|
|
});
|
2017-01-06 21:54:24 +02:00
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
intravisit::walk_local(self, local);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
2019-09-15 03:41:21 +02:00
|
|
|
check_expr(self, ex);
|
2019-01-25 16:56:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
2019-09-15 03:41:21 +02:00
|
|
|
self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
|
|
|
|
intravisit::walk_arm(self, arm);
|
2019-01-25 16:56:27 +01:00
|
|
|
}
|
2012-08-24 11:04:07 -07:00
|
|
|
}
|
|
|
|
|
2019-11-30 15:08:22 +01:00
|
|
|
fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2019-12-22 21:08:53 +00:00
|
|
|
hir::ExprKind::Assign(ref l, ..) => {
|
2018-01-29 01:49:29 +02:00
|
|
|
this.check_place(&l);
|
2016-03-04 21:01:59 +01:00
|
|
|
}
|
2012-05-23 20:53:49 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
hir::ExprKind::AssignOp(_, ref l, _) => {
|
2020-07-17 08:47:04 +00:00
|
|
|
if !this.typeck_results.is_method_call(expr) {
|
2018-10-02 18:29:48 +02:00
|
|
|
this.check_place(&l);
|
|
|
|
}
|
2013-03-12 17:53:25 -07:00
|
|
|
}
|
|
|
|
|
2020-02-13 11:00:55 +00:00
|
|
|
hir::ExprKind::InlineAsm(ref asm) => {
|
|
|
|
for op in asm.operands {
|
|
|
|
match op {
|
|
|
|
hir::InlineAsmOperand::Out { expr, .. } => {
|
|
|
|
if let Some(expr) = expr {
|
|
|
|
this.check_place(expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hir::InlineAsmOperand::InOut { expr, .. } => {
|
|
|
|
this.check_place(expr);
|
|
|
|
}
|
2020-05-24 14:26:20 +01:00
|
|
|
hir::InlineAsmOperand::SplitInOut { out_expr, .. } => {
|
2020-02-13 11:00:55 +00:00
|
|
|
if let Some(out_expr) = out_expr {
|
|
|
|
this.check_place(out_expr);
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 14:26:20 +01:00
|
|
|
_ => {}
|
2020-02-13 11:00:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-14 13:40:42 +00:00
|
|
|
hir::ExprKind::LlvmInlineAsm(ref asm) => {
|
2019-11-30 15:08:22 +01:00
|
|
|
for input in asm.inputs_exprs {
|
2018-10-02 18:29:48 +02:00
|
|
|
this.visit_expr(input);
|
|
|
|
}
|
2013-03-12 17:53:25 -07:00
|
|
|
|
2018-10-02 18:29:48 +02:00
|
|
|
// Output operands must be places
|
2019-11-30 15:08:22 +01:00
|
|
|
for (o, output) in asm.inner.outputs.iter().zip(asm.outputs_exprs) {
|
2018-10-02 18:29:48 +02:00
|
|
|
if !o.is_indirect {
|
|
|
|
this.check_place(output);
|
|
|
|
}
|
|
|
|
this.visit_expr(output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no correctness conditions related to liveness
|
2019-12-22 17:42:04 -05:00
|
|
|
hir::ExprKind::Call(..)
|
|
|
|
| hir::ExprKind::MethodCall(..)
|
|
|
|
| hir::ExprKind::Match(..)
|
|
|
|
| hir::ExprKind::Loop(..)
|
|
|
|
| hir::ExprKind::Index(..)
|
|
|
|
| hir::ExprKind::Field(..)
|
|
|
|
| hir::ExprKind::Array(..)
|
|
|
|
| hir::ExprKind::Tup(..)
|
|
|
|
| hir::ExprKind::Binary(..)
|
|
|
|
| hir::ExprKind::Cast(..)
|
|
|
|
| hir::ExprKind::DropTemps(..)
|
|
|
|
| hir::ExprKind::Unary(..)
|
|
|
|
| hir::ExprKind::Ret(..)
|
|
|
|
| hir::ExprKind::Break(..)
|
|
|
|
| hir::ExprKind::Continue(..)
|
|
|
|
| hir::ExprKind::Lit(_)
|
|
|
|
| hir::ExprKind::Block(..)
|
|
|
|
| hir::ExprKind::AddrOf(..)
|
|
|
|
| hir::ExprKind::Struct(..)
|
|
|
|
| hir::ExprKind::Repeat(..)
|
|
|
|
| hir::ExprKind::Closure(..)
|
|
|
|
| hir::ExprKind::Path(_)
|
|
|
|
| hir::ExprKind::Yield(..)
|
|
|
|
| hir::ExprKind::Box(..)
|
|
|
|
| hir::ExprKind::Type(..)
|
|
|
|
| hir::ExprKind::Err => {}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
2019-09-15 03:41:21 +02:00
|
|
|
|
|
|
|
intravisit::walk_expr(this, expr);
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
impl<'tcx> Liveness<'_, 'tcx> {
|
2019-11-30 15:08:22 +01:00
|
|
|
fn check_place(&mut self, expr: &'tcx Expr<'tcx>) {
|
2019-09-26 14:39:48 +01:00
|
|
|
match expr.kind {
|
2018-07-11 20:05:29 +08:00
|
|
|
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
|
2019-04-20 19:36:05 +03:00
|
|
|
if let Res::Local(var_hid) = path.res {
|
2020-05-21 00:00:00 +00:00
|
|
|
// Assignment to an immutable variable or argument: only legal
|
|
|
|
// if there is no later assignment. If this local is actually
|
|
|
|
// mutable, then check for a reassignment to flag the mutability
|
|
|
|
// as being used.
|
|
|
|
let ln = self.live_node(expr.hir_id, expr.span);
|
|
|
|
let var = self.variable(var_hid, expr.span);
|
|
|
|
self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
|
2014-11-29 16:41:21 -05:00
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
_ => {
|
2018-01-29 01:49:29 +02:00
|
|
|
// For other kinds of places, no checks are required,
|
2014-11-29 16:41:21 -05:00
|
|
|
// and any embedded expressions are actually rvalues
|
2015-11-17 17:51:44 -05:00
|
|
|
intravisit::walk_expr(self, expr);
|
2014-11-29 16:41:21 -05:00
|
|
|
}
|
|
|
|
}
|
2012-05-19 05:52:01 -07:00
|
|
|
}
|
|
|
|
|
2014-05-22 16:57:53 -07:00
|
|
|
fn should_warn(&self, var: Variable) -> Option<String> {
|
2013-01-10 10:59:58 -08:00
|
|
|
let name = self.ir.variable_name(var);
|
2020-09-26 00:00:00 +00:00
|
|
|
if name == kw::Invalid {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let name: &str = &name.as_str();
|
|
|
|
if name.as_bytes()[0] == b'_' {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(name.to_owned())
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
|
|
|
|
let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
|
|
|
|
None => return,
|
|
|
|
Some(upvars) => upvars,
|
|
|
|
};
|
|
|
|
for (&var_hir_id, upvar) in upvars.iter() {
|
|
|
|
let var = self.variable(var_hir_id, upvar.span);
|
|
|
|
let upvar_id = ty::UpvarId {
|
|
|
|
var_path: ty::UpvarPath { hir_id: var_hir_id },
|
2020-06-27 13:09:54 +02:00
|
|
|
closure_expr_id: self.ir.body_owner,
|
2020-05-22 00:00:00 +00:00
|
|
|
};
|
2020-07-17 08:47:04 +00:00
|
|
|
match self.typeck_results.upvar_capture(upvar_id) {
|
2020-08-25 23:58:58 -04:00
|
|
|
ty::UpvarCapture::ByValue(_) => {}
|
2020-05-22 00:00:00 +00:00
|
|
|
ty::UpvarCapture::ByRef(..) => continue,
|
|
|
|
};
|
|
|
|
if self.used_on_entry(entry_ln, var) {
|
|
|
|
if self.live_on_entry(entry_ln, var).is_none() {
|
|
|
|
if let Some(name) = self.should_warn(var) {
|
|
|
|
self.ir.tcx.struct_span_lint_hir(
|
|
|
|
lint::builtin::UNUSED_ASSIGNMENTS,
|
|
|
|
var_hir_id,
|
|
|
|
vec![upvar.span],
|
|
|
|
|lint| {
|
|
|
|
lint.build(&format!("value captured by `{}` is never read", name))
|
|
|
|
.help("did you mean to capture by reference instead?")
|
|
|
|
.emit();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if let Some(name) = self.should_warn(var) {
|
|
|
|
self.ir.tcx.struct_span_lint_hir(
|
|
|
|
lint::builtin::UNUSED_VARIABLES,
|
|
|
|
var_hir_id,
|
|
|
|
vec![upvar.span],
|
|
|
|
|lint| {
|
|
|
|
lint.build(&format!("unused variable: `{}`", name))
|
|
|
|
.help("did you mean to capture by reference instead?")
|
|
|
|
.emit();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-29 11:09:23 +01:00
|
|
|
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
|
|
|
|
for p in body.params {
|
2019-09-15 03:41:21 +02:00
|
|
|
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
|
|
|
|
if self.live_on_entry(ln, var).is_none() {
|
2020-05-22 00:00:00 +00:00
|
|
|
self.report_unsed_assign(hir_id, spans, var, |name| {
|
|
|
|
format!("value passed to `{}` is never read", name)
|
|
|
|
});
|
2014-01-27 14:18:36 +02:00
|
|
|
}
|
2019-09-15 03:41:21 +02:00
|
|
|
});
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
fn check_unused_vars_in_pat(
|
|
|
|
&self,
|
2019-11-30 15:08:22 +01:00
|
|
|
pat: &hir::Pat<'_>,
|
2019-09-15 03:41:21 +02:00
|
|
|
entry_ln: Option<LiveNode>,
|
|
|
|
on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
|
|
|
|
) {
|
|
|
|
// In an or-pattern, only consider the variable; any later patterns must have the same
|
|
|
|
// bindings, and we also consider the first pattern to be the "authoritative" set of ids.
|
2020-01-01 01:20:34 +02:00
|
|
|
// However, we should take the ids and spans of variables with the same name from the later
|
2019-09-15 03:41:21 +02:00
|
|
|
// patterns so the suggestions to prefix with underscores will apply to those too.
|
2020-09-26 00:00:00 +00:00
|
|
|
let mut vars: FxIndexMap<Symbol, (LiveNode, Variable, Vec<(HirId, Span)>)> = <_>::default();
|
2019-09-15 03:41:21 +02:00
|
|
|
|
|
|
|
pat.each_binding(|_, hir_id, pat_sp, ident| {
|
|
|
|
let ln = entry_ln.unwrap_or_else(|| self.live_node(hir_id, pat_sp));
|
|
|
|
let var = self.variable(hir_id, ident.span);
|
2020-01-01 01:20:34 +02:00
|
|
|
let id_and_sp = (hir_id, pat_sp);
|
2019-09-15 03:41:21 +02:00
|
|
|
vars.entry(self.ir.variable_name(var))
|
2020-01-01 01:20:34 +02:00
|
|
|
.and_modify(|(.., hir_ids_and_spans)| hir_ids_and_spans.push(id_and_sp))
|
|
|
|
.or_insert_with(|| (ln, var, vec![id_and_sp]));
|
2019-09-15 03:41:21 +02:00
|
|
|
});
|
|
|
|
|
2020-01-01 01:20:34 +02:00
|
|
|
for (_, (ln, var, hir_ids_and_spans)) in vars {
|
2019-09-15 03:41:21 +02:00
|
|
|
if self.used_on_entry(ln, var) {
|
2020-01-01 01:20:34 +02:00
|
|
|
let id = hir_ids_and_spans[0].0;
|
|
|
|
let spans = hir_ids_and_spans.into_iter().map(|(_, sp)| sp).collect();
|
2019-09-15 03:41:21 +02:00
|
|
|
on_used_on_entry(spans, id, ln, var);
|
|
|
|
} else {
|
2020-01-01 01:20:34 +02:00
|
|
|
self.report_unused(hir_ids_and_spans, ln, var);
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
2019-09-15 03:41:21 +02:00
|
|
|
}
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
|
2020-01-01 01:20:34 +02:00
|
|
|
fn report_unused(&self, hir_ids_and_spans: Vec<(HirId, Span)>, ln: LiveNode, var: Variable) {
|
|
|
|
let first_hir_id = hir_ids_and_spans[0].0;
|
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
if let Some(name) = self.should_warn(var).filter(|name| name != "self") {
|
|
|
|
// annoying: for parameters in funcs like `fn(x: i32)
|
|
|
|
// {ret}`, there is only one node, so asking about
|
|
|
|
// assigned_on_exit() is not meaningful.
|
2019-12-22 17:42:04 -05:00
|
|
|
let is_assigned =
|
|
|
|
if ln == self.s.exit_ln { false } else { self.assigned_on_exit(ln, var).is_some() };
|
2012-05-24 22:44:30 -07:00
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
if is_assigned {
|
2020-02-02 09:47:58 +10:00
|
|
|
self.ir.tcx.struct_span_lint_hir(
|
|
|
|
lint::builtin::UNUSED_VARIABLES,
|
2020-01-01 01:20:34 +02:00
|
|
|
first_hir_id,
|
|
|
|
hir_ids_and_spans.into_iter().map(|(_, sp)| sp).collect::<Vec<_>>(),
|
2020-02-02 09:47:58 +10:00
|
|
|
|lint| {
|
|
|
|
lint.build(&format!("variable `{}` is assigned to, but never used", name))
|
|
|
|
.note(&format!("consider using `_{}` instead", name))
|
|
|
|
.emit();
|
|
|
|
},
|
|
|
|
)
|
2019-09-15 03:41:21 +02:00
|
|
|
} else {
|
2020-01-31 22:24:57 +10:00
|
|
|
self.ir.tcx.struct_span_lint_hir(
|
2019-09-15 03:41:21 +02:00
|
|
|
lint::builtin::UNUSED_VARIABLES,
|
2020-01-01 01:20:34 +02:00
|
|
|
first_hir_id,
|
|
|
|
hir_ids_and_spans.iter().map(|(_, sp)| *sp).collect::<Vec<_>>(),
|
2020-01-31 22:24:57 +10:00
|
|
|
|lint| {
|
|
|
|
let mut err = lint.build(&format!("unused variable: `{}`", name));
|
2020-01-01 01:20:34 +02:00
|
|
|
|
|
|
|
let (shorthands, non_shorthands): (Vec<_>, Vec<_>) =
|
|
|
|
hir_ids_and_spans.into_iter().partition(|(hir_id, span)| {
|
|
|
|
let var = self.variable(*hir_id, *span);
|
|
|
|
self.ir.variable_is_shorthand(var)
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut shorthands = shorthands
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_, span)| (span, format!("{}: _", name)))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
// If we have both shorthand and non-shorthand, prefer the "try ignoring
|
2020-03-06 10:03:34 +02:00
|
|
|
// the field" message, and suggest `_` for the non-shorthands. If we only
|
|
|
|
// have non-shorthand, then prefix with an underscore instead.
|
2020-01-01 01:20:34 +02:00
|
|
|
if !shorthands.is_empty() {
|
2020-03-06 10:03:34 +02:00
|
|
|
shorthands.extend(
|
|
|
|
non_shorthands
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_, span)| (span, "_".to_string()))
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
);
|
2020-01-01 01:20:34 +02:00
|
|
|
|
|
|
|
err.multipart_suggestion(
|
|
|
|
"try ignoring the field",
|
|
|
|
shorthands,
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
2020-01-31 22:24:57 +10:00
|
|
|
} else {
|
|
|
|
err.multipart_suggestion(
|
2020-03-23 11:02:46 +03:00
|
|
|
"if this is intentional, prefix it with an underscore",
|
2020-03-06 10:03:34 +02:00
|
|
|
non_shorthands
|
|
|
|
.into_iter()
|
|
|
|
.map(|(_, span)| (span, format!("_{}", name)))
|
|
|
|
.collect::<Vec<_>>(),
|
2020-01-31 22:24:57 +10:00
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
2020-01-01 01:20:34 +02:00
|
|
|
|
2020-01-31 22:24:57 +10:00
|
|
|
err.emit()
|
|
|
|
},
|
2019-09-15 03:41:21 +02:00
|
|
|
);
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 03:41:21 +02:00
|
|
|
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
|
2012-08-23 09:14:19 -07:00
|
|
|
if self.live_on_exit(ln, var).is_none() {
|
2020-05-22 00:00:00 +00:00
|
|
|
self.report_unsed_assign(hir_id, spans, var, |name| {
|
|
|
|
format!("value assigned to `{}` is never read", name)
|
|
|
|
});
|
2015-11-03 19:04:36 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 00:00:00 +00:00
|
|
|
fn report_unsed_assign(
|
|
|
|
&self,
|
|
|
|
hir_id: HirId,
|
|
|
|
spans: Vec<Span>,
|
|
|
|
var: Variable,
|
|
|
|
message: impl Fn(&str) -> String,
|
|
|
|
) {
|
2015-11-03 19:04:36 +09:00
|
|
|
if let Some(name) = self.should_warn(var) {
|
2020-05-22 00:00:00 +00:00
|
|
|
self.ir.tcx.struct_span_lint_hir(
|
|
|
|
lint::builtin::UNUSED_ASSIGNMENTS,
|
|
|
|
hir_id,
|
|
|
|
spans,
|
|
|
|
|lint| {
|
|
|
|
lint.build(&message(&name))
|
|
|
|
.help("maybe it is overwritten before being read?")
|
|
|
|
.emit();
|
|
|
|
},
|
|
|
|
)
|
2012-05-23 20:53:49 -07:00
|
|
|
}
|
|
|
|
}
|
2015-11-03 19:04:36 +09:00
|
|
|
}
|