Repository: rust
Issue: Unavailable variables under the Visual Studio Debugger at the function end
Labels: A-debuginfo,C-bug,WG-debugging,E-needs-investigation

Rust version:
```
cargo 1.76.0-nightly (1aa9df1a5 2023-12-12)
release: 1.76.0-nightly
commit-hash: 1aa9df1a5be205cce621f0bc0ea6062a5e22a98c
commit-date: 2023-12-12
host: x86_64-pc-windows-msvc
libgit2: 1.7.1 (sys:0.18.1 vendored)
libcurl: 8.5.0-DEV (sys:0.4.70+curl-8.5.0 vendored ssl:Schannel)
os: Windows 10.0.19045 (Windows 10 Pro) [64-bit]
```

Rust code:
```rs
pub fn main() {
    let x = 10;
    let y = x + 10;
}
```

How it looks now:
![image](https://github.com/rust-lang/rust/assets/16454588/79770ab6-ced8-4af1-ac82-c688eee07f36)
The `y` variable never appears in the window in this case.

I slightly modified the LLVM output in order to be easily tested with clang (`clang main.ll -g -gcodeview`).
<details><summary>(Semi) Original LLVM IR</summary>
<p>

```
; ModuleID = 'example.1d7810305fd8544f-cgu.0'
source_filename = "example.1d7810305fd8544f-cgu.0"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"

@alloc_f58f411d2051cb7654f7b8a078d6577a = private unnamed_addr constant <{ [69 x i8] }> <{ [69 x i8] c"/tmp/compiler-explorer-compiler20231118-8707-17mgdgd.x5dse/example.rs" }>, align 1
@alloc_2aef29fe3608c713caacd9ceb2662f82 = private unnamed_addr constant <{ ptr, [16 x i8] }> <{ ptr @alloc_f58f411d2051cb7654f7b8a078d6577a, [16 x i8] c"E\00\00\00\00\00\00\00\03\00\00\00\0D\00\00\00" }>, align 8
@str.0 = internal unnamed_addr constant [28 x i8] c"attempt to add with overflow"

; example::main
; Function Attrs: uwtable
define void @main() unnamed_addr #0 !dbg !6 {
start:
  %y.dbg.spill = alloca i32, align 4
  %x.dbg.spill = alloca i32, align 4
  store i32 10, ptr %x.dbg.spill, align 4, !dbg !20
  call void @llvm.dbg.declare(metadata ptr %x.dbg.spill, metadata !12, metadata !DIExpression()), !dbg !20
  %0 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 10, i32 10), !dbg !21
  %_3.0 = extractvalue { i32, i1 } %0, 0, !dbg !21
  %_3.1 = extractvalue { i32, i1 } %0, 1, !dbg !21
  %1 = call i1 @llvm.expect.i1(i1 %_3.1, i1 false), !dbg !21
  br i1 %1, label %panic, label %bb1, !dbg !21

bb1:                                              ; preds = %start
  store i32 %_3.0, ptr %y.dbg.spill, align 4, !dbg !21
  call void @llvm.dbg.declare(metadata ptr %y.dbg.spill, metadata !17, metadata !DIExpression()), !dbg !22
  ret void, !dbg !23

panic:                                            ; preds = %start
; call core_panicking_panic
  call void @core_panicking_panic(ptr align 1 @str.0, i64 28, ptr align 8 @alloc_2aef29fe3608c713caacd9ceb2662f82) #4, !dbg !21
  unreachable, !dbg !21
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
declare i1 @llvm.expect.i1(i1, i1) #2

; core_panicking_panic
; Function Attrs: cold noinline noreturn uwtable
define void @core_panicking_panic(ptr align 1, i64, ptr align 8) unnamed_addr #3 {
start:
  ret void;
}

attributes #0 = { uwtable "target-cpu"="x86-64" }
attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(none) }
attributes #3 = { cold noinline noreturn uwtable "target-cpu"="x86-64" }
attributes #4 = { noreturn }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!llvm.dbg.cu = !{!4}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 2, !"CodeView", i32 1}
!2 = !{i32 2, !"Debug Info Version", i32 3}
!3 = !{!"rustc version 1.76.0-nightly (6a6287132 2023-12-17)"}
!4 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !5, producer: "clang LLVM (rustc version 1.76.0-nightly (6a6287132 2023-12-17))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false)
!5 = !DIFile(filename: "main.rs", directory: "/tmp/compiler-explorer-compiler20231118-8707-17mgdgd.x5dse")
!6 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !8, file: !7, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, templateParams: !19, retainedNodes: !11)
!7 = !DIFile(filename: "main.rs", directory: "/tmp/compiler-explorer-compiler20231118-8707-17mgdgd.x5dse", checksumkind: CSK_SHA256, checksum: "b560e17d45bb87e6939526cf009c50f61f7ef727de0fede23b15cee6d2ab18d6")
!8 = !DINamespace(name: "example", scope: null)
!9 = !DISubroutineType(types: !10)
!10 = !{null}
!11 = !{!12, !17}
!12 = !DILocalVariable(name: "x", scope: !13, file: !7, line: 2, type: !14, align: 4)
!13 = distinct !DILexicalBlock(scope: !6, file: !7, line: 2)
!14 = !DIDerivedType(tag: DW_TAG_typedef, name: "i32", file: !15, baseType: !16)
!15 = !DIFile(filename: "<unknown>", directory: "")
!16 = !DIBasicType(name: "__int32", size: 32, encoding: DW_ATE_signed)
!17 = !DILocalVariable(name: "y", scope: !18, file: !7, line: 3, type: !14, align: 4)
!18 = distinct !DILexicalBlock(scope: !13, file: !7, line: 3)
!19 = !{}
!20 = !DILocation(line: 2, scope: !13)
!21 = !DILocation(line: 3, scope: !13)
!22 = !DILocation(line: 3, scope: !18)
!23 = !DILocation(line: 4, scope: !6)
```

</p>
</details>

I played with it a little and tried a similar code in C++ and see what clang generates, and it seems like the difference is the addition of `DILexicalBlock` in the Rust version. Removing them seems to make the debugging work as expected.

<details><summary>Diff with `DILexicalBlock` removed</summary>
<p>

```
diff --git a/exhibit_a.ll b/exhibit_b.ll
index 87d5ab8..942fab4 100755
--- a/exhibit_a.ll
+++ b/exhibit_b.ll
@@ -70,15 +70,13 @@ attributes #4 = { noreturn }
 !9 = !DISubroutineType(types: !10)
 !10 = !{null}
 !11 = !{!12, !17}
-!12 = !DILocalVariable(name: "x", scope: !13, file: !7, line: 2, type: !14, align: 4)
-!13 = distinct !DILexicalBlock(scope: !6, file: !7, line: 2)
+!12 = !DILocalVariable(name: "x", scope: !6, file: !7, line: 2, type: !14, align: 4)
 !14 = !DIDerivedType(tag: DW_TAG_typedef, name: "i32", file: !15, baseType: !16)
 !15 = !DIFile(filename: "<unknown>", directory: "")
 !16 = !DIBasicType(name: "__int32", size: 32, encoding: DW_ATE_signed)
-!17 = !DILocalVariable(name: "y", scope: !18, file: !7, line: 3, type: !14, align: 4)
-!18 = distinct !DILexicalBlock(scope: !13, file: !7, line: 3)
+!17 = !DILocalVariable(name: "y", scope: !6, file: !7, line: 3, type: !14, align: 4)
 !19 = !{}
-!20 = !DILocation(line: 2, scope: !13)
-!21 = !DILocation(line: 3, scope: !13)
-!22 = !DILocation(line: 3, scope: !18)
+!20 = !DILocation(line: 2, scope: !6)
+!21 = !DILocation(line: 3, scope: !6)
+!22 = !DILocation(line: 3, scope: !6)
 !23 = !DILocation(line: 4, scope: !6)
\ No newline at end of file
```

</p>
</details>

With the above changes:
![image](https://github.com/rust-lang/rust/assets/16454588/8b4d170d-6f5e-4417-9bec-126d751630a9)

I'm sure that `DILexicalBlock` makes a lot of sense, and clang seems to be generating them too when entering a new scope, but right now it seems like they do more harm than good. I'm not sure what the good solution for this would be, but I've been hit by this problem more times than I can remember debugging through rust code.

Can this be fixed somehow?