Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>(
let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) };
let subscripts = &[subrange];

let di_node = unsafe {
let mut di_node = unsafe {
llvm::LLVMDIBuilderCreateArrayType(
DIB(cx),
size.bits(),
Expand All @@ -128,6 +128,22 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>(
)
};

if cpp_like_debuginfo(cx.tcx) {
Copy link
Copy Markdown
Member

@Enselic Enselic Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, but I still don't understand why we need to change both fn build_pointer_or_reference_di_node() and fn build_fixed_size_array_di_node() in the same PR. They seem completely orthogonal to me. So far I've only looked at the change in the former.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean i guess it could be. It's all sortof under the umbrella of "we try to give this information to the debugger, the debugger doesn't care, so we need a typedef to make it care". The context and reasoning are all the same and the implementations are almost identical.

let array_type_name = compute_debuginfo_type_name(cx.tcx, array_type, false);
di_node = unsafe {
llvm::LLVMDIBuilderCreateTypedef(
DIB(cx),
di_node,
array_type_name.as_ptr(),
array_type_name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
None,
0,
)
};
}

DINodeCreationResult::new(di_node, false)
}

Expand Down Expand Up @@ -177,8 +193,20 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
pointer_align.abi,
&ptr_type_debuginfo_name,
);
let typedefed_ptr = unsafe {
Copy link
Copy Markdown
Member

@Enselic Enselic Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've dug deeper, I've discovered that this program:

fn foo(f: &usize) {
    println!("Value: {}", f);
}

fn main() {
    let x = 5;
    foo(&x);
}

with plain gdb (not rust-gdb) says that the type of f is *mut usize:

$ rustc -g /home/martin/src/main.rs && gdb -ex "b main::foo" -ex "run" -ex "ptype f" --args main
Breakpoint 1, main::foo (f=0x7fffffffe2b0) at /home/martin/src/main.rs:2
2           println!("Value: {}", f);
type = *mut usize

I've debugged gdb with gdb, and the reason is that gdb doesn't think the type have a name in this part of gdb code:

    case TYPE_CODE_PTR:
      {
        if (type->name () != nullptr)
          gdb_puts (type->name (), stream);
        else
          {
            /* We currently can't distinguish between pointers and
               references.  */
            gdb_puts ("*mut ", stream);
            type_print (type->target_type (), "", stream, 0);
          }
      }
(For reference, here is the top of the gdb stacktrace to reach that code)
#0  rust_internal_print_type (type=0x555556522700, varstring=0x555555dae803 "", stream=0x55555640c390, show=1, level=0, flags=0x7fffffffdfc0, for_rust_enum=false, podata=0x7fffffffdf3c)
    at rust-lang.c:1114
#1  0x0000555555abd054 in rust_language::print_type (this=this@entry=0x555556148730 <rust_language_defn>, type=type@entry=0x555556522780, varstring=varstring@entry=0x555555dae803 "",
    stream=0x55555640c390, show=show@entry=1, level=level@entry=0, flags=0x7fffffffdfc0) at rust-lang.c:1834
#2  0x0000555555babc66 in whatis_exp (exp=<optimized out>, show=1) at typeprint.c:497
#3  0x0000555555796ee5 in cmd_func (cmd=<optimized out>, args=<optimized out>, from_tty=<optimized out>) at cli/cli-decode.c:2810
#4  0x0000555555b75065 in execute_command (p=<optimized out>, p@entry=0x55555616c030 "ptype f", from_tty=<optimized out>) at top.c:632

But with dwarfdump main, the type info looks correct:

< 3><0x00000872>        DW_TAG_formal_parameter
                          DW_AT_location              len 0x0002: 0x9108: 
                              DW_OP_fbreg 8
                          DW_AT_name                  f
                          DW_AT_decl_file             0x00000009 /home/martin/src/main.rs
                          DW_AT_decl_line             0x00000001
                          DW_AT_type                  <0x00000723>
< 1><0x00000723>    DW_TAG_typedef
                      DW_AT_type                  <0x0000072c>
                      DW_AT_name                  &usize
< 1><0x0000072c>    DW_TAG_pointer_type
                      DW_AT_type                  <0x000000a8>
                      DW_AT_name                  &usize
                      DW_AT_address_class         0x00000000
< 1><0x000000a8>    DW_TAG_base_type
                      DW_AT_name                  usize
                      DW_AT_encoding              DW_ATE_unsigned
                      DW_AT_byte_size             0x00000008

So it looks like gdb wants to work well with Rust without any Python scripts.

To me, it seems better to focus on getting the basics right, than to stack workarounds on top of each other.

So, before we go ahead with something like this, I'd like to understand why gdb is not already working well for this case. Maybe it makes more sense to fix gdb.

Disclaimer: There might very well be decisions or context I am missing. But this is where I stand right now.

