Skip to content

Commit 9be5930

Browse files
committed
fix debuginfo scoping of let-statements
1 parent 7eb64b8 commit 9be5930

File tree

9 files changed

+132
-25
lines changed

9 files changed

+132
-25
lines changed

src/librustc/ich/impls_mir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
2828
name,
2929
source_info,
3030
internal,
31-
lexical_scope,
31+
syntactic_scope,
3232
is_user_variable
3333
});
3434
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, by_ref, mutability });

src/librustc/mir/mod.rs

+77-5
Original file line numberDiff line numberDiff line change
@@ -480,11 +480,83 @@ pub struct LocalDecl<'tcx> {
480480
/// Source info of the local.
481481
pub source_info: SourceInfo,
482482

483-
/// The *lexical* visibility scope the local is defined
483+
/// The *syntactic* visibility scope the local is defined
484484
/// in. If the local was defined in a let-statement, this
485485
/// is *within* the let-statement, rather than outside
486486
/// of it.
487-
pub lexical_scope: VisibilityScope,
487+
///
488+
/// This is needed because visibility scope of locals within a let-statement
489+
/// is weird.
490+
///
491+
/// The reason is that we want the local to be *within* the let-statement
492+
/// for lint purposes, but we want the local to be *after* the let-statement
493+
/// for names-in-scope purposes.
494+
///
495+
/// That's it, if we have a let-statement like the one in this
496+
/// function:
497+
/// ```
498+
/// fn foo(x: &str) {
499+
/// #[allow(unused_mut)]
500+
/// let mut x: u32 = { // <- one unused mut
501+
/// let mut y: u32 = x.parse().unwrap();
502+
/// y + 2
503+
/// };
504+
/// drop(x);
505+
/// }
506+
/// ```
507+
///
508+
/// Then, from a lint point of view, the declaration of `x: u32`
509+
/// (and `y: u32`) are within the `#[allow(unused_mut)]` scope - the
510+
/// lint scopes are the same as the AST/HIR nesting.
511+
///
512+
/// However, from a name lookup point of view, the scopes look more like
513+
/// as if the let-statements were `match` expressions:
514+
///
515+
/// ```
516+
/// fn foo(x: &str) {
517+
/// match {
518+
/// match x.parse().unwrap() {
519+
/// y => y + 2
520+
/// }
521+
/// } {
522+
/// x => drop(x)
523+
/// };
524+
/// }
525+
/// ```
526+
///
527+
/// We care about the name-lookup scopes for debuginfo - if the
528+
/// debuginfo instruction pointer is at the call to `x.parse()`, we
529+
/// want `x` to refer to `x: &str`, but if it is at the call to
530+
/// `drop(x)`, we want it to refer to `x: u32`.
531+
///
532+
/// To allow both uses to work, we need to have more than a single scope
533+
/// for a local. We have the `syntactic_scope` represent the
534+
/// "syntactic" lint scope (with a variable being under its let
535+
/// block) while the source-info scope represents the "local variable"
536+
/// scope (where the "rest" of a block is under all prior let-statements).
537+
///
538+
/// The end result looks like this:
539+
///
540+
/// ROOT SCOPE
541+
/// │{ argument x: &str }
542+
/// │
543+
/// │ │{ #[allow(unused_mut] } // this is actually split into 2 scopes
544+
/// │ │ // in practice because I'm lazy.
545+
/// │ │
546+
/// │ │← x.syntactic_scope
547+
/// │ │← `x.parse().unwrap()`
548+
/// │ │
549+
/// │ │ │← y.syntactic_scope
550+
/// │ │
551+
/// │ │ │{ let y: u32 }
552+
/// │ │ │
553+
/// │ │ │← y.source_info.scope
554+
/// │ │ │← `y + 2`
555+
/// │
556+
/// │ │{ let x: u32 }
557+
/// │ │← x.source_info.scope
558+
/// │ │← `drop(x)` // this accesses `x: u32`
559+
pub syntactic_scope: VisibilityScope,
488560
}
489561

