From 4fbf3044baf2a34487f65a30473dbdac07b87e15 Mon Sep 17 00:00:00 2001 From: Victoria Sing Date: Thu, 1 Oct 2026 16:09:24 -0500 Subject: [PATCH] gdb: don't let decltype(nullptr) hide the decltype keyword GDB fails to understand expressions that use "decltype" when the program being debugged contains a type named "decltype(nullptr)". Compilers use that name for std::nullptr_t in debug information, so it appears in many C++ programs and libraries. For example: (gdb) ptype decltype(global_var) A syntax error in expression, near `global_var)'. We saw this with a debug build of the ROCm runtime library. This happens because "decltype" was a normal name in C++ before C++11. To support older code, GDB first checks whether the program has something named "decltype". If it does, GDB uses that thing instead of treating "decltype" as a keyword. That check is too loose. In C++, GDB ignores anything in parentheses at the end of a name, so that "foo" finds a function "foo(int)". Because of this, the check also finds the type "decltype(nullptr)", and GDB wrongly stops treating "decltype" as a keyword. We can't fix this by requiring the exact name "decltype". GDB stores an old-style C++ function named decltype as "decltype(int)", and gdb.cp/casts.exp checks that such a function still works. Instead, only allow the loose match for functions, where the parentheses really are the function's parameters. Anything else, such as a type or a variable, must be named exactly "decltype", ignoring a namespace prefix such as "ns::". Extend gdb.dwarf2/nullptr_t.exp to stop in main and check that "decltype" still works as a keyword there. Tested on x86_64-linux, no regressions in gdb.cp and gdb.dwarf2. --- gdb/c-exp-parser.c | 36 +++++++++++++++++++++++--- gdb/testsuite/gdb.dwarf2/nullptr_t.exp | 9 +++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/gdb/c-exp-parser.c b/gdb/c-exp-parser.c index 64bf455e722..0f075bbe86a 100644 --- a/gdb/c-exp-parser.c +++ b/gdb/c-exp-parser.c @@ -677,6 +677,34 @@ lex_selector (const char **lex_ptr, struct stoken *token) return true; } +/* Return true if SYM, found by looking up KEYWORD, shadows that + keyword. C++ symbol lookup ignores a trailing parameter list, which + is what we want for a function such as "decltype(int)", but it also + means that looking up "decltype" can find the type + "decltype(nullptr)". Only accept a symbol that is not a function if + its unqualified name is exactly KEYWORD. This is used for all + FLAG_SHADOW keywords ("decltype", "typeof" and "restrict"). */ + +static bool +symbol_shadows_keyword (const struct symbol *sym, const char *keyword) +{ + if (sym->loc_class () == LOC_BLOCK) + return true; + + const char *name = sym->search_name (); + + /* cp_entire_prefix_len assumes a fully-qualified name with no + leading "::". */ + if (startswith (name, "::")) + name += 2; + + unsigned int prefix_len = cp_entire_prefix_len (name); + if (prefix_len > 0) + name += prefix_len + 2; + + return strcmp (name, keyword) == 0; +} + /* Read one token, getting characters through lexptr. */ static int @@ -1042,10 +1070,12 @@ lex_one_token (struct parser_state *par_state, bool *is_quoted_name) { struct field_of_this_result is_a_field_of_this; - if (lookup_symbol (copy.c_str (), + struct symbol *sym + = lookup_symbol (copy.c_str (), pstate->expression_context_block, - SEARCH_VFT, &is_a_field_of_this).symbol - != NULL) + SEARCH_VFT, &is_a_field_of_this).symbol; + + if (sym != nullptr && symbol_shadows_keyword (sym, copy.c_str ())) { /* The keyword is shadowed. */ break; diff --git a/gdb/testsuite/gdb.dwarf2/nullptr_t.exp b/gdb/testsuite/gdb.dwarf2/nullptr_t.exp index f25988fb301..236cb2761fc 100644 --- a/gdb/testsuite/gdb.dwarf2/nullptr_t.exp +++ b/gdb/testsuite/gdb.dwarf2/nullptr_t.exp @@ -42,3 +42,12 @@ with_complaints 5 { gdb_test "maint print symbols" \ "typedef void decltype\\(nullptr\\); \r\n.*" \ "decltype(nullptr) symbol" + +if {![runto_main]} { + return +} + +# The type named "decltype(nullptr)" must not shadow the decltype +# keyword. +gdb_test "ptype decltype(5)" "type = int" +gdb_test "print alignof(decltype(5)) == alignof(int)" " = true" -- 2.43.0