View changes since the review

Copy link
Copy Markdown
Contributor Author

@Walnut356 Walnut356 Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with plain gdb (not rust-gdb) says that the type of f is *mut usize

I'm not 100% clear on the implications of the wording, but ptype's help text reads "Contrary to "whatis", "ptype" always unrolls any typedefs.". I assume "unroll" essentially means "look through", but i'm not sure. If that's the case, whatis should print the correct type name (or maybe ptype /r?).

To me, it seems better to focus on getting the basics right, than to stack workarounds on top of each other.

So, before we go ahead with something like this, I'd like to understand why gdb is not already working well for this case.

As I understand it, at the time GDB's rust support was written, the debug info output by rustc did not differentiate between the 5 types of indirection (&, &mut, *const, *mut, Box<T>). I know the nodes have the correct names now, but i think GDB itself doesn't parse name information for whatever reason? I don't know, and digging through their DWARF parsing code (or contributing a fix) are a bit out of scope for me at the moment. The effect on GDB is mostly incidental, and happens to align with what we want anyway.

The major beneficiaries of this change are LLDB and microsoft's debugger(s). There are literally no alternatives on the PDB side of things because pointer nodes cannot store names at all, and because C/C++ reference and const semantics disagree with rust's (and we piggyback on C/C++ handling).

For DWARF debug info with LLDB, the issue is LLDB uses the clang compiler's structs for type information. I'm not sure if clang's pointer struct even has a name field. I'm relatively certain it doesn't though, because the dwarf parser reads the name tag and afaik, TypeSystemClang doesn't make any attempt to enforce a naming scheme for pointers, it just defers to clang. Asking them to change the in-memory type representation of C objects to account for rust type information is a tough sell (assuming i could even figure out how to do it in a reasonable amount of time), so this is likely the most realistic solution for LLDB too aside from creating a TypeSystemRust so that we can use our own type representation (which is, bare minimum, hundreds of hours of up-front effort and a huge ongoing maintenance burden).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it looks like gdb wants to work well with Rust without any Python scripts.

Yes, that's correct, at least for compiler-generated entities. gdb tries not to know about library-defined types.

So, before we go ahead with something like this, I'd like to understand why gdb is not already working well for this case. Maybe it makes more sense to fix gdb.

gdb started as a C debugger and sometimes there are still holes. In particular the DWARF reader seems to ignore a DW_AT_name on a DW_TAG_pointer_type, I suppose because this just never came up (it's impossible in C and C++) (though TBH I'm mildly surprised that this wasn't implemented for Ada). Anyway this is fixable.

Copy link
Copy Markdown
Member

@Enselic Enselic Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The major beneficiaries of this change are LLDB and microsoft's debugger(s).

Ok, then I would like to ask you to demonstrate that with diffs in ./tests/debuginfo, please. Because with the current diffs, only the debugging experience with gdb seems to change.

I would like to again recommend to split this PR up into two. This is a quite complicated PR (in terms of impact), and anything we can do to minimize scope of discussion and impact of change is worthwhile, IMHO.

Also, if you need to add new tests to demonstrate how the change affects the debugging experience, please add the test in a separate commit (maybe even separate PR), so that the diff of the impact of the change can easily be seen in a separate commit. Thanks!

llvm::LLVMDIBuilderCreateTypedef(
DIB(cx),
di_node,
ptr_type_debuginfo_name.as_ptr(),
ptr_type_debuginfo_name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
None,
0,
)
};

DINodeCreationResult { di_node, already_stored_in_typemap: false }
DINodeCreationResult { di_node: typedefed_ptr, already_stored_in_typemap: false }
}
Some(wide_pointer_kind) => {
type_map::build_type_with_children(
Expand Down
13 changes: 9 additions & 4 deletions src/etc/gdb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def unwrap_unique_or_non_null(unique_or_nonnull):
# BACKCOMPAT: rust 1.60
# https://github.com/rust-lang/rust/commit/2a91eeac1a2d27dd3de1bf55515d765da20fd86f
ptr = unique_or_nonnull["pointer"]
if ptr.type.code == gdb.TYPE_CODE_TYPEDEF:
ptr = ptr.cast(ptr.type.strip_typedefs())

return ptr if ptr.type.code == gdb.TYPE_CODE_PTR else ptr[ptr.type.fields()[0]]


Expand Down Expand Up @@ -147,8 +150,9 @@ def __init__(self, valobj):
self._valobj = valobj
self._length = int(valobj["len"])
self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"])
ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0))
self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty)
self._data_ptr = self._data_ptr.cast(self._data_ptr.type.strip_typedefs())
ptr_ty = valobj.type.template_argument(0).pointer()
self._data_ptr = self._data_ptr.cast(ptr_ty)