490562
impl<'tcx> LocalDecl<'tcx> {
@@ -499,7 +571,7 @@ impl<'tcx> LocalDecl<'tcx> {
499571
span,
500572
scope: ARGUMENT_VISIBILITY_SCOPE
501573
},
502-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
574+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
503575
internal: false,
504576
is_user_variable: false
505577
}
@@ -516,7 +588,7 @@ impl<'tcx> LocalDecl<'tcx> {
516588
span,
517589
scope: ARGUMENT_VISIBILITY_SCOPE
518590
},
519-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
591+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
520592
internal: true,
521593
is_user_variable: false
522594
}
@@ -534,7 +606,7 @@ impl<'tcx> LocalDecl<'tcx> {
534606
span,
535607
scope: ARGUMENT_VISIBILITY_SCOPE
536608
},
537-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
609+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
538610
internal: false,
539611
name: None, // FIXME maybe we do want some name here?
540612
is_user_variable: false

src/librustc/mir/visit.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ macro_rules! make_mir_visitor {
702702
name: _,
703703
ref $($mutability)* source_info,
704704
internal: _,
705-
ref $($mutability)* lexical_scope,
705+
ref $($mutability)* syntactic_scope,
706706
is_user_variable: _,
707707
} = *local_decl;
708708

@@ -711,7 +711,7 @@ macro_rules! make_mir_visitor {
711711
source_info: *source_info,
712712
});
713713
self.visit_source_info(source_info);
714-
self.visit_visibility_scope(lexical_scope);
714+
self.visit_visibility_scope(syntactic_scope);
715715
}
716716

717717
fn super_visibility_scope(&mut self,

src/librustc_mir/build/expr/into.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
237237
ty: ptr_ty,
238238
name: None,
239239
source_info,
240-
lexical_scope: source_info.scope,
240+
syntactic_scope: source_info.scope,
241241
internal: true,
242242
is_user_variable: false
243243
});

src/librustc_mir/build/matches/mod.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -196,29 +196,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
196196
pattern: &Pattern<'tcx>)
197197
-> Option<VisibilityScope> {
198198
assert!(!(var_scope.is_some() && lint_level.is_explicit()),
199-
"can't have both a var and a lint scope at the same time");
199+
"can't have both a var and a lint scope at the same time");
200+
let mut syntactic_scope = self.visibility_scope;
200201
self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| {
201202
if var_scope.is_none() {
202203
var_scope = Some(this.new_visibility_scope(scope_span,
203204
LintLevel::Inherited,
204205
None));
205206
// If we have lints, create a new visibility scope
206-
// that marks the lints for the locals.
207+
// that marks the lints for the locals. See the comment
208+
// on the `syntactic_scope` field for why this is needed.
207209
if lint_level.is_explicit() {
208-
this.visibility_scope =
210+
syntactic_scope =
209211
this.new_visibility_scope(scope_span, lint_level, None);
210212
}
211213
}
212214
let source_info = SourceInfo {
213215
span,
214216
scope: var_scope.unwrap()
215217
};
216-
this.declare_binding(source_info, mutability, name, var, ty);
218+
this.declare_binding(source_info, syntactic_scope, mutability, name, var, ty);
217219
});
218-
// Pop any scope we created for the locals.
219-
if let Some(var_scope) = var_scope {
220-
self.visibility_scope = var_scope;
221-
}
222220
var_scope
223221
}
224222

@@ -783,21 +781,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
783781

784782
fn declare_binding(&mut self,
785783
source_info: SourceInfo,
784+
syntactic_scope: VisibilityScope,
786785
mutability: Mutability,
787786
name: Name,
788787
var_id: NodeId,
789788
var_ty: Ty<'tcx>)
790789
-> Local
791790
{
792-
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
793-
var_id, name, var_ty, source_info);
791+
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?}, \
792+
syntactic_scope={:?})",
793+
var_id, name, var_ty, source_info, syntactic_scope);
794794

