Auto merge of #56231 - eddyb:mir-debuginfo, r=oli-obk

rustc: move debug info from LocalDecl and UpvarDecl into a dedicated VarDebugInfo.

This PR introduces a MIR "user variable" debuginfo system, which amounts to mapping a variable name, in some `SourceScope`, to a `Place`, so that:

* each name can appear multiple times (e.g. due to macro hygiene), even in the same scope
* each `Place` can appear multiple times (e.g. in the future from optimizations like NRVO, which collapse multiple MIR locals into one)
* the `Place`s aren't limited to just locals, so they can describe the (right now quite ad-hoc) closure upvars and generator saved state fields, and can be properly transformed by optimizations (e.g. inlining - see `src/test/mir-opt/inline-closure-captures.rs`)

The main motivation for this was that #48300 and further optimizations were blocked on being able to describe complex debuginfo transformations (see https://github.com/rust-lang/rust/pull/48300#discussion_r170020762).

<hr/>

In the textual representation, the "user variable" debuginfo can be found in each scope, and consists of `debug NAME => PLACE;` "declarations", e.g. the MIR for `let x = ...; let y = ...; ...` is now:
```rust
    let _1: T;                           // in scope 0 at ...
    scope 1 {
        debug x => _1;                   // in scope 1 at ...
        let _2: T;                       // in scope 1 at ...
        scope 2 {
            debug y => _2;               // in scope 2 at ...
        }
    }
```
For reference, this is how the information was represented before this PR:
(notably, the scopes in which the variables are visible for debuginfo weren't even shown anywhere, making `scope 2` look pointless, and user variable names were part of MIR locals)
```rust
    let _1: T;                           // "x" in scope 0 at ...
    scope 1 {
        let _2: T;                       // "y" in scope 1 at ...
        scope 2 {
        }
    }
```

cc @nikomatsakis @michaelwoerister
This commit is contained in:
bors 2019-11-27 17:44:49 +00:00
commit e87a205c2e
49 changed files with 782 additions and 482 deletions

View file

@ -141,14 +141,8 @@ pub struct Body<'tcx> {
/// This is used for the "rust-call" ABI.
pub spread_arg: Option<Local>,
/// Names and capture modes of all the closure upvars, assuming
/// the first argument is either the closure or a reference to it.
//
// NOTE(eddyb) This is *strictly* a temporary hack for codegen
// debuginfo generation, and will be removed at some point.
// Do **NOT** use it for anything else; upvar information should not be
// in the MIR, so please rely on local crate HIR or other side-channels.
pub __upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
/// Debug information pertaining to user variables, including captures.
pub var_debug_info: Vec<VarDebugInfo<'tcx>>,
/// Mark this MIR of a const context other than const functions as having converted a `&&` or
/// `||` expression into `&` or `|` respectively. This is problematic because if we ever stop
@ -170,11 +164,10 @@ impl<'tcx> Body<'tcx> {
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
source_scopes: IndexVec<SourceScope, SourceScopeData>,
source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
yield_ty: Option<Ty<'tcx>>,
local_decls: LocalDecls<'tcx>,
user_type_annotations: CanonicalUserTypeAnnotations<'tcx>,
arg_count: usize,
__upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
var_debug_info: Vec<VarDebugInfo<'tcx>>,
span: Span,
control_flow_destroyed: Vec<(Span, String)>,
) -> Self {
@ -191,14 +184,14 @@ impl<'tcx> Body<'tcx> {
basic_blocks,
source_scopes,
source_scope_local_data,
yield_ty,
yield_ty: None,
generator_drop: None,
generator_layout: None,
local_decls,
user_type_annotations,
arg_count,
__upvar_debuginfo_codegen_only_do_not_use,
spread_arg: None,
var_debug_info,
span,
cache: cache::Cache::new(),
control_flow_destroyed,
@ -280,7 +273,7 @@ impl<'tcx> Body<'tcx> {
LocalKind::ReturnPointer
} else if index < self.arg_count + 1 {
LocalKind::Arg
} else if self.local_decls[local].name.is_some() {
} else if self.local_decls[local].is_user_variable() {
LocalKind::Var
} else {
LocalKind::Temp
@ -728,12 +721,6 @@ pub struct LocalDecl<'tcx> {
// FIXME(matthewjasper) Don't store in this in `Body`
pub user_ty: UserTypeProjections,
/// The name of the local, used in debuginfo and pretty-printing.
///
/// Note that function arguments can also have this set to `Some(_)`
/// to generate better debuginfo.
pub name: Option<Name>,
/// The *syntactic* (i.e., not visibility) source scope the local is defined
/// in. If the local was defined in a let-statement, this
/// is *within* the let-statement, rather than outside
@ -785,9 +772,9 @@ pub struct LocalDecl<'tcx> {
/// `drop(x)`, we want it to refer to `x: u32`.
///
/// To allow both uses to work, we need to have more than a single scope
/// for a local. We have the `source_info.scope` represent the
/// "syntactic" lint scope (with a variable being under its let
/// block) while the `visibility_scope` represents the "local variable"
/// for a local. We have the `source_info.scope` represent the "syntactic"
/// lint scope (with a variable being under its let block) while the
/// `var_debug_info.source_info.scope` represents the "local variable"
/// scope (where the "rest" of a block is under all prior let-statements).
///
/// The end result looks like this:
@ -806,18 +793,14 @@ pub struct LocalDecl<'tcx> {
/// │ │
/// │ │ │{ let y: u32 }
/// │ │ │
/// │ │ │← y.visibility_scope
/// │ │ │← y.var_debug_info.source_info.scope
/// │ │ │← `y + 2`
/// │
/// │ │{ let x: u32 }
/// │ │← x.visibility_scope
/// │ │← x.var_debug_info.source_info.scope
/// │ │← `drop(x)` // This accesses `x: u32`.
/// ```
pub source_info: SourceInfo,
/// Source scope within which the local is visible (for debuginfo)
/// (see `source_info` for more details).
pub visibility_scope: SourceScope,
}
/// Extra information about a local that's used for diagnostics.
@ -955,9 +938,7 @@ impl<'tcx> LocalDecl<'tcx> {
mutability,
ty,
user_ty: UserTypeProjections::none(),
name: None,
source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE },
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal,
local_info: LocalInfo::Other,
is_block_tail: None,
@ -974,22 +955,27 @@ impl<'tcx> LocalDecl<'tcx> {
ty: return_ty,
user_ty: UserTypeProjections::none(),
source_info: SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE },
visibility_scope: OUTERMOST_SOURCE_SCOPE,
internal: false,
is_block_tail: None,
name: None, // FIXME maybe we do want some name here?
local_info: LocalInfo::Other,
}
}
}
/// A closure capture, with its name and mode.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
pub struct UpvarDebuginfo {
pub debug_name: Name,
/// Debug information pertaining to a user variable.
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable, TypeFoldable)]
pub struct VarDebugInfo<'tcx> {
pub name: Name,
/// If true, the capture is behind a reference.
pub by_ref: bool,
/// Source info of the user variable, including the scope
/// within which the variable is visible (to debuginfo)
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: SourceInfo,
/// Where the data for this user variable is to be found.
/// NOTE(eddyb) There's an unenforced invariant that this `Place` is
/// based on a `Local`, not a `Static`, and contains no indexing.
pub place: Place<'tcx>,
}
///////////////////////////////////////////////////////////////////////////
@ -2758,16 +2744,6 @@ pub struct GeneratorLayout<'tcx> {
/// have conflicts with each other are allowed to overlap in the computed
/// layout.
pub storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
/// The names and scopes of all the stored generator locals.
///
/// N.B., this is *strictly* a temporary hack for codegen
/// debuginfo generation, and will be removed at some point.
/// Do **NOT** use it for anything else, local information should not be
/// in the MIR, please rely on local crate HIR or other side-channels.
//
// FIXME(tmandry): see above.
pub __local_debuginfo_codegen_only_do_not_use: IndexVec<GeneratorSavedLocal, LocalDecl<'tcx>>,
}
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@ -2946,7 +2922,6 @@ CloneTypeFoldableAndLiftImpls! {
MirPhase,
Mutability,
SourceInfo,
UpvarDebuginfo,
FakeReadCause,
RetagKind,
SourceScope,

View file

@ -221,6 +221,11 @@ macro_rules! make_mir_visitor {
self.super_local_decl(local, local_decl);
}
fn visit_var_debug_info(&mut self,
var_debug_info: & $($mutability)* VarDebugInfo<'tcx>) {
self.super_var_debug_info(var_debug_info);
}
fn visit_local(&mut self,
_local: & $($mutability)? Local,
_context: PlaceContext,
@ -279,6 +284,10 @@ macro_rules! make_mir_visitor {
);
}
for var_debug_info in &$($mutability)? body.var_debug_info {
self.visit_var_debug_info(var_debug_info);
}
self.visit_span(&$($mutability)? body.span);
}
@ -687,9 +696,7 @@ macro_rules! make_mir_visitor {
mutability: _,
ty,
user_ty,
name: _,
source_info,
visibility_scope,
internal: _,
local_info: _,
is_block_tail: _,
@ -703,7 +710,23 @@ macro_rules! make_mir_visitor {
self.visit_user_type_projection(user_ty);
}
self.visit_source_info(source_info);
self.visit_source_scope(visibility_scope);
}
fn super_var_debug_info(&mut self,
var_debug_info: & $($mutability)? VarDebugInfo<'tcx>) {
let VarDebugInfo {
name: _,
source_info,
place,
} = var_debug_info;
self.visit_source_info(source_info);
let location = START_BLOCK.start_location();
self.visit_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location,
);
}
fn super_source_scope(&mut self,
@ -1029,6 +1052,8 @@ pub enum NonUseContext {
StorageDead,
/// User type annotation assertions for NLL.
AscribeUserTy,
/// The data of an user variable, for debug info.
VarDebugInfo,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

View file

@ -23,12 +23,10 @@ pub fn compute_mir_scopes(
) {
// Find all the scopes with variables defined in them.
let mut has_variables = BitSet::new_empty(mir.source_scopes.len());
// FIXME(eddyb) base this on `decl.name`, or even better, on debuginfo.
// FIXME(eddyb) take into account that arguments always have debuginfo,
// irrespective of their name (assuming full debuginfo is enabled).
for var in mir.vars_iter() {
let decl = &mir.local_decls[var];
has_variables.insert(decl.visibility_scope);
for var_debug_info in &mir.var_debug_info {
has_variables.insert(var_debug_info.source_info.scope);
}
// Instantiate all scopes.

View file

@ -17,13 +17,13 @@ use crate::llvm_util;
use crate::value::Value;
use rustc_codegen_ssa::traits::*;
use rustc_index::vec::{Idx, IndexVec};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc::hir::CodegenFnAttrFlags;
use rustc::hir::def::CtorKind;
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
use rustc::ich::NodeIdHashingMode;
use rustc::mir::Field;
use rustc::mir::GeneratorLayout;
use rustc::mir::{self, Field, GeneratorLayout};
use rustc::mir::interpret::truncate;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc::ty::Instance;
@ -1316,6 +1316,45 @@ fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool {
|| llvm_util::get_major_version() < 8;
}
// FIXME(eddyb) maybe precompute this? Right now it's computed once
// per generator monomorphization, but it doesn't depend on substs.
fn generator_layout_and_saved_local_names(
tcx: TyCtxt<'tcx>,
def_id: DefId,
) -> (&'tcx GeneratorLayout<'tcx>, IndexVec<mir::GeneratorSavedLocal, Option<ast::Name>>) {
let body = tcx.optimized_mir(def_id);
let generator_layout = body.generator_layout.as_ref().unwrap();
let mut generator_saved_local_names =
IndexVec::from_elem(None, &generator_layout.field_tys);
let state_arg = mir::PlaceBase::Local(mir::Local::new(1));
for var in &body.var_debug_info {
if var.place.base != state_arg {
continue;
}
match var.place.projection[..] {
[
// Deref of the `Pin<&mut Self>` state argument.
mir::ProjectionElem::Field(..),
mir::ProjectionElem::Deref,
// Field of a variant of the state.
mir::ProjectionElem::Downcast(_, variant),
mir::ProjectionElem::Field(field, _),
] => {
let name = &mut generator_saved_local_names[
generator_layout.variant_fields[variant][field]
];
if name.is_none() {
name.replace(var.name);
}
}
_ => {}
}
}
(generator_layout, generator_saved_local_names)
}
/// Describes the members of an enum value; an enum is described as a union of
/// structs in DWARF. This `MemberDescriptionFactory` provides the description for
/// the members of this union; so for every variant of the given enum, this
@ -1332,12 +1371,25 @@ struct EnumMemberDescriptionFactory<'ll, 'tcx> {
impl EnumMemberDescriptionFactory<'ll, 'tcx> {
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>)
-> Vec<MemberDescription<'ll>> {
let generator_variant_info_data = match self.enum_type.kind {
ty::Generator(def_id, ..) => {
Some(generator_layout_and_saved_local_names(cx.tcx, def_id))
}
_ => None,
};
let variant_info_for = |index: VariantIdx| {
match &self.enum_type.kind {
match self.enum_type.kind {
ty::Adt(adt, _) => VariantInfo::Adt(&adt.variants[index]),
ty::Generator(def_id, substs, _) => {
let generator_layout = cx.tcx.generator_layout(*def_id);
VariantInfo::Generator(substs, generator_layout, index)
ty::Generator(_, substs, _) => {
let (generator_layout, generator_saved_local_names) =
generator_variant_info_data.as_ref().unwrap();
VariantInfo::Generator {
substs,
generator_layout: *generator_layout,
generator_saved_local_names,
variant_index: index,
}
}
_ => bug!(),
}
@ -1608,16 +1660,21 @@ enum EnumDiscriminantInfo<'ll> {
}
#[derive(Copy, Clone)]
enum VariantInfo<'tcx> {
enum VariantInfo<'a, 'tcx> {
Adt(&'tcx ty::VariantDef),
Generator(SubstsRef<'tcx>, &'tcx GeneratorLayout<'tcx>, VariantIdx),
Generator {
substs: SubstsRef<'tcx>,
generator_layout: &'tcx GeneratorLayout<'tcx>,
generator_saved_local_names: &'a IndexVec<mir::GeneratorSavedLocal, Option<ast::Name>>,
variant_index: VariantIdx,
},
}
impl<'tcx> VariantInfo<'tcx> {
impl<'tcx> VariantInfo<'_, 'tcx> {
fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R {
match self {
VariantInfo::Adt(variant) => f(&variant.ident.as_str()),
VariantInfo::Generator(substs, _, variant_index) =>
VariantInfo::Generator { substs, variant_index, .. } =>
f(&substs.as_generator().variant_name(*variant_index)),
}
}
@ -1625,7 +1682,7 @@ impl<'tcx> VariantInfo<'tcx> {
fn variant_name(&self) -> String {
match self {
VariantInfo::Adt(variant) => variant.ident.to_string(),
VariantInfo::Generator(_, _, variant_index) => {
VariantInfo::Generator { variant_index, .. } => {
// Since GDB currently prints out the raw discriminant along
// with every variant, make each variant name be just the value
// of the discriminant. The struct name for the variant includes
@ -1636,17 +1693,20 @@ impl<'tcx> VariantInfo<'tcx> {
}
fn field_name(&self, i: usize) -> String {
let field_name = match self {
let field_name = match *self {
VariantInfo::Adt(variant) if variant.ctor_kind != CtorKind::Fn =>
Some(variant.fields[i].ident.to_string()),
VariantInfo::Generator(_, generator_layout, variant_index) => {
let field = generator_layout.variant_fields[*variant_index][i.into()];
let decl = &generator_layout.__local_debuginfo_codegen_only_do_not_use[field];
decl.name.map(|name| name.to_string())
}
Some(variant.fields[i].ident.name),
VariantInfo::Generator {
generator_layout,
generator_saved_local_names,
variant_index,
..
} => generator_saved_local_names[
generator_layout.variant_fields[variant_index][i.into()]
],
_ => None,
};
field_name.unwrap_or_else(|| format!("__{}", i))
field_name.map(|name| name.to_string()).unwrap_or_else(|| format!("__{}", i))
}
}
@ -1657,7 +1717,7 @@ impl<'tcx> VariantInfo<'tcx> {
fn describe_enum_variant(
cx: &CodegenCx<'ll, 'tcx>,
layout: layout::TyLayout<'tcx>,
variant: VariantInfo<'tcx>,
variant: VariantInfo<'_, 'tcx>,
discriminant_info: EnumDiscriminantInfo<'ll>,
containing_scope: &'ll DIScope,
span: Span,

View file

@ -5,7 +5,9 @@ use rustc_index::bit_set::BitSet;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::vec::{Idx, IndexVec};
use rustc::mir::{self, Location, TerminatorKind};
use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext};
use rustc::mir::visit::{
Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext, NonUseContext,
};
use rustc::mir::traversal;
use rustc::session::config::DebugInfo;
use rustc::ty;
@ -27,7 +29,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
if fx.cx.sess().opts.debuginfo == DebugInfo::Full {
if mir.local_kind(local) == mir::LocalKind::Arg || decl.name.is_some() {
if mir.local_kind(local) == mir::LocalKind::Arg {
analyzer.not_ssa(local);
continue;
}
@ -114,6 +116,12 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
let cx = self.fx.cx;
if let [proj_base @ .., elem] = place_ref.projection {
let mut base_context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
// Allow uses of projections that are ZSTs or from scalar fields.
let is_consume = match context {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
@ -145,47 +153,81 @@ impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
// Recurse with the same context, instead of `Projection`,
// potentially stopping at non-operand projections,
// which would trigger `not_ssa` on locals.
self.process_place(
&mir::PlaceRef {
base: place_ref.base,
projection: proj_base,
},
context,
location,
);
return;
base_context = context;
}
}
}
// A deref projection only reads the pointer, never needs the place.
if let mir::ProjectionElem::Deref = elem {
self.process_place(
&mir::PlaceRef {
base: place_ref.base,
projection: proj_base,
},
// Deref projections typically only read the pointer.
// (the exception being `VarDebugInfo` contexts, handled below)
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
// Indirect debuginfo requires going through memory, that only
// the debugger accesses, following our emitted DWARF pointer ops.
//
// FIXME(eddyb) Investigate the possibility of relaxing this, but
// note that `llvm.dbg.declare` *must* be used for indirect places,
// even if we start using `llvm.dbg.value` for all other cases,
// as we don't necessarily know when the value changes, but only
// where it lives in memory.
//
// It's possible `llvm.dbg.declare` could support starting from
// a pointer that doesn't point to an `alloca`, but this would
// only be useful if we know the pointer being `Deref`'d comes
// from an immutable place, and if `llvm.dbg.declare` calls
// must be at the very start of the function, then only function
// arguments could contain such pointers.
if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
// We use `NonUseContext::VarDebugInfo` for the base,
// which might not force the base local to memory,
// so we have to do it manually.
if let mir::PlaceBase::Local(local) = place_ref.base {
self.visit_local(&local, context, location);
}
}
}
// `NonUseContext::VarDebugInfo` needs to flow all the
// way down to the base local (see `visit_local`).
if context == PlaceContext::NonUse(NonUseContext::VarDebugInfo) {
base_context = context;
}
self.process_place(
&mir::PlaceRef {
base: place_ref.base,
projection: proj_base,
},
base_context,
location
);
// HACK(eddyb) this emulates the old `visit_projection_elem`, this
// entire `visit_place`-like `process_place` method should be rewritten,
// now that we have moved to the "slice of projections" representation.
if let mir::ProjectionElem::Index(local) = elem {
self.visit_local(
local,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
location
);
return;
}
} else {
// FIXME this is super_place code, is repeated here to avoid cloning place or changing
// visit_place API
let mut context = context;
if !place_ref.projection.is_empty() {
context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
}
self.visit_place_base(place_ref.base, context, location);
self.visit_projection(place_ref.base, place_ref.projection, context, location);
}
// FIXME this is super_place code, is repeated here to avoid cloning place or changing
// visit_place API
let mut context = context;
if !place_ref.projection.is_empty() {
context = if context.is_mutating_use() {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
} else {
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
};
}
self.visit_place_base(place_ref.base, context, location);
self.visit_projection(place_ref.base, place_ref.projection, context, location);
}
}
@ -264,6 +306,15 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
self.assign(local, location);
}
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
// We need to keep locals in `alloca`s for debuginfo.
// FIXME(eddyb): We should figure out how to use `llvm.dbg.value` instead
// of putting everything in allocas just so we can use `llvm.dbg.declare`.
if self.fx.cx.sess().opts.debuginfo == DebugInfo::Full {
self.not_ssa(local);
}
}
PlaceContext::NonUse(_) |
PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}

View file

@ -1,12 +1,12 @@
use rustc_index::vec::{Idx, IndexVec};
use rustc_index::vec::IndexVec;
use rustc::hir::def_id::CrateNum;
use rustc::mir;
use rustc::session::config::DebugInfo;
use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::{LayoutOf, Size, VariantIdx};
use rustc::ty::TyCtxt;
use rustc::ty::layout::{LayoutOf, Size};
use crate::traits::*;
use syntax_pos::{BytePos, Span, Symbol};
use syntax_pos::{BytePos, Span};
use syntax::symbol::kw;
use super::{FunctionCx, LocalRef};
@ -113,7 +113,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(per_local) => &per_local[local],
None => return,
};
let whole_local_var = vars.iter().find(|var| {
let whole_local_var = vars.iter().copied().find(|var| {
var.place.projection.is_empty()
});
let has_proj = || vars.iter().any(|var| {
@ -131,7 +131,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// be offset to account for the hidden environment?
None
} else {
Some(VarDebugInfo {
Some(mir::VarDebugInfo {
name: kw::Invalid,
source_info: self.mir.local_decls[local].source_info,
place: local.into(),
@ -185,7 +185,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => return,
};
let vars = vars.iter().chain(if whole_local_var.is_none() {
let vars = vars.iter().copied().chain(if whole_local_var.is_none() {
fallback_var.as_ref()
} else {
None
@ -253,133 +253,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
/// Partition all `VarDebuginfo` in `body`, by their base `Local`.
pub fn per_local_var_debug_info(
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
) -> Option<IndexVec<mir::Local, Vec<VarDebugInfo<'tcx>>>> {
body: &'a mir::Body<'tcx>,
) -> Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>> {
if tcx.sess.opts.debuginfo == DebugInfo::Full || !tcx.sess.fewer_names() {
let mut per_local = IndexVec::from_elem(vec![], &body.local_decls);
for (local, decl) in body.local_decls.iter_enumerated() {
if let Some(name) = decl.name {
per_local[local].push(VarDebugInfo {
name,
source_info: mir::SourceInfo {
span: decl.source_info.span,
scope: decl.visibility_scope,
},
place: local.into(),
});
for var in &body.var_debug_info {
if let mir::PlaceBase::Local(local) = var.place.base {
per_local[local].push(var);
}
}
let upvar_debuginfo = &body.__upvar_debuginfo_codegen_only_do_not_use;
if !upvar_debuginfo.is_empty() {
let env_arg = mir::Local::new(1);
let mut env_projs = vec![];
let pin_did = tcx.lang_items().pin_type();
match body.local_decls[env_arg].ty.kind {
ty::RawPtr(_) |
ty::Ref(..) => {
env_projs.push(mir::ProjectionElem::Deref);
}
ty::Adt(def, substs) if Some(def.did) == pin_did => {
if let ty::Ref(..) = substs.type_at(0).kind {
env_projs.push(mir::ProjectionElem::Field(
mir::Field::new(0),
// HACK(eddyb) field types aren't used or needed here.
tcx.types.err,
));
env_projs.push(mir::ProjectionElem::Deref);
}
}
_ => {}
}
let extra_locals = {
let upvars = upvar_debuginfo
.iter()
.enumerate()
.map(|(i, upvar)| {
let source_info = mir::SourceInfo {
span: body.span,
scope: mir::OUTERMOST_SOURCE_SCOPE,
};
(None, i, upvar.debug_name, upvar.by_ref, source_info)
});
let generator_fields = body.generator_layout.as_ref().map(|generator_layout| {
generator_layout.variant_fields.iter()
.enumerate()
.flat_map(move |(variant_idx, fields)| {
let variant_idx = Some(VariantIdx::from(variant_idx));
fields.iter()
.enumerate()
.filter_map(move |(i, field)| {
let decl = &generator_layout.
__local_debuginfo_codegen_only_do_not_use[*field];
if let Some(name) = decl.name {
let source_info = mir::SourceInfo {
span: decl.source_info.span,
scope: decl.visibility_scope,
};
Some((variant_idx, i, name, false, source_info))
} else {
None
}
})
})
}).into_iter().flatten();
upvars.chain(generator_fields)
};
for (variant_idx, field, name, by_ref, source_info) in extra_locals {
let mut projs = env_projs.clone();
if let Some(variant_idx) = variant_idx {
projs.push(mir::ProjectionElem::Downcast(None, variant_idx));
}
projs.push(mir::ProjectionElem::Field(
mir::Field::new(field),
// HACK(eddyb) field types aren't used or needed here.
tcx.types.err,
));
if by_ref {
projs.push(mir::ProjectionElem::Deref);
}
per_local[env_arg].push(VarDebugInfo {
name,
source_info,
place: mir::Place {
base: mir::PlaceBase::Local(env_arg),
projection: tcx.intern_place_elems(&projs),
},
});
}
}
Some(per_local)
} else {
None
}
}
/// Debug information relatating to an user variable.
// FIXME(eddyb) move this to the MIR bodies themselves.
#[derive(Clone)]
pub struct VarDebugInfo<'tcx> {
pub name: Symbol,
/// Source info of the user variable, including the scope
/// within which the variable is visible (to debuginfo)
/// (see `LocalDecl`'s `source_info` field for more details).
pub source_info: mir::SourceInfo,
/// Where the data for this user variable is to be found.
pub place: mir::Place<'tcx>,
}

View file

@ -74,7 +74,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// notably `expect`.
locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>,
per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<debuginfo::VarDebugInfo<'tcx>>>>,
/// All `VarDebuginfo` from the MIR body, partitioned by `Local`.
/// This is `None` if no variable debuginfo/names are needed.
per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>>,
}
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {

View file

@ -308,7 +308,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
location,
borrow,
None,
).add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", Some(borrow_span));
).add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
Some(borrow_span),
);
err.buffer(&mut self.errors_buffer);
}
@ -343,7 +350,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
});
self.explain_why_borrow_contains_point(location, borrow, None)
.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
None,
);
err
}
@ -561,6 +575,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
first_borrow_desc,
None,
@ -947,6 +962,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
None,
@ -971,7 +987,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
explanation.add_explanation_to_diagnostic(
self.infcx.tcx, self.body, &mut err, "", None);
self.infcx.tcx, self.body, &self.local_names, &mut err, "", None);
}
err
@ -1029,7 +1045,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
_ => {}
}
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
None,
);
err.buffer(&mut self.errors_buffer);
}
@ -1109,7 +1132,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
_ => {}
}
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
explanation.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
None,
);
let within = if borrow_spans.for_generator() {
" by generator"
@ -1478,7 +1508,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
self.explain_why_borrow_contains_point(location, loan, None)
.add_explanation_to_diagnostic(self.infcx.tcx, self.body, &mut err, "", None);
.add_explanation_to_diagnostic(
self.infcx.tcx,
self.body,
&self.local_names,
&mut err,
"",
None,
);
err.buffer(&mut self.errors_buffer);
}
@ -1496,14 +1533,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
assigned_span: Span,
err_place: &Place<'tcx>,
) {
let (from_arg, local_decl) = if let Some(local) = err_place.as_local() {
if let LocalKind::Arg = self.body.local_kind(local) {
(true, Some(&self.body.local_decls[local]))
} else {
(false, Some(&self.body.local_decls[local]))
}
} else {
(false, None)
let (from_arg, local_decl, local_name) = match err_place.as_local() {
Some(local) => (
self.body.local_kind(local) == LocalKind::Arg,
Some(&self.body.local_decls[local]),
self.local_names[local],
),
None => (false, None, None),
};
// If root local is initialized immediately (everything apart from let
@ -1553,7 +1589,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
if let Some(decl) = local_decl {
if let Some(name) = decl.name {
if let Some(name) = local_name {
if decl.can_be_made_mutable() {
err.span_suggestion(
decl.source_info.span,

View file

@ -331,10 +331,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
/// a name, or its name was generated by the compiler, then `Err` is returned
fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
let local = &self.body.local_decls[local_index];
match local.name {
Some(name) if !local.from_compiler_desugaring() => {
fn append_local_to_string(&self, local: Local, buf: &mut String) -> Result<(), ()> {
let decl = &self.body.local_decls[local];
match self.local_names[local] {
Some(name) if !decl.from_compiler_desugaring() => {
buf.push_str(&name.as_str());
Ok(())
}

View file

@ -115,6 +115,20 @@ fn do_mir_borrowck<'a, 'tcx>(
.as_local_hir_id(def_id)
.expect("do_mir_borrowck: non-local DefId");
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
for var_debug_info in &input_body.var_debug_info {
if let Some(local) = var_debug_info.place.as_local() {
if let Some(prev_name) = local_names[local] {
if var_debug_info.name != prev_name {
span_bug!(var_debug_info.source_info.span,
"local {:?} has many names (`{}` vs `{}`)",
local, prev_name, var_debug_info.name);
}
}
local_names[local] = Some(var_debug_info.name);
}
}
// Gather the upvars of a closure, if any.
let tables = tcx.typeck_tables_of(def_id);
let upvars: Vec<_> = tables
@ -189,6 +203,7 @@ fn do_mir_borrowck<'a, 'tcx>(
free_regions,
body,
&promoted,
&local_names,
&upvars,
location_table,
param_env,
@ -264,6 +279,7 @@ fn do_mir_borrowck<'a, 'tcx>(
borrow_set,
dominators,
upvars,
local_names,
};
let mut state = Flows::new(
@ -325,13 +341,8 @@ fn do_mir_borrowck<'a, 'tcx>(
if let ClearCrossCrate::Set(ref vsi) = mbcx.body.source_scope_local_data {
let local_decl = &mbcx.body.local_decls[local];
// Skip implicit `self` argument for closures
if local.index() == 1 && tcx.is_closure(mbcx.mir_def_id) {
continue;
}
// Skip over locals that begin with an underscore or have no name
match local_decl.name {
match mbcx.local_names[local] {
Some(name) => if name.as_str().starts_with("_") {
continue;
},
@ -463,6 +474,9 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
/// Information about upvars not necessarily preserved in types or MIR
upvars: Vec<Upvar>,
/// Names of local (user) variables (extracted from `var_debug_info`).
local_names: IndexVec<Local, Option<Name>>,
}
// Check that:

View file

@ -322,7 +322,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if decl.is_ref_for_guard() {
let mut err = self.cannot_move_out_of(
span,
&format!("`{}` in pattern guard", decl.name.unwrap()),
&format!("`{}` in pattern guard", self.local_names[*local].unwrap()),
);
err.note(
"variables bound in patterns cannot be moved from \
@ -571,7 +571,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if binds_to.len() == 1 {
self.note_type_does_not_implement_copy(
err,
&format!("`{}`", bind_to.name.unwrap()),
&format!("`{}`", self.local_names[*local].unwrap()),
bind_to.ty,
Some(binding_span)
);

View file

@ -50,8 +50,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
if access_place.as_local().is_some() {
reason = ", as it is not declared as mutable".to_string();
} else {
let name = self.body.local_decls[*local]
.name
let name = self.local_names[*local]
.expect("immutable unnamed local");
reason = format!(", as `{}` is not declared as mutable", name);
}
@ -253,7 +252,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
// Deliberately fall into this case for all implicit self types,
// so that we don't fall in to the next case with them.
kind == mir::ImplicitSelfKind::MutRef
} else if Some(kw::SelfLower) == local_decl.name {
} else if Some(kw::SelfLower) == self.local_names[*local] {
// Otherwise, check if the name is the self kewyord - in which case
// we have an explicit self. Do the same thing in this case and check
// for a `self: &mut Self` to suggest removing the `&mut`.
@ -290,7 +289,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
err.span_suggestion(
local_decl.source_info.span,
"consider changing this to be mutable",
format!("mut {}", local_decl.name.unwrap()),
format!("mut {}", self.local_names[*local].unwrap()),
Applicability::MachineApplicable,
);
}
@ -415,7 +414,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
);
}
match local_decl.name {
match self.local_names[*local] {
Some(name) if !local_decl.from_compiler_desugaring() => {
err.span_label(
span,

View file

@ -11,9 +11,11 @@ use rustc::mir::{
};
use rustc::ty::{self, TyCtxt};
use rustc::ty::adjustment::{PointerCast};
use rustc_index::vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagnosticBuilder;
use syntax_pos::Span;
use syntax_pos::symbol::Symbol;
mod find_use;
@ -56,6 +58,7 @@ impl BorrowExplanation {
&self,
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
err: &mut DiagnosticBuilder<'_>,
borrow_desc: &str,
borrow_span: Option<Span>,
@ -112,7 +115,7 @@ impl BorrowExplanation {
_ => ("destructor", format!("type `{}`", local_decl.ty)),
};
match local_decl.name {
match local_names[dropped_local] {
Some(local_name) if !local_decl.from_compiler_desugaring() => {
let message = format!(
"{B}borrow might be used here, when `{LOC}` is dropped \
@ -271,10 +274,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some(Cause::DropVar(local, location)) => {
let mut should_note_order = false;
if body.local_decls[local].name.is_some() {
if self.local_names[local].is_some() {
if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
if let Some(borrowed_local) = place.as_local() {
if body.local_decls[borrowed_local].name.is_some()
if self.local_names[borrowed_local].is_some()
&& local != borrowed_local
{
should_note_order = true;
@ -295,6 +298,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let (category, from_closure, span, region_name) =
self.nonlexical_regioncx.free_region_constraint_info(
self.body,
&self.local_names,
&self.upvars,
self.mir_def_id,
self.infcx,
@ -495,7 +499,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Operand::Move(place) => {
if let Some(l) = place.as_local() {
let local_decl = &self.body.local_decls[l];
if local_decl.name.is_none() {
if self.local_names[l].is_none() {
local_decl.source_info.span
} else {
span

View file

@ -16,6 +16,7 @@ use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements,
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_index::vec::IndexVec;
use rustc_errors::Diagnostic;
use syntax_pos::symbol::Symbol;
use std::fmt::Debug;
use std::env;
use std::io;
@ -158,6 +159,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
universal_regions: UniversalRegions<'tcx>,
body: &Body<'tcx>,
promoted: &IndexVec<Promoted, Body<'tcx>>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
location_table: &LocationTable,
param_env: ty::ParamEnv<'tcx>,
@ -281,7 +283,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
// Solve the region constraints.
let closure_region_requirements =
regioncx.solve(infcx, &body, upvars, def_id, errors_buffer);
regioncx.solve(infcx, body, local_names, upvars, def_id, errors_buffer);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.

View file

@ -9,7 +9,7 @@ use rustc::hir::def_id::DefId;
use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
use rustc::infer::InferCtxt;
use rustc::infer::NLLRegionVariableOrigin;
use rustc::mir::{ConstraintCategory, Location, Body};
use rustc::mir::{ConstraintCategory, Local, Location, Body};
use rustc::ty::{self, RegionVid};
use rustc_index::vec::IndexVec;
use rustc_errors::DiagnosticBuilder;
@ -17,6 +17,7 @@ use std::collections::VecDeque;
use syntax::errors::Applicability;
use syntax::symbol::kw;
use syntax_pos::Span;
use syntax_pos::symbol::Symbol;
use self::outlives_suggestion::OutlivesSuggestionBuilder;
@ -71,6 +72,9 @@ pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
/// The MIR body we are reporting errors on (for convenience).
body: &'b Body<'tcx>,
/// User variable names for MIR locals (where applicable).
local_names: &'b IndexVec<Local, Option<Symbol>>,
/// Any upvars for the MIR body we have kept track of during borrow checking.
upvars: &'b [Upvar],
}
@ -367,13 +371,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(super) fn report_error<'a>(
&'a self,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
infcx: &'a InferCtxt<'a, 'tcx>,
mir_def_id: DefId,
fr: RegionVid,
fr_origin: NLLRegionVariableOrigin,
outlived_fr: RegionVid,
outlives_suggestion: &mut OutlivesSuggestionBuilder,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'a> {
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
@ -407,6 +412,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
infcx,
mir_def_id,
body,
local_names,
upvars,
};
@ -551,7 +557,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'_> {
let ErrorReportingCtx {
infcx, body, upvars, ..
infcx, body, upvars, local_names, ..
} = errctx;
let ErrorConstraintInfo {
@ -559,9 +565,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
} = errci;
let fr_name_and_span =
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.fr);
let outlived_fr_name_and_span =
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.outlived_fr);
self.get_var_name_and_span_for_region(infcx.tcx, body, local_names, upvars, errci.fr);
let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
infcx.tcx,
body,
local_names,
upvars,
errci.outlived_fr,
);
let escapes_from = match self.universal_regions.defining_ty {
DefiningTy::Closure(..) => "closure",
@ -789,6 +800,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
crate fn free_region_constraint_info(
&self,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
infcx: &InferCtxt<'_, 'tcx>,
@ -804,7 +816,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let mut renctx = RegionErrorNamingCtx::new();
let errctx = ErrorReportingCtx {
infcx, body, upvars, mir_def_id,
infcx, body, local_names, upvars, mir_def_id,
region_infcx: self,
};
let outlived_fr_name = self.give_region_a_name(&errctx, &mut renctx, outlived_region);

View file

@ -4,9 +4,12 @@
use std::collections::BTreeMap;
use log::debug;
use rustc::{hir::def_id::DefId, infer::InferCtxt, mir::Body, ty::RegionVid};
use rustc::{hir::def_id::DefId, infer::InferCtxt, ty::RegionVid};
use rustc::mir::{Body, Local};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use rustc_index::vec::IndexVec;
use syntax_pos::symbol::Symbol;
use smallvec::SmallVec;
@ -34,10 +37,12 @@ enum SuggestedConstraint {
/// corresponding to a function definition.
///
/// Adds a help note suggesting adding a where clause with the needed constraints.
pub struct OutlivesSuggestionBuilder {
pub struct OutlivesSuggestionBuilder<'a> {
/// The MIR DefId of the fn with the lifetime error.
mir_def_id: DefId,
local_names: &'a IndexVec<Local, Option<Symbol>>,
/// The list of outlives constraints that need to be added. Specifically, we map each free
/// region to all other regions that it must outlive. I will use the shorthand `fr:
/// outlived_frs`. Not all of these regions will already have names necessarily. Some could be
@ -46,10 +51,17 @@ pub struct OutlivesSuggestionBuilder {
constraints_to_add: BTreeMap<RegionVid, Vec<RegionVid>>,
}
impl OutlivesSuggestionBuilder {
impl OutlivesSuggestionBuilder<'a> {
/// Create a new builder for the given MIR node representing a fn definition.
crate fn new(mir_def_id: DefId) -> Self {
OutlivesSuggestionBuilder { mir_def_id, constraints_to_add: BTreeMap::default() }
crate fn new(
mir_def_id: DefId,
local_names: &'a IndexVec<Local, Option<Symbol>>,
) -> Self {
OutlivesSuggestionBuilder {
mir_def_id,
local_names,
constraints_to_add: BTreeMap::default(),
}
}
/// Returns `true` iff the `RegionNameSource` is a valid source for an outlives
@ -125,6 +137,7 @@ impl OutlivesSuggestionBuilder {
infcx,
body,
mir_def_id: self.mir_def_id,
local_names: self.local_names,
// We should not be suggesting naming upvars, so we pass in a dummy set of upvars that
// should never be used.

View file

@ -11,10 +11,11 @@ use rustc::hir;
use rustc::hir::def::{Res, DefKind};
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::mir::Body;
use rustc::mir::{Local, Body};
use rustc::ty::subst::{SubstsRef, GenericArgKind};
use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
use rustc::ty::print::RegionHighlightMode;
use rustc_index::vec::IndexVec;
use rustc_errors::DiagnosticBuilder;
use syntax::symbol::kw;
use rustc_data_structures::fx::FxHashMap;
@ -210,7 +211,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fr: RegionVid,
) -> Option<RegionName> {
let ErrorReportingCtx {
infcx, body, mir_def_id, upvars, ..
infcx, body, mir_def_id, local_names, upvars, ..
} = errctx;
debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
@ -225,7 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.give_name_from_error_region(infcx.tcx, *mir_def_id, fr, renctx)
.or_else(|| {
self.give_name_if_anonymous_region_appears_in_arguments(
infcx, body, *mir_def_id, fr, renctx,
infcx, body, local_names, *mir_def_id, fr, renctx,
)
})
.or_else(|| {
@ -395,6 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
mir_def_id: DefId,
fr: RegionVid,
renctx: &mut RegionErrorNamingCtx,
@ -415,7 +417,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return Some(region_name);
}
self.give_name_if_we_cannot_match_hir_ty(infcx, body, fr, arg_ty, renctx)
self.give_name_if_we_cannot_match_hir_ty(infcx, body, local_names, fr, arg_ty, renctx)
}
fn give_name_if_we_can_match_hir_ty_from_argument(
@ -463,6 +465,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
needle_fr: RegionVid,
argument_ty: Ty<'tcx>,
renctx: &mut RegionErrorNamingCtx,
@ -479,7 +482,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
// Only add a label if we can confirm that a region was labelled.
let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
let (_, span) = self.get_argument_name_and_span_for_region(body, argument_index);
let (_, span) =
self.get_argument_name_and_span_for_region(body, local_names, argument_index);
Some(RegionName {
// This counter value will already have been used, so this function will increment

View file

@ -3,7 +3,7 @@ use crate::borrow_check::nll::ToRegionVid;
use crate::borrow_check::Upvar;
use rustc::mir::{Local, Body};
use rustc::ty::{RegionVid, TyCtxt};
use rustc_index::vec::Idx;
use rustc_index::vec::{Idx, IndexVec};
use syntax::source_map::Span;
use syntax_pos::symbol::Symbol;
@ -12,6 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
fr: RegionVid,
) -> Option<(Option<Symbol>, Span)> {
@ -27,8 +28,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
})
.or_else(|| {
debug!("get_var_name_and_span_for_region: attempting argument");
self.get_argument_index_for_region(tcx, fr)
.map(|index| self.get_argument_name_and_span_for_region(body, index))
self.get_argument_index_for_region(tcx, fr).map(|index| {
self.get_argument_name_and_span_for_region(body, local_names, index)
})
})
}
@ -117,13 +119,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
crate fn get_argument_name_and_span_for_region(
&self,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
argument_index: usize,
) -> (Option<Symbol>, Span) {
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
let argument_local = Local::new(implicit_inputs + argument_index + 1);
debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
let argument_name = body.local_decls[argument_local].name;
let argument_name = local_names[argument_local];
let argument_span = body.local_decls[argument_local].source_info.span;
debug!("get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
argument_name, argument_span);

View file

@ -36,6 +36,7 @@ use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_index::vec::IndexVec;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use syntax_pos::Span;
use syntax_pos::symbol::Symbol;
crate use self::error_reporting::{RegionName, RegionNameSource, RegionErrorNamingCtx};
use self::values::{LivenessValues, RegionValueElements, RegionValues};
@ -471,6 +472,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&mut self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
errors_buffer: &mut Vec<Diagnostic>,
@ -502,6 +504,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.check_universal_regions(
infcx,
body,
local_names,
upvars,
mir_def_id,
outlives_requirements.as_mut(),
@ -1321,13 +1324,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) {
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id);
let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names);
for (fr, fr_definition) in self.definitions.iter_enumerated() {
match fr_definition.origin {
@ -1338,6 +1342,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
self.check_universal_region(
infcx,
body,
local_names,
upvars,
mir_def_id,
fr,
@ -1374,11 +1379,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
longer_fr: RegionVid,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
outlives_suggestion: &mut OutlivesSuggestionBuilder,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) {
@ -1404,6 +1410,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
representative,
infcx,
body,
local_names,
upvars,
mir_def_id,
propagated_outlives_requirements,
@ -1422,6 +1429,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
shorter_fr,
infcx,
body,
local_names,
upvars,
mir_def_id,
propagated_outlives_requirements,
@ -1445,10 +1453,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
shorter_fr: RegionVid,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
local_names: &IndexVec<Local, Option<Symbol>>,
upvars: &[Upvar],
mir_def_id: DefId,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
outlives_suggestion: &mut OutlivesSuggestionBuilder,
outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) -> Option<ErrorReported> {
@ -1502,6 +1511,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// error. This gives better error messages in some cases.
let db = self.report_error(
body,
local_names,
upvars,
infcx,
mir_def_id,

View file

@ -227,9 +227,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mutability: Mutability::Mut,
ty: ptr_ty,
user_ty: UserTypeProjections::none(),
name: None,
source_info,
visibility_scope: source_info.scope,
internal: true,
local_info: LocalInfo::Other,
is_block_tail: None,

View file

@ -1721,6 +1721,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
let tcx = self.hir.tcx();
let debug_source_info = SourceInfo {
span: source_info.span,
scope: visibility_scope,
};
let binding_mode = match mode {
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability.into()),
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability.into()),
@ -1730,9 +1734,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mutability,
ty: var_ty,
user_ty,
name: Some(name),
source_info,
visibility_scope,
internal: false,
is_block_tail: None,
local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
@ -1749,6 +1751,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
))),
};
let for_arm_body = self.local_decls.push(local);
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
place: for_arm_body.into(),
});
let locals = if has_guard.0 {
let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
// This variable isn't mutated but has a name, so has to be
@ -1756,13 +1763,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mutability: Mutability::Not,
ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
user_ty: UserTypeProjections::none(),
name: Some(name),
source_info,
visibility_scope,
internal: false,
is_block_tail: None,
local_info: LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard)),
});
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
place: ref_for_guard.into(),
});
LocalsForNode::ForGuard {
ref_for_guard,
for_arm_body,

View file

@ -161,8 +161,18 @@ pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
(None, fn_sig.output())
};
build::construct_fn(cx, id, arguments, safety, abi,
return_ty, yield_ty, return_ty_span, body)
let mut mir = build::construct_fn(
cx,
id,
arguments,
safety,
abi,
return_ty,
return_ty_span,
body,
);
mir.yield_ty = yield_ty;
mir
} else {
// Get the revealed type of this const. This is *not* the adjusted
// type of its body, which may be a subtype of this type. For
@ -312,10 +322,11 @@ struct Builder<'a, 'tcx> {
var_indices: HirIdMap<LocalsForNode>,
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
canonical_user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>,
__upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
upvar_mutbls: Vec<Mutability>,
unit_temp: Option<Place<'tcx>>,
var_debug_info: Vec<VarDebugInfo<'tcx>>,
/// Cached block with the `RESUME` terminator; this is created
/// when first set of cleanups are built.
cached_resume_block: Option<BasicBlock>,
@ -539,7 +550,6 @@ fn construct_fn<'a, 'tcx, A>(
safety: Safety,
abi: Abi,
return_ty: Ty<'tcx>,
yield_ty: Option<Ty<'tcx>>,
return_ty_span: Span,
body: &'tcx hir::Body,
) -> Body<'tcx>
@ -552,58 +562,14 @@ where
let tcx_hir = tcx.hir();
let span = tcx_hir.span(fn_id);
let hir_tables = hir.tables();
let fn_def_id = tcx_hir.local_def_id(fn_id);
// Gather the upvars of a closure, if any.
let mut upvar_mutbls = vec![];
// In analyze_closure() in upvar.rs we gathered a list of upvars used by a
// closure and we stored in a map called upvar_list in TypeckTables indexed
// with the closure's DefId. Here, we run through that vec of UpvarIds for
// the given closure and use the necessary information to create UpvarDecl.
let upvar_debuginfo: Vec<_> = hir_tables
.upvar_list
.get(&fn_def_id)
.into_iter()
.flatten()
.map(|(&var_hir_id, &upvar_id)| {
let capture = hir_tables.upvar_capture(upvar_id);
let by_ref = match capture {
ty::UpvarCapture::ByValue => false,
ty::UpvarCapture::ByRef(..) => true,
};
let mut debuginfo = UpvarDebuginfo {
debug_name: kw::Invalid,
by_ref,
};
let mut mutability = Mutability::Not;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_hir_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
debuginfo.debug_name = ident.name;
if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
if bm == ty::BindByValue(hir::Mutability::Mutable) {
mutability = Mutability::Mut;
} else {
mutability = Mutability::Not;
}
} else {
tcx.sess.delay_span_bug(pat.span, "missing binding mode");
}
}
}
upvar_mutbls.push(mutability);
debuginfo
})
.collect();
let mut builder = Builder::new(hir,
span,
arguments.len(),
safety,
return_ty,
return_ty_span,
upvar_debuginfo,
upvar_mutbls,
body.generator_kind.is_some());
let call_site_scope = region::Scope {
@ -631,7 +597,7 @@ where
Place::return_place(),
|builder| {
builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
builder.args_and_body(block, &arguments, arg_scope, &body.value)
builder.args_and_body(block, fn_def_id, &arguments, arg_scope, &body.value)
})
},
));
@ -660,7 +626,7 @@ where
info!("fn_id {:?} has attrs {:?}", fn_def_id,
tcx.get_attrs(fn_def_id));
let mut body = builder.finish(yield_ty);
let mut body = builder.finish();
body.spread_arg = spread_arg;
body
}
@ -681,8 +647,6 @@ fn construct_const<'a, 'tcx>(
Safety::Safe,
const_ty,
const_ty_span,
vec![],
vec![],
false,
);
@ -704,7 +668,7 @@ fn construct_const<'a, 'tcx>(
TerminatorKind::Unreachable);
}
builder.finish(None)
builder.finish()
}
fn construct_error<'a, 'tcx>(
@ -714,10 +678,10 @@ fn construct_error<'a, 'tcx>(
let owner_id = hir.tcx().hir().body_owner(body_id);
let span = hir.tcx().hir().span(owner_id);
let ty = hir.tcx().types.err;
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![], false);
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, false);
let source_info = builder.source_info(span);
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
builder.finish(None)
builder.finish()
}
impl<'a, 'tcx> Builder<'a, 'tcx> {
@ -727,8 +691,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
safety: Safety,
return_ty: Ty<'tcx>,
return_span: Span,
__upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
upvar_mutbls: Vec<Mutability>,
is_generator: bool)
-> Builder<'a, 'tcx> {
let lint_level = LintLevel::Explicit(hir.root_lint_level);
@ -751,10 +713,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1,
),
canonical_user_type_annotations: IndexVec::new(),
__upvar_debuginfo_codegen_only_do_not_use,
upvar_mutbls,
upvar_mutbls: vec![],
var_indices: Default::default(),
unit_temp: None,
var_debug_info: vec![],
cached_resume_block: None,
cached_return_block: None,
cached_unreachable_block: None,
@ -769,9 +731,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
builder
}
fn finish(self,
yield_ty: Option<Ty<'tcx>>)
-> Body<'tcx> {
fn finish(self) -> Body<'tcx> {
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
if block.terminator.is_none() {
span_bug!(self.fn_span, "no terminator on block {:?}", index);
@ -782,11 +742,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.basic_blocks,
self.source_scopes,
ClearCrossCrate::Set(self.source_scope_local_data),
yield_ty,
self.local_decls,
self.canonical_user_type_annotations,
self.arg_count,
self.__upvar_debuginfo_codegen_only_do_not_use,
self.var_debug_info,
self.fn_span,
self.hir.control_flow_destroyed(),
)
@ -794,6 +753,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn args_and_body(&mut self,
mut block: BasicBlock,
fn_def_id: DefId,
arguments: &[ArgInfo<'tcx>],
argument_scope: region::Scope,
ast_body: &'tcx hir::Expr)
@ -801,28 +761,100 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
{
// Allocate locals for the function arguments
for &ArgInfo(ty, _, arg_opt, _) in arguments.iter() {
// If this is a simple binding pattern, give the local a name for
// debuginfo and so that error reporting knows that this is a user
// variable. For any other pattern the pattern introduces new
// variables which will be named instead.
let (name, span) = if let Some(arg) = arg_opt {
(arg.pat.simple_ident().map(|ident| ident.name), arg.pat.span)
} else {
(None, self.fn_span)
let source_info = SourceInfo {
scope: OUTERMOST_SOURCE_SCOPE,
span: arg_opt.map_or(self.fn_span, |arg| arg.pat.span)
};
let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span, };
self.local_decls.push(LocalDecl {
let arg_local = self.local_decls.push(LocalDecl {
mutability: Mutability::Mut,
ty,
user_ty: UserTypeProjections::none(),
source_info,
visibility_scope: source_info.scope,
name,
internal: false,
local_info: LocalInfo::Other,
is_block_tail: None,
});
// If this is a simple binding pattern, give debuginfo a nice name.
if let Some(arg) = arg_opt {
if let Some(ident) = arg.pat.simple_ident() {
self.var_debug_info.push(VarDebugInfo {
name: ident.name,
source_info,
place: arg_local.into(),
});
}
}
}
let tcx = self.hir.tcx();
let tcx_hir = tcx.hir();
let hir_tables = self.hir.tables();
// In analyze_closure() in upvar.rs we gathered a list of upvars used by a
// closure and we stored in a map called upvar_list in TypeckTables indexed
// with the closure's DefId. Here, we run through that vec of UpvarIds for
// the given closure and use the necessary information to create upvar
// debuginfo and to fill `self.upvar_mutbls`.
if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
let closure_env_arg = Local::new(1);
let mut closure_env_projs = vec![];
let mut closure_ty = self.local_decls[closure_env_arg].ty;
if let ty::Ref(_, ty, _) = closure_ty.kind {
closure_env_projs.push(ProjectionElem::Deref);
closure_ty = ty;
}
let (def_id, upvar_substs) = match closure_ty.kind {
ty::Closure(def_id, substs) => (def_id, ty::UpvarSubsts::Closure(substs)),
ty::Generator(def_id, substs, _) => (def_id, ty::UpvarSubsts::Generator(substs)),
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty)
};
let upvar_tys = upvar_substs.upvar_tys(def_id, tcx);
let upvars_with_tys = upvars.iter().zip(upvar_tys);
self.upvar_mutbls = upvars_with_tys.enumerate().map(|(i, ((&var_id, &upvar_id), ty))| {
let capture = hir_tables.upvar_capture(upvar_id);
let mut mutability = Mutability::Not;
let mut name = kw::Invalid;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
name = ident.name;
if let Some(&bm) = hir_tables.pat_binding_modes().get(pat.hir_id) {
if bm == ty::BindByValue(hir::Mutability::Mutable) {
mutability = Mutability::Mut;
} else {
mutability = Mutability::Not;
}
} else {
tcx.sess.delay_span_bug(pat.span, "missing binding mode");
}
}
}
let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(Field::new(i), ty));
match capture {
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => {
projs.push(ProjectionElem::Deref);
}
};
self.var_debug_info.push(VarDebugInfo {
name,
source_info: SourceInfo {
scope: OUTERMOST_SOURCE_SCOPE,
span: tcx_hir.span(var_id),
},
place: Place {
base: closure_env_arg.into(),
projection: tcx.intern_place_elems(&projs),
},
});
mutability
}).collect();
}
let mut scope = None;

View file

@ -148,9 +148,7 @@ fn temp_decl(mutability: Mutability, ty: Ty<'_>, span: Span) -> LocalDecl<'_> {
mutability,
ty,
user_ty: UserTypeProjections::none(),
name: None,
source_info,
visibility_scope: source_info.scope,
internal: false,
local_info: LocalInfo::Other,
is_block_tail: None,
@ -204,7 +202,6 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
SourceScopeData { span: span, parent_scope: None }, 1
),
ClearCrossCrate::Clear,
None,
local_decls_for_sig(&sig, span),
IndexVec::new(),
sig.inputs().len(),
@ -371,7 +368,6 @@ impl CloneShimBuilder<'tcx> {
SourceScopeData { span: self.span, parent_scope: None }, 1
),
ClearCrossCrate::Clear,
None,
self.local_decls,
IndexVec::new(),
self.sig.inputs().len(),
@ -832,7 +828,6 @@ fn build_call_shim<'tcx>(
SourceScopeData { span: span, parent_scope: None }, 1
),
ClearCrossCrate::Clear,
None,
local_decls,
IndexVec::new(),
sig.inputs().len(),
@ -919,7 +914,6 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &Body<'_> {
SourceScopeData { span: span, parent_scope: None }, 1
),
ClearCrossCrate::Clear,
None,
local_decls,
IndexVec::new(),
sig.inputs().len(),

View file

@ -85,7 +85,6 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
body.basic_blocks().clone(),
Default::default(),
ClearCrossCrate::Clear,
None,
body.local_decls.clone(),
Default::default(),
body.arg_count,

View file

@ -62,7 +62,6 @@ use rustc_index::vec::{Idx, IndexVec};
use rustc_index::bit_set::{BitSet, BitMatrix};
use std::borrow::Cow;
use std::iter;
use std::mem;
use crate::transform::{MirPass, MirSource};
use crate::transform::simplify;
use crate::transform::no_landing_pads::no_landing_pads;
@ -427,9 +426,7 @@ fn replace_result_variable<'tcx>(
mutability: Mutability::Mut,
ty: ret_ty,
user_ty: UserTypeProjections::none(),
name: None,
source_info,
visibility_scope: source_info.scope,
internal: false,
is_block_tail: None,
local_info: LocalInfo::Other
@ -788,18 +785,12 @@ fn compute_layout<'tcx>(
}
}
let dummy_local = LocalDecl::new_internal(tcx.mk_unit(), body.span);
// Gather live locals and their indices replacing values in body.local_decls
// with a dummy to avoid changing local indices.
// Gather live local types and their indices.
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
let mut decls = IndexVec::<GeneratorSavedLocal, _>::new();
for (idx, local) in live_locals.iter().enumerate() {
let var = mem::replace(&mut body.local_decls[local], dummy_local.clone());
locals.push(local);
tys.push(var.ty);
decls.push(var);
tys.push(body.local_decls[local].ty);
debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
}
@ -831,7 +822,6 @@ fn compute_layout<'tcx>(
field_tys: tys,
variant_fields,
storage_conflicts,
__local_debuginfo_codegen_only_do_not_use: decls,
};
(remap, layout, storage_liveness)
@ -962,9 +952,7 @@ fn create_generator_drop_shim<'tcx>(
mutability: Mutability::Mut,
ty: tcx.mk_unit(),
user_ty: UserTypeProjections::none(),
name: None,
source_info,
visibility_scope: source_info.scope,
internal: false,
is_block_tail: None,
local_info: LocalInfo::Other
@ -980,9 +968,7 @@ fn create_generator_drop_shim<'tcx>(
mutbl: hir::Mutability::Mutable,
}),
user_ty: UserTypeProjections::none(),
name: None,
source_info,
visibility_scope: source_info.scope,
internal: false,
is_block_tail: None,
local_info: LocalInfo::Other

View file

@ -219,13 +219,6 @@ impl Inliner<'tcx> {
debug!("should_inline({:?})", callsite);
let tcx = self.tcx;
// Don't inline closures that have capture debuginfo
// FIXME: Handle closures better
if callee_body.__upvar_debuginfo_codegen_only_do_not_use.len() > 0 {
debug!(" upvar debuginfo present - not inlining");
return false;
}
// Cannot inline generators which haven't been transformed yet
if callee_body.yield_ty.is_some() {
debug!(" yield ty present - not inlining");
@ -413,7 +406,6 @@ impl Inliner<'tcx> {
local.source_info.scope =
scope_map[local.source_info.scope];
local.source_info.span = callsite.location.span;
local.visibility_scope = scope_map[local.visibility_scope];
let idx = caller_body.local_decls.push(local);
local_map.push(idx);
@ -484,6 +476,10 @@ impl Inliner<'tcx> {
tcx: self.tcx,
};
for mut var_debug_info in callee_body.var_debug_info.drain(..) {
integrator.visit_var_debug_info(&mut var_debug_info);
caller_body.var_debug_info.push(var_debug_info);
}
for (bb, mut block) in callee_body.basic_blocks_mut().drain_enumerated(..) {
integrator.visit_basic_block_data(bb, &mut block);

View file

@ -1098,7 +1098,6 @@ pub fn promote_candidates<'tcx>(
// memory usage?
body.source_scopes.clone(),
body.source_scope_local_data.clone(),
None,
initial_locals,
IndexVec::new(),
0,

View file

@ -32,7 +32,6 @@ use rustc_index::vec::{Idx, IndexVec};
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext};
use rustc::session::config::DebugInfo;
use std::borrow::Cow;
use crate::transform::{MirPass, MirSource};
@ -307,13 +306,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {
marker.locals.insert(arg);
}
// We may need to keep dead user variables live for debuginfo.
if tcx.sess.opts.debuginfo == DebugInfo::Full {
for local in body.vars_iter() {
marker.locals.insert(local);
}
}
marker.locals
};

View file

@ -362,6 +362,7 @@ impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
match context {
PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {}
_ => {
local_use.use_count += 1;
if local_use.first_use.is_none() {

View file

@ -1,6 +1,6 @@
//! Def-use analysis.
use rustc::mir::{Body, Local, Location, PlaceElem};
use rustc::mir::{Body, Local, Location, PlaceElem, VarDebugInfo};
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
use rustc::ty::TyCtxt;
use rustc_index::vec::IndexVec;
@ -12,7 +12,9 @@ pub struct DefUseAnalysis {
#[derive(Clone)]
pub struct Info {
// FIXME(eddyb) use smallvec where possible.
pub defs_and_uses: Vec<Use>,
var_debug_info_indices: Vec<usize>,
}
#[derive(Clone)]
@ -33,6 +35,8 @@ impl DefUseAnalysis {
let mut finder = DefUseFinder {
info: mem::take(&mut self.info),
var_debug_info_index: 0,
in_var_debug_info: false,
};
finder.visit_body(body);
self.info = finder.info
@ -55,9 +59,14 @@ impl DefUseAnalysis {
new_local: Local,
tcx: TyCtxt<'tcx>,
) {
for place_use in &self.info[local].defs_and_uses {
MutateUseVisitor::new(local, new_local, body, tcx)
.visit_location(body, place_use.location)
let mut visitor = MutateUseVisitor::new(local, new_local, body, tcx);
let info = &self.info[local];
for place_use in &info.defs_and_uses {
visitor.visit_location(body, place_use.location)
}
// Update debuginfo as well, alongside defs/uses.
for &i in &info.var_debug_info_indices {
visitor.visit_var_debug_info(&mut body.var_debug_info[i]);
}
}
@ -73,6 +82,8 @@ impl DefUseAnalysis {
struct DefUseFinder {
info: IndexVec<Local, Info>,
var_debug_info_index: usize,
in_var_debug_info: bool,
}
impl Visitor<'_> for DefUseFinder {
@ -80,10 +91,22 @@ impl Visitor<'_> for DefUseFinder {
&local: &Local,
context: PlaceContext,
location: Location) {
self.info[local].defs_and_uses.push(Use {
context,
location,
});
let info = &mut self.info[local];
if self.in_var_debug_info {
info.var_debug_info_indices.push(self.var_debug_info_index);
} else {
info.defs_and_uses.push(Use {
context,
location,
});
}
}
fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) {
assert!(!self.in_var_debug_info);
self.in_var_debug_info = true;
self.super_var_debug_info(var_debug_info);
self.in_var_debug_info = false;
self.var_debug_info_index += 1;
}
}
@ -91,11 +114,13 @@ impl Info {
fn new() -> Info {
Info {
defs_and_uses: vec![],
var_debug_info_indices: vec![],
}
}
fn clear(&mut self) {
self.defs_and_uses.clear();
self.var_debug_info_indices.clear();
}
pub fn def_count(&self) -> usize {

View file

@ -197,13 +197,13 @@ fn write_graph_label<'tcx, W: Write>(
write!(w, "mut ")?;
}
if let Some(name) = decl.name {
write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
Place::from(local), escape(&decl.ty), name)?;
} else {
write!(w, r#"{:?}: {};<br align="left"/>"#,
Place::from(local), escape(&decl.ty))?;
}
write!(w, r#"{:?}: {};<br align="left"/>"#,
Place::from(local), escape(&decl.ty))?;
}
for var_debug_info in &body.var_debug_info {
write!(w, r#"debug {} => {};<br align="left"/>"#,
var_debug_info.name, escape(&var_debug_info.place))?;
}
writeln!(w, ">;")

View file

@ -183,6 +183,9 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
Some(DefUse::Drop),
// Debug info is neither def nor use.
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
}
}

View file

@ -463,6 +463,30 @@ fn write_scope_tree(
) -> io::Result<()> {
let indent = depth * INDENT.len();
// Local variable debuginfo.
for var_debug_info in &body.var_debug_info {
if var_debug_info.source_info.scope != parent {
// Not declared in this scope.
continue;
}
let indented_debug_info = format!(
"{0:1$}debug {2} => {3:?};",
INDENT,
indent,
var_debug_info.name,
var_debug_info.place,
);
writeln!(
w,
"{0:1$} // in {2}",
indented_debug_info,
ALIGN,
comment(tcx, var_debug_info.source_info),
)?;
}
// Local variable types (including the user's name in a comment).
for (local, local_decl) in body.local_decls.iter_enumerated() {
if (1..body.arg_count+1).contains(&local.index()) {
@ -496,8 +520,6 @@ fn write_scope_tree(
let local_name = if local == RETURN_PLACE {
format!(" return place")
} else if let Some(name) = local_decl.name {
format!(" \"{}\"", name)
} else {
String::new()
};

View file

@ -25,7 +25,7 @@ pub fn change_loop_body() {
}
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_loop_body() {
let mut _x = 0;

View file

@ -22,7 +22,7 @@ pub fn change_name() {
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,mir_built")]
except="HirBody,mir_built,optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_name() {
let _y = 2u64;
@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() {
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,typeck_tables_of,mir_built")]
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_mutability_of_slot() {
let _x: u64 = 0;
@ -182,7 +182,7 @@ pub fn add_initializer() {
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,typeck_tables_of,mir_built")]
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn add_initializer() {
let _x: i16 = 3i16;
@ -198,7 +198,7 @@ pub fn change_initializer() {
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2",
except="HirBody,mir_built")]
except="HirBody,mir_built,optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_initializer() {
let _x = 5u16;

View file

@ -25,7 +25,7 @@ pub fn change_loop_body() {
}
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_loop_body() {
let mut _x = 0;

View file

@ -25,7 +25,7 @@ pub fn change_loop_body() {
}
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_loop_body() {
let mut _x = 0;
@ -48,7 +48,7 @@ pub fn change_loop_condition() {
}
#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
#[rustc_clean(cfg="cfail3")]
pub fn change_loop_condition() {
let mut _x = 0;

View file

@ -27,6 +27,7 @@ impl Drop for S {
// let _3: ();
// let mut _4: std::boxed::Box<S>;
// scope 1 {
// debug x => _1;
// }
// bb0: {
// StorageLive(_1);

View file

@ -37,8 +37,10 @@ fn main() {
// ...
// let mut _9: Bar;
// scope 1 {
// debug a => _2;
// let _3: Bar;
// scope 2 {
// debug b => _3;
// }
// }
// bb0: {

View file

@ -17,23 +17,43 @@ fn foo<T: Copy>(_t: T, q: &i32) -> i32 {
// END RUST SOURCE
// START rustc.foo.Inline.after.mir
// ...
// bb0: {
// ...
// _3 = [closure@HirId { owner: DefIndex(4), local_id: 31 }];
// ...
// _4 = &_3;
// ...
// _6 = &(*_2);
// ...
// _7 = &(*_2);
// _5 = (move _6, move _7);
// _8 = move (_5.0: &i32);
// _9 = move (_5.1: &i32);
// ...
// _0 = (*_8);
// ...
// return;
// fn foo(_1: T, _2: &i32) -> i32{
// debug _t => _1;
// debug q => _2;
// let mut _0: i32;
// let _3: [closure@HirId { owner: DefIndex(4), local_id: 31 }];
// let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 31 }];
// let mut _5: (&i32, &i32);
// let mut _6: &i32;
// let mut _7: &i32;
// let mut _8: &i32;
// let mut _9: &i32;
// scope 1 {
// debug x => _3;
// scope 2 {
// debug r => _8;
// debug _s => _9;
// }
// }
// scope 3 {
// debug variable => _8;
// }
// bb0: {
// ...
// _3 = [closure@HirId { owner: DefIndex(4), local_id: 31 }];
// ...
// _4 = &_3;
// ...
// _6 = &(*_2);
// ...
// _7 = &(*_2);
// _5 = (move _6, move _7);
// _8 = move (_5.0: &i32);
// _9 = move (_5.1: &i32);
// ...
// _0 = (*_8);
// ...
// return;
// }
// }
// ...
// END rustc.foo.Inline.after.mir

View file

@ -0,0 +1,60 @@
// compile-flags: -Z span_free_formats
// Tests that MIR inliner can handle closure captures.
fn main() {
println!("{:?}", foo(0, 14));
}
fn foo<T: Copy>(t: T, q: i32) -> (i32, T) {
let x = |_q| (q, t);
x(q)
}
// END RUST SOURCE
// START rustc.foo.Inline.after.mir
// fn foo(_1: T, _2: i32) -> (i32, T){
// debug t => _1;
// debug q => _2;
// let mut _0: (i32, T);
// let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
// let mut _4: &i32;
// let mut _5: &T;
// let mut _6: &[closure@HirId { owner: DefIndex(4), local_id: 15 } q:&i32, t:&T];
// let mut _7: (i32,);
// let mut _8: i32;
// let mut _11: i32;
// scope 1 {
// debug x => _3;
// scope 2 {
// debug _q => _11;
// debug q => (*((*_6).0: &i32));
// debug t => (*((*_6).1: &T));
// let mut _9: i32;
// let mut _10: T;
// }
// }
// bb0: {
// ...
// _4 = &_2;
// ...
// _5 = &_1;
// _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }] { q: move _4, t: move _5 };
// ...
// _6 = &_3;
// ...
// ...
// _8 = _2;
// _7 = (move _8,);
// _11 = move (_7.0: i32);
// ...
// _9 = (*((*_6).0: &i32));
// ...
// _10 = (*((*_6).1: &T));
// (_0.0: i32) = move _9;
// (_0.1: T) = move _10;
// ...
// return;
// }
// }
// END rustc.foo.Inline.after.mir

View file

@ -13,22 +13,38 @@ fn foo<T: Copy>(_t: T, q: i32) -> i32 {
// END RUST SOURCE
// START rustc.foo.Inline.after.mir
// ...
// bb0: {
// ...
// _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }];
// ...
// _4 = &_3;
// ...
// _6 = _2;
// ...
// _7 = _2;
// _5 = (move _6, move _7);
// _8 = move (_5.0: i32);
// _9 = move (_5.1: i32);
// _0 = _8;
// ...
// return;
// }
// ...
// fn foo(_1: T, _2: i32) -> i32{
// debug _t => _1;
// debug q => _2;
// let mut _0: i32;
// let _3: [closure@HirId { owner: DefIndex(4), local_id: 15 }];
// let mut _4: &[closure@HirId { owner: DefIndex(4), local_id: 15 }];
// let mut _5: (i32, i32);
// let mut _6: i32;
// let mut _7: i32;
// let mut _8: i32;
// let mut _9: i32;
// scope 1 {
// debug x => _3;
// scope 2 {
// debug _t => _8;
// debug _q => _9;
// }
// }
// bb0: {
// ...
// _3 = [closure@HirId { owner: DefIndex(4), local_id: 15 }];
// ...
// _4 = &_3;
// ...
// _6 = _2;
// ...
// _7 = _2;
// _5 = (move _6, move _7);
// _8 = move (_5.0: i32);
// _9 = move (_5.1: i32);
// _0 = _8;
// ...
// return;
// }
// END rustc.foo.Inline.after.mir

View file

@ -35,6 +35,7 @@ impl S {
// let mut _4: S;
// let mut _5: bool;
// scope 1 {
// debug x => _1;
// }
// ...
// bb0: {
@ -47,7 +48,11 @@ impl S {
// let mut _5: S;
// let mut _6: bool;
// ...
// debug u => _1;
// ...
// let mut _2: S;
// ...
// debug v => _2;
// ...
// bb0: {
// END rustc.test.ElaborateDrops.after.mir

View file

@ -25,9 +25,11 @@ enum E {
// fn main() -> () {
// let mut _0: ();
// scope 1 {
// let _1: E; // `e`
// let _1: E;
// debug e => _1;
// scope 2 {
// let _6: K;
// debug _k => _6;
// }
// }
// let mut _2: bool;

View file

@ -24,6 +24,7 @@ fn main() {
// let _5: ();
// let mut _6: &i32;
// scope 1 {
// debug beacon => _2;
// }
// bb0: {
// goto -> bb1;

View file

@ -53,8 +53,14 @@ fn main() {
// let _15: bool; // `b`
// let _16: std::string::String; // `t`
// scope 1 {
// debug a => _5;
// debug a => _6;
// debug s => _7;
// debug s => _8;
// }
// scope 2 {
// debug b => _15;
// debug t => _16;
// }
// bb0: {
// FakeRead(ForMatchedPlace, _2);

View file

@ -29,8 +29,12 @@ fn main() {
// START rustc.main.nll.0.mir
// let _2: &'_#3r usize;
// ...
// debug p => _2;
// ...
// let _6: &'_#4r usize;
// ...
// debug q => _6;
// ...
// _2 = &'_#2r _1[_3];
// ...
// _6 = _2;

View file

@ -25,6 +25,7 @@ impl Drop for Droppy {
// let mut _5: Droppy;
// let mut _6: Aligned;
// scope 1 {
// debug x => _1;
// }
//
// bb0: {

View file

@ -10,6 +10,7 @@ fn main() {
// END RUST SOURCE
// START rustc.try_identity.SimplifyArmIdentity.before.mir
// fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
// debug x => _1;
// let mut _0: std::result::Result<u32, i32>;
// let _2: u32;
// let mut _3: std::result::Result<u32, i32>;
@ -22,21 +23,27 @@ fn main() {
// let _10: u32;
// let mut _11: u32;
// scope 1 {
// debug y => _10;
// }
// scope 2 {
// debug err => _6;
// scope 3 {
// scope 7 {
// debug t => _6;
// }
// scope 8 {
// debug v => _6;
// let mut _12: i32;
// }
// }
// }
// scope 4 {
// debug val => _10;
// scope 5 {
// }
// }
// scope 6 {
// debug self => _1;
// }
// bb0: {
// _5 = discriminant(_1);
@ -65,6 +72,7 @@ fn main() {
// START rustc.try_identity.SimplifyArmIdentity.after.mir
// fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
// debug x => _1;
// let mut _0: std::result::Result<u32, i32>;
// let _2: u32;
// let mut _3: std::result::Result<u32, i32>;
@ -77,21 +85,27 @@ fn main() {
// let _10: u32;
// let mut _11: u32;
// scope 1 {
// debug y => _10;
// }
// scope 2 {
// debug err => _6;
// scope 3 {
// scope 7 {
// debug t => _6;
// }
// scope 8 {
// debug v => _6;
// let mut _12: i32;
// }
// }
// }
// scope 4 {
// debug val => _10;
// scope 5 {
// }
// }
// scope 6 {
// debug self => _1;
// }
// bb0: {
// _5 = discriminant(_1);
@ -120,6 +134,7 @@ fn main() {
// START rustc.try_identity.SimplifyBranchSame.after.mir
// fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
// debug x => _1;
// let mut _0: std::result::Result<u32, i32>;
// let _2: u32;
// let mut _3: std::result::Result<u32, i32>;
@ -132,21 +147,27 @@ fn main() {
// let _10: u32;
// let mut _11: u32;
// scope 1 {
// debug y => _10;
// }
// scope 2 {
// debug err => _6;
// scope 3 {
// scope 7 {
// debug t => _6;
// }
// scope 8 {
// debug v => _6;
// let mut _12: i32;
// }
// }
// }
// scope 4 {
// debug val => _10;
// scope 5 {
// }
// }
// scope 6 {
// debug self => _1;
// }
// bb0: {
// _5 = discriminant(_1);
@ -166,23 +187,32 @@ fn main() {
// START rustc.try_identity.SimplifyLocals.after.mir
// fn try_identity(_1: std::result::Result<u32, i32>) -> std::result::Result<u32, i32> {
// debug x => _1;
// let mut _0: std::result::Result<u32, i32>;
// let mut _2: isize;
// let _3: i32;
// let _4: u32;
// scope 1 {
// debug y => _4;
// }
// scope 2 {
// debug err => _3;
// scope 3 {
// scope 7 {
// debug t => _3;
// }
// scope 8 {
// debug v => _3;
// }
// }
// }
// scope 4 {
// debug val => _4;
// scope 5 {
// }
// }
// scope 6 {
// debug self => _1;
// }
// bb0: {
// _2 = discriminant(_1);