def to_string(self):
return "Vec(size={})".format(self._length)
Expand Down Expand Up @@ -177,8 +181,9 @@ def __init__(self, valobj):
cap = cap[ZERO_FIELD]
self._cap = int(cap)
self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"])
ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0))
self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty)
self._data_ptr = self._data_ptr.cast(self._data_ptr.type.strip_typedefs())
ptr_ty = valobj.type.template_argument(0).pointer()
self._data_ptr = self._data_ptr.cast(ptr_ty)

def to_string(self):
return "VecDeque(size={})".format(self._size)
Expand Down
18 changes: 10 additions & 8 deletions tests/codegen-llvm/debug-vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,39 @@

// NONMSVC: ![[USIZE:[0-9]+]] = !DIBasicType(name: "usize"
// MSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize"
// NONMSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()"
// MSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >"
// NONMSVC: ![[PTR_TYPEDEF:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "*const ()", file: {{.*}}, baseType: ![[PTR:[0-9]+]])
// MSVC: ![[PTR_TYPEDEF:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "ptr_const$<tuple$<> >", file: {{.*}}, baseType: ![[PTR:[0-9]+]])
// NONMSVC: ![[PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()"
// MSVC: ![[PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >"

// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"

// NONMSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable_type}", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
// MSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable_type$", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}})
// CHECK: ![[FOO_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo",

// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"

// NONMSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable_type}", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
// MSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable_type$", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})

// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"

// NONMSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as _>::{vtable_type}", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
// MSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, _>::vtable_type$", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR_TYPEDEF]], size: {{64|32}}, align: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})

Expand Down
6 changes: 3 additions & 3 deletions tests/debuginfo/basic-types-metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@
//@ gdb-check: type = struct basic_types_metadata::main::{closure_env#0}
//@ gdb-command:ptype closure_1
//@ gdb-check: type = struct basic_types_metadata::main::{closure_env#1} {
//@ gdb-check: *mut bool,
//@ gdb-check: _ref__b: &bool,
//@ gdb-check: }
//@ gdb-command:ptype closure_2
//@ gdb-check: type = struct basic_types_metadata::main::{closure_env#2} {
//@ gdb-check: *mut bool,
//@ gdb-check: *mut isize,
//@ gdb-check: _ref__b: &bool,
//@ gdb-check: _ref__i: &isize,
//@ gdb-check: }

//@ gdb-command:continue
Expand Down
2 changes: 1 addition & 1 deletion tests/debuginfo/function-call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//@ gdb-command:print fun(444, false)
//@ gdb-check:$2 = false

//@ gdb-command:print r.get_x()
//@ gdb-command: print function_call::RegularStruct::get_x(&r)
//@ gdb-check:$3 = 4

#![allow(dead_code, unused_variables)]
Expand Down
6 changes: 3 additions & 3 deletions tests/debuginfo/function-names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@

// Closure
//@ gdb-command:info functions -q function_names::.*::{closure.*
//@ gdb-check:[...]static fn function_names::generic_func::{closure#0}<i32>(*mut function_names::generic_func::{closure_env#0}<i32>);
//@ gdb-check:[...]static fn function_names::main::{closure#0}(*mut function_names::main::{closure_env#0});
//@ gdb-check:[...]static fn function_names::{impl#2}::impl_function::{closure#0}<i32, i32>(*mut function_names::{impl#2}::impl_function::{closure_env#0}<i32, i32>);
//@ gdb-check:[...]static fn function_names::generic_func::{closure#0}<i32>(&function_names::generic_func::{closure_env#0}<i32>);
//@ gdb-check:[...]static fn function_names::main::{closure#0}(&function_names::main::{closure_env#0});
//@ gdb-check:[...]static fn function_names::{impl#2}::impl_function::{closure#0}<i32, i32>(&function_names::{impl#2}::impl_function::{closure_env#0}<i32, i32>);

// Coroutine
// Coroutines don't seem to appear in GDB's symbol table.
Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/type-names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@

// FOREIGN TYPES
//@ gdb-command:whatis foreign1
//@ gdb-check:type = *mut type_names::{extern#0}::ForeignType1
//@ gdb-check:type = *const type_names::{extern#0}::ForeignType1

//@ gdb-command:whatis foreign2
//@ gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2
//@ gdb-check:type = *const type_names::mod1::{extern#0}::ForeignType2

// === CDB TESTS ==================================================================================

Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/unit-type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
//@ gdb-command: run

//@ gdb-command: print _ref
//@ gdb-check: $1 = (*mut ()) 0x[...]
//@ gdb-check: $1 = (&()) 0x[...]

//@ gdb-command: print _ptr
//@ gdb-check: $2 = (*mut ()) 0x[...]
//@ gdb-check: $2 = (*const ()) 0x[...]

//@ gdb-command: print _local
//@ gdb-check: $3 = ()
Expand Down
Loading