795795
let var = self.local_decls.push(LocalDecl::<'tcx> {
796796
mutability,
797797
ty: var_ty.clone(),
798798
name: Some(name),
799799
source_info,
800-
lexical_scope: self.visibility_scope,
800+
syntactic_scope,
801801
internal: false,
802802
is_user_variable: true,
803803
});

src/librustc_mir/build/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
409409
// RustCall pseudo-ABI untuples the last argument.
410410
spread_arg = Some(Local::new(arguments.len()));
411411
}
412+
let closure_expr_id = tcx.hir.local_def_id(fn_id);
413+
info!("fn_id {:?} has attrs {:?}", closure_expr_id,
414+
tcx.get_attrs(closure_expr_id));
412415

413416
// Gather the upvars of a closure, if any.
414417
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
@@ -571,7 +574,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
571574
scope: ARGUMENT_VISIBILITY_SCOPE,
572575
span: pattern.map_or(self.fn_span, |pat| pat.span)
573576
},
574-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
577+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
575578
name,
576579
internal: false,
577580
is_user_variable: false,

src/librustc_mir/shim.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ fn temp_decl(mutability: Mutability, ty: Ty, span: Span) -> LocalDecl {
142142
LocalDecl {
143143
mutability, ty, name: None,
144144
source_info: SourceInfo { scope: ARGUMENT_VISIBILITY_SCOPE, span },
145-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
145+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
146146
internal: false,
147147
is_user_variable: false
148148
}

src/librustc_mir/transform/generator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
301301
ty: ret_ty,
302302
name: None,
303303
source_info: source_info(mir),
304-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
304+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
305305
internal: false,
306306
is_user_variable: false,
307307
};
@@ -562,7 +562,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
562562
ty: tcx.mk_nil(),
563563
name: None,
564564
source_info,
565-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
565+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
566566
internal: false,
567567
is_user_variable: false,
568568
};
@@ -578,7 +578,7 @@ fn create_generator_drop_shim<'a, 'tcx>(
578578
}),
579579
name: None,
580580
source_info,
581-
lexical_scope: ARGUMENT_VISIBILITY_SCOPE,
581+
syntactic_scope: ARGUMENT_VISIBILITY_SCOPE,
582582
internal: false,
583583
is_user_variable: false,
584584
};

src/test/debuginfo/shadowed-variable.rs

+32
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@
3434
// gdb-check:$6 = 20
3535
// gdb-command:continue
3636

37+
// gdb-command:print x
38+
// gdb-check:$5 = 10.5
39+
// gdb-command:print y
40+
// gdb-check:$6 = 20
41+
// gdb-command:continue
42+
43+
// gdb-command:print x
44+
// gdb-check:$5 = 11.5
45+
// gdb-command:print y
46+
// gdb-check:$6 = 20
47+
// gdb-command:continue
3748

3849
// === LLDB TESTS ==================================================================================
3950

@@ -57,6 +68,18 @@
5768
// lldb-check:[...]$5 = 20
5869
// lldb-command:continue
5970

71+
// lldb-command:print x
72+
// lldb-check:[...]$4 = 10.5
73+
// lldb-command:print y
74+
// lldb-check:[...]$5 = 20
75+
// lldb-command:continue
76+
77+
// lldb-command:print x
78+
// lldb-check:[...]$4 = 11.5
79+
// lldb-command:print y
80+
// lldb-check:[...]$5 = 20
81+
// lldb-command:continue
82+
6083
#![feature(omit_gdb_pretty_printer_section)]
6184
#![omit_gdb_pretty_printer_section]
6285

@@ -77,6 +100,15 @@ fn main() {
77100

78101
zzz(); // #break
79102
sentinel();
103+
104+
let x = {
105+
zzz(); // #break
106+
sentinel();
107+
11.5
108+
};
109+
110+
zzz(); // #break
111+
sentinel()
80112
}
81113

82114
fn zzz() {()}

0 commit comments

Comments
 (0)