fix debuginfo scoping of let-statements
This commit is contained in:
parent
7eb64b86ce
commit
9be593032d
9 changed files with 132 additions and 25 deletions
|
@ -28,7 +28,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
|
||||||
name,
|
name,
|
||||||
source_info,
|
source_info,
|
||||||
internal,
|
internal,
|
||||||
lexical_scope,
|
syntactic_scope,
|
||||||
is_user_variable
|
is_user_variable
|
||||||
});
|
});
|
||||||
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref, mutability });
|
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref, mutability });
|
||||||
|
|
|
@ -480,11 +480,83 @@ pub struct LocalDecl<'tcx> {
|
||||||
/// Source info of the local.
|
/// Source info of the local.
|
||||||
pub source_info: SourceInfo,
|
pub source_info: SourceInfo,
|
||||||
|
|
||||||
/// The *lexical* visibility scope the local is defined
|
/// The *syntactic* visibility scope the local is defined
|
||||||
/// in. If the local was defined in a let-statement, this
|
/// in. If the local was defined in a let-statement, this
|
||||||
/// is *within* the let-statement, rather than outside
|
/// is *within* the let-statement, rather than outside
|
||||||
/// of it.
|
/// of it.
|
||||||
pub lexical_scope: VisibilityScope,
|
///
|
||||||
|
/// This is needed because visibility scope of locals within a let-statement
|
||||||
|
/// is weird.
|
||||||
|
///
|
||||||
|
/// The reason is that we want the local to be *within* the let-statement
|
||||||
|
/// for lint purposes, but we want the local to be *after* the let-statement
|
||||||
|
/// for names-in-scope purposes.
|
||||||
|
///
|
||||||
|
/// That's it, if we have a let-statement like the one in this
|
||||||
|
/// function:
|
||||||
|
/// ```
|
||||||
|
/// fn foo(x: &str) {
|
||||||
|
/// #[allow(unused_mut)]
|
||||||
|
/// let mut x: u32 = { // <- one unused mut
|
||||||
|
/// let mut y: u32 = x.parse().unwrap();
|
||||||
|
/// y + 2
|
||||||
|
/// };
|
||||||
|
/// drop(x);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Then, from a lint point of view, the declaration of `x: u32`
|
||||||
|
/// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the
|
||||||
|
/// lint scopes are the same as the AST/HIR nesting.
|
||||||
|
///
|
||||||
|
/// However, from a name lookup point of view, the scopes look more like
|
||||||
|
/// as if the let-statements were `match` expressions:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn foo(x: &str) {
|
||||||
|
/// match {
|
||||||
|
/// match x.parse().unwrap() {
|
||||||
|
/// y => y + 2
|
||||||
|
/// }
|
||||||
|
/// } {
|
||||||
|
/// x => drop(x)
|
||||||
|
/// };
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// We care about the name-lookup scopes for debuginfo - if the
|
||||||
|
/// debuginfo instruction pointer is at the call to `x.parse()`, we
|
||||||
|
/// want `x` to refer to `x: &str`, but if it is at the call to
|
||||||
|
/// `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 `syntactic_scope` represent the
|
||||||
|
/// "syntactic" lint scope (with a variable being under its let
|
||||||
|
/// block) while the 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:
|
||||||
|
///
|
||||||
|
/// ROOT SCOPE
|
||||||
|
/// │{ argument x: &str }
|
||||||
|
/// │
|
||||||
|
/// │ │{ #[allow(unused_mut] } // this is actually split into 2 scopes
|
||||||
|
/// │ │ // in practice because I'm lazy.
|
||||||
|
/// │ │
|
||||||
|
/// │ │← x.syntactic_scope
|
||||||
|
/// │ │← `x.parse().unwrap()`
|
||||||
|
/// │ │
|
||||||
|
/// │ │ │← y.syntactic_scope
|
||||||
|
/// │ │
|
||||||
|
/// │ │ │{ let y: u32 }
|
||||||
|
/// │ │ │
|
||||||
|
/// │ │ │← y.source_info.scope
|
||||||
|
/// │ │ │← `y + 2`
|
||||||
|
/// │
|
||||||
|
/// │ │{ let x: u32 }
|
||||||
|
/// │ │← x.source_info.scope
|
||||||
|
/// │ │← `drop(x)` // this accesses `x: u32`
|
||||||
|
pub syntactic_scope: VisibilityScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> LocalDecl<'tcx> {
|
impl<'tcx> LocalDecl<'tcx> {
|
||||||
|
@ -499,7 +571,7 @@ impl<'tcx> LocalDecl<'tcx> {
|
||||||
span,
|
span,
|
||||||
scope: ARGUMENT_VISIBILITY_SCOPE
|
scope: ARGUMENT_VISIBILITY_SCOPE
|
||||||
},
|
},
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false
|
is_user_variable: false
|
||||||
}
|
}
|
||||||
|
@ -516,7 +588,7 @@ impl<'tcx> LocalDecl<'tcx> {
|
||||||
span,
|
span,
|
||||||
scope: ARGUMENT_VISIBILITY_SCOPE
|
scope: ARGUMENT_VISIBILITY_SCOPE
|
||||||
},
|
},
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: true,
|
internal: true,
|
||||||
is_user_variable: false
|
is_user_variable: false
|
||||||
}
|
}
|
||||||
|
@ -534,7 +606,7 @@ impl<'tcx> LocalDecl<'tcx> {
|
||||||
span,
|
span,
|
||||||
scope: ARGUMENT_VISIBILITY_SCOPE
|
scope: ARGUMENT_VISIBILITY_SCOPE
|
||||||
},
|
},
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
name: None, // FIXME maybe we do want some name here?
|
name: None, // FIXME maybe we do want some name here?
|
||||||
is_user_variable: false
|
is_user_variable: false
|
||||||
|
|
|
@ -702,7 +702,7 @@ macro_rules! make_mir_visitor {
|
||||||
name: _,
|
name: _,
|
||||||
ref $($mutability)* source_info,
|
ref $($mutability)* source_info,
|
||||||
internal: _,
|
internal: _,
|
||||||
ref $($mutability)* lexical_scope,
|
ref $($mutability)* syntactic_scope,
|
||||||
is_user_variable: _,
|
is_user_variable: _,
|
||||||
} = *local_decl;
|
} = *local_decl;
|
||||||
|
|
||||||
|
@ -711,7 +711,7 @@ macro_rules! make_mir_visitor {
|
||||||
source_info: *source_info,
|
source_info: *source_info,
|
||||||
});
|
});
|
||||||
self.visit_source_info(source_info);
|
self.visit_source_info(source_info);
|
||||||
self.visit_visibility_scope(lexical_scope);
|
self.visit_visibility_scope(syntactic_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn super_visibility_scope(&mut self,
|
fn super_visibility_scope(&mut self,
|
||||||
|
|
|
@ -237,7 +237,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
ty: ptr_ty,
|
ty: ptr_ty,
|
||||||
name: None,
|
name: None,
|
||||||
source_info,
|
source_info,
|
||||||
lexical_scope: source_info.scope,
|
syntactic_scope: source_info.scope,
|
||||||
internal: true,
|
internal: true,
|
||||||
is_user_variable: false
|
is_user_variable: false
|
||||||
});
|
});
|
||||||
|
|
|
@ -197,15 +197,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
-> Option<VisibilityScope> {
|
-> Option<VisibilityScope> {
|
||||||
assert!(!(var_scope.is_some() && lint_level.is_explicit()),
|
assert!(!(var_scope.is_some() && lint_level.is_explicit()),
|
||||||
"can't have both a var and a lint scope at the same time");
|
"can't have both a var and a lint scope at the same time");
|
||||||
|
let mut syntactic_scope = self.visibility_scope;
|
||||||
self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| {
|
self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| {
|
||||||
if var_scope.is_none() {
|
if var_scope.is_none() {
|
||||||
var_scope = Some(this.new_visibility_scope(scope_span,
|
var_scope = Some(this.new_visibility_scope(scope_span,
|
||||||
LintLevel::Inherited,
|
LintLevel::Inherited,
|
||||||
None));
|
None));
|
||||||
// If we have lints, create a new visibility scope
|
// If we have lints, create a new visibility scope
|
||||||
// that marks the lints for the locals.
|
// that marks the lints for the locals. See the comment
|
||||||
|
// on the `syntactic_scope` field for why this is needed.
|
||||||
if lint_level.is_explicit() {
|
if lint_level.is_explicit() {
|
||||||
this.visibility_scope =
|
syntactic_scope =
|
||||||
this.new_visibility_scope(scope_span, lint_level, None);
|
this.new_visibility_scope(scope_span, lint_level, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,12 +215,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
span,
|
span,
|
||||||
scope: var_scope.unwrap()
|
scope: var_scope.unwrap()
|
||||||
};
|
};
|
||||||
this.declare_binding(source_info, mutability, name, var, ty);
|
this.declare_binding(source_info, syntactic_scope, mutability, name, var, ty);
|
||||||
});
|
});
|
||||||
// Pop any scope we created for the locals.
|
|
||||||
if let Some(var_scope) = var_scope {
|
|
||||||
self.visibility_scope = var_scope;
|
|
||||||
}
|
|
||||||
var_scope
|
var_scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,21 +781,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
fn declare_binding(&mut self,
|
fn declare_binding(&mut self,
|
||||||
source_info: SourceInfo,
|
source_info: SourceInfo,
|
||||||
|
syntactic_scope: VisibilityScope,
|
||||||
mutability: Mutability,
|
mutability: Mutability,
|
||||||
name: Name,
|
name: Name,
|
||||||
var_id: NodeId,
|
var_id: NodeId,
|
||||||
var_ty: Ty<'tcx>)
|
var_ty: Ty<'tcx>)
|
||||||
-> Local
|
-> Local
|
||||||
{
|
{
|
||||||
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
|
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?}, \
|
||||||
var_id, name, var_ty, source_info);
|
syntactic_scope={:?})",
|
||||||
|
var_id, name, var_ty, source_info, syntactic_scope);
|
||||||
|
|
||||||
let var = self.local_decls.push(LocalDecl::<'tcx> {
|
let var = self.local_decls.push(LocalDecl::<'tcx> {
|
||||||
mutability,
|
mutability,
|
||||||
ty: var_ty.clone(),
|
ty: var_ty.clone(),
|
||||||
name: Some(name),
|
name: Some(name),
|
||||||
source_info,
|
source_info,
|
||||||
lexical_scope: self.visibility_scope,
|
syntactic_scope,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: true,
|
is_user_variable: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -409,6 +409,9 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
|
||||||
// RustCall pseudo-ABI untuples the last argument.
|
// RustCall pseudo-ABI untuples the last argument.
|
||||||
spread_arg = Some(Local::new(arguments.len()));
|
spread_arg = Some(Local::new(arguments.len()));
|
||||||
}
|
}
|
||||||
|
let closure_expr_id = tcx.hir.local_def_id(fn_id);
|
||||||
|
info!("fn_id {:?} has attrs {:?}", closure_expr_id,
|
||||||
|
tcx.get_attrs(closure_expr_id));
|
||||||
|
|
||||||
// Gather the upvars of a closure, if any.
|
// Gather the upvars of a closure, if any.
|
||||||
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
|
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
|
||||||
|
@ -571,7 +574,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
scope: ARGUMENT_VISIBILITY_SCOPE,
|
scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
span: pattern.map_or(self.fn_span, |pat| pat.span)
|
span: pattern.map_or(self.fn_span, |pat| pat.span)
|
||||||
},
|
},
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
name,
|
name,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false,
|
is_user_variable: false,
|
||||||
|
|
|
@ -142,7 +142,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
|
||||||
LocalDecl {
|
LocalDecl {
|
||||||
mutability, ty, name: None,
|
mutability, ty, name: None,
|
||||||
source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
|
source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false
|
is_user_variable: false
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
|
||||||
ty: ret_ty,
|
ty: ret_ty,
|
||||||
name: None,
|
name: None,
|
||||||
source_info: source_info(mir),
|
source_info: source_info(mir),
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false,
|
is_user_variable: false,
|
||||||
};
|
};
|
||||||
|
@ -562,7 +562,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
|
||||||
ty: tcx.mk_nil(),
|
ty: tcx.mk_nil(),
|
||||||
name: None,
|
name: None,
|
||||||
source_info,
|
source_info,
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false,
|
is_user_variable: false,
|
||||||
};
|
};
|
||||||
|
@ -578,7 +578,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
|
||||||
}),
|
}),
|
||||||
name: None,
|
name: None,
|
||||||
source_info,
|
source_info,
|
||||||
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
|
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
|
||||||
internal: false,
|
internal: false,
|
||||||
is_user_variable: false,
|
is_user_variable: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,6 +34,17 @@
|
||||||
// gdb-check:$6 = 20
|
// gdb-check:$6 = 20
|
||||||
// gdb-command:continue
|
// gdb-command:continue
|
||||||
|
|
||||||
|
// gdb-command:print x
|
||||||
|
// gdb-check:$5 = 10.5
|
||||||
|
// gdb-command:print y
|
||||||
|
// gdb-check:$6 = 20
|
||||||
|
// gdb-command:continue
|
||||||
|
|
||||||
|
// gdb-command:print x
|
||||||
|
// gdb-check:$5 = 11.5
|
||||||
|
// gdb-command:print y
|
||||||
|
// gdb-check:$6 = 20
|
||||||
|
// gdb-command:continue
|
||||||
|
|
||||||
// === LLDB TESTS ==================================================================================
|
// === LLDB TESTS ==================================================================================
|
||||||
|
|
||||||
|
@ -57,6 +68,18 @@
|
||||||
// lldb-check:[...]$5 = 20
|
// lldb-check:[...]$5 = 20
|
||||||
// lldb-command:continue
|
// lldb-command:continue
|
||||||
|
|
||||||
|
// lldb-command:print x
|
||||||
|
// lldb-check:[...]$4 = 10.5
|
||||||
|
// lldb-command:print y
|
||||||
|
// lldb-check:[...]$5 = 20
|
||||||
|
// lldb-command:continue
|
||||||
|
|
||||||
|
// lldb-command:print x
|
||||||
|
// lldb-check:[...]$4 = 11.5
|
||||||
|
// lldb-command:print y
|
||||||
|
// lldb-check:[...]$5 = 20
|
||||||
|
// lldb-command:continue
|
||||||
|
|
||||||
#![feature(omit_gdb_pretty_printer_section)]
|
#![feature(omit_gdb_pretty_printer_section)]
|
||||||
#![omit_gdb_pretty_printer_section]
|
#![omit_gdb_pretty_printer_section]
|
||||||
|
|
||||||
|
@ -77,6 +100,15 @@ fn main() {
|
||||||
|
|
||||||
zzz(); // #break
|
zzz(); // #break
|
||||||
sentinel();
|
sentinel();
|
||||||
|
|
||||||
|
let x = {
|
||||||
|
zzz(); // #break
|
||||||
|
sentinel();
|
||||||
|
11.5
|
||||||
|
};
|
||||||
|
|
||||||
|
zzz(); // #break
|
||||||
|
sentinel()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zzz() {()}
|
fn zzz() {()}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue