From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id AXMGIqSV12pzDTsAWB0awg (envelope-from ) for ; Tue, 20 Oct 2026 12:24:04 -0400 Received: by simark.ca (Postfix, from userid 112) id 885E71E04D; Tue, 20 Oct 2026 12:24:04 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-5.3 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id A62881E04D for ; Tue, 20 Oct 2026 12:24:03 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4E22A4A9C01C for ; Tue, 20 Oct 2026 16:24:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 4E22A4A9C01C Received: from simark.ca (simark.ca [158.69.221.121]) by sourceware.org (Postfix) with ESMTPS id E15874A9A88C for ; Tue, 20 Oct 2026 16:23:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E15874A9A88C Authentication-Results: sourceware.org; dmarc=fail (p=none dis=none) header.from=efficios.com Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=efficios.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E15874A9A88C Authentication-Results: sourceware.org; arc=none smtp.remote-ip=158.69.221.121 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792513383; cv=none; b=s9211fxA90U/wkUouHcdrzTy40SCHdKBM5EklKWLBIiNrcH40WSxpOmwOOyHXtC3hbj9Zu96OpK9kus0jAOgLYGJVSC4cTWYsP8fIjXWsPqnqmGXEernidUHsLiWpeDcnK/u3Q56VBMgcfp2LI8jR0b7d8MRlk1O8Wh4coTTUXA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792513383; c=relaxed/simple; bh=2CimrtAVZPt5Y8RHBA6MjD+BZB/9dkPehw+5US52iSk=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=xwDcTwGVATq/ajdYd5TxDAvOfnRNcWapBWPAv81r7+Uy2kh0EgkhwY7n2V/KNXnr21hfvRfb2ebJIeY69mcL5fu2XzZ5n0YWuKrf5725/hrO/jOifpvIc9so8+Bqg8Xxzm0jpu/vnVvdvOliyfRfA02GmJSCpAQr98Bianr2NWg= ARC-Authentication-Results: i=1; sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E15874A9A88C Received: by simark.ca (Postfix) id 796D51E090; Tue, 20 Oct 2026 12:23:01 -0400 (EDT) From: Simon Marchi To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 12/13] gdb: handle character literal encoding prefixes in C++ name parser Date: Tue, 20 Oct 2026 12:17:34 -0400 Message-ID: <20261020162054.2140671-13-simon.marchi@efficios.com> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261020162054.2140671-1-simon.marchi@efficios.com> References: <20261020162054.2140671-1-simon.marchi@efficios.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org From: Simon Marchi clang produces template names with character literals with an encoding prefix, for instance: WChar Char16 Char32 Char8 The C++ name parser doesn't handle those, so these names fail to canonicalize, and don't match the demangler's spelling of the same types: WChar<(wchar_t)97> Char16<(char16_t)97> Char32<(char32_t)97> Char8<(char8_t)97> This causes the same problem with RTTI lookups as described in the previous patch. Fix it by handling the L, u, U and u8 prefixes in the lexer, which give the literal the type wchar_t, char16_t, char32_t and char8_t respectively. Add a TYPE_NAME parameter to lex_char_literal for that, instead of always using "char". Add some tests: - canonicalization selftests - a parser selftest, checking that a prefixed character literal at the end of the input is reported as unterminated - cases to gdb.dwarf2/nontype-template-lookup.exp - cases to gdb.cp/nontype-template-rtti.exp The latter is now compiled with -std=c++20 when the compiler supports it, to test char8_t. Add a cxx_compiler_supports_cxx20 proc in lib/gdb.exp for that. With gcc, the new cases fail for the same reason as the existing one (gcc spells the arguments as plain numbers, like "97"), so they are marked as known failures. Change-Id: I95f023ecc6c00894e68a5aad65f35b1a2280a406 --- gdb/cp-name-parser.c | 63 +++++++++++++++++-- gdb/testsuite/gdb.cp/nontype-template-rtti.cc | 6 ++ .../gdb.cp/nontype-template-rtti.exp | 18 +++++- .../gdb.dwarf2/nontype-template-lookup.exp | 28 +++++++++ gdb/testsuite/lib/gdb.exp | 7 +++ 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/gdb/cp-name-parser.c b/gdb/cp-name-parser.c index b8007187b35b..abc2e2f19eed 100644 --- a/gdb/cp-name-parser.c +++ b/gdb/cp-name-parser.c @@ -563,12 +563,16 @@ cpname_yyerror (cp_name_parser::cpname_state *state, const char *msg) } /* Lex a character constant ('0' or '\177' for example), starting at the - opening quote at STATE->LEXPTR. Store the result in LVALP and return - INT, or ERROR on error. */ + opening quote at STATE->LEXPTR. + + TYPE_NAME is the name of the character's type, which depends on the + encoding prefix that precedes the quote, if any. + + Store the result in LVALP and return INT, or ERROR on error. */ static int lex_char_literal (cp_name_parser_YYSTYPE *lvalp, - cp_name_parser::cpname_state *state) + cp_name_parser::cpname_state *state, const char *type_name) { /* Skip opening quote. */ state->lexptr++; @@ -614,7 +618,7 @@ lex_char_literal (cp_name_parser_YYSTYPE *lvalp, lvalp->comp = state->fill_comp (DEMANGLE_COMPONENT_LITERAL, - state->make_builtin_type ("char"), + state->make_builtin_type (type_name), state->make_name (copy, strlen (copy))); return INT; @@ -670,7 +674,40 @@ cpname_yylex (cp_name_parser_YYSTYPE *lvalp, goto retry; case '\'': - return lex_char_literal (lvalp, state); + return lex_char_literal (lvalp, state, "char"); + + case 'L': + if (tokstart[1] == '\'') + { + /* L'...' */ + state->lexptr++; + return lex_char_literal (lvalp, state, "wchar_t"); + } + break; + + case 'u': + if (tokstart[1] == '\'') + { + /* u'...' */ + state->lexptr++; + return lex_char_literal (lvalp, state, "char16_t"); + } + else if (tokstart[1] == '8' && tokstart[2] == '\'') + { + /* u8'...' */ + state->lexptr += 2; + return lex_char_literal (lvalp, state, "char8_t"); + } + break; + + case 'U': + if (tokstart[1] == '\'') + { + /* U'...' */ + state->lexptr++; + return lex_char_literal (lvalp, state, "char32_t"); + } + break; case '(': if (startswith (tokstart, "(anonymous namespace)")) @@ -1119,6 +1156,21 @@ canonicalize_tests () should_canonicalize_to ("C<(unsigned char) 200>", "C<(unsigned char)200>"); should_canonicalize_to ("C<(signed char)'\\x05'>", "C<(signed char)5>"); + /* Character constants with an encoding prefix. */ + should_canonicalize_to ("C", "C<(wchar_t)97>"); + should_canonicalize_to ("C<(wchar_t) 97>", "C<(wchar_t)97>"); + should_canonicalize_to ("C", "C<(char16_t)97>"); + should_canonicalize_to ("C<(char16_t) 97>", "C<(char16_t)97>"); + should_canonicalize_to ("C", "C<(char32_t)97>"); + should_canonicalize_to ("C<(char32_t) 97>", "C<(char32_t)97>"); + should_canonicalize_to ("C", "C<(char8_t)97>"); + should_canonicalize_to ("C<(char8_t) 97>", "C<(char8_t)97>"); + should_canonicalize_to ("C", "C<(char32_t)128512>"); + should_canonicalize_to ("C<(char32_t) 128512>", "C<(char32_t)128512>"); + + /* Identifiers that start like an encoding prefix. */ + should_canonicalize_to ("C", "C"); + should_canonicalize_to ("something< void() >", "something"); should_canonicalize_to ("something", "something"); @@ -1146,6 +1198,7 @@ parser_tests () should_not_parse ("C<'", "unterminated character constant"); should_not_parse ("C<'a", "unterminated character constant"); should_not_parse ("C<'\\", "unterminated character constant"); + should_not_parse ("C", "invalid escape sequence"); should_not_parse ("C<'\\x", "invalid escape sequence"); diff --git a/gdb/testsuite/gdb.cp/nontype-template-rtti.cc b/gdb/testsuite/gdb.cp/nontype-template-rtti.cc index c12203fbee39..e19c7e4371ba 100644 --- a/gdb/testsuite/gdb.cp/nontype-template-rtti.cc +++ b/gdb/testsuite/gdb.cp/nontype-template-rtti.cc @@ -28,6 +28,12 @@ struct S : base base *uchar_200 = new S; base *schar_5 = new S; +base *wchar_a = new S; +base *char16_a = new S; +base *char32_a = new S; +#ifdef __cpp_char8_t +base *char8_a = new S; +#endif int main () diff --git a/gdb/testsuite/gdb.cp/nontype-template-rtti.exp b/gdb/testsuite/gdb.cp/nontype-template-rtti.exp index b67736d37697..f3de351b648a 100644 --- a/gdb/testsuite/gdb.cp/nontype-template-rtti.exp +++ b/gdb/testsuite/gdb.cp/nontype-template-rtti.exp @@ -25,7 +25,13 @@ require allow_cplus_tests standard_testfile .cc -if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { +# char8_t requires C++20. +set flags {debug c++} +if {[cxx_compiler_supports_cxx20]} { + lappend flags additional_flags=-std=c++20 +} + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile $flags]} { return } @@ -46,6 +52,13 @@ gdb_test_no_output "set print static-members off" set objects { {uchar_200 "unsigned char" 0xc8} {schar_5 "signed char" 0x5} + {wchar_a wchar_t 0x61} + {char16_a char16_t 0x61} + {char32_a char32_t 0x61} +} + +if {[cxx_compiler_supports_cxx20]} { + lappend objects {char8_a char8_t 0x61} } foreach obj $objects { @@ -53,7 +66,8 @@ foreach obj $objects { # gcc spells the template arguments of these objects in a way GDB # doesn't match yet. - if {[test_compiler_info gcc-* c++] && $var in {uchar_200 schar_5}} { + if {[test_compiler_info gcc-* c++] + && $var in {uchar_200 schar_5 wchar_a char16_a char32_a char8_a}} { setup_kfail "gdb/19320" *-*-* } diff --git a/gdb/testsuite/gdb.dwarf2/nontype-template-lookup.exp b/gdb/testsuite/gdb.dwarf2/nontype-template-lookup.exp index 464766c7c3fb..537d5cb1e9e1 100644 --- a/gdb/testsuite/gdb.dwarf2/nontype-template-lookup.exp +++ b/gdb/testsuite/gdb.dwarf2/nontype-template-lookup.exp @@ -41,6 +41,30 @@ Dwarf::assemble $asm_file { DW_AT_name {SCharHexEscape<(signed char)'\\x05'>} DW_AT_byte_size 1 DW_FORM_sdata } + + # This is how clang encodes a wchar_t value. + structure_type { + DW_AT_name {WCharPrefix} + DW_AT_byte_size 1 DW_FORM_sdata + } + + # This is how clang encodes a char16_t value. + structure_type { + DW_AT_name {Char16Prefix} + DW_AT_byte_size 1 DW_FORM_sdata + } + + # This is how clang encodes a char32_t value. + structure_type { + DW_AT_name {Char32Prefix} + DW_AT_byte_size 1 DW_FORM_sdata + } + + # This is how clang encodes a char8_t value. + structure_type { + DW_AT_name {Char8Prefix} + DW_AT_byte_size 1 DW_FORM_sdata + } } } } @@ -55,6 +79,10 @@ if { [prepare_for_testing "failed to prepare" $testfile \ foreach name [list \ {UCharHexEscape<(unsigned char)200>} \ {SCharHexEscape<(signed char)5>} \ + {WCharPrefix<(wchar_t)97>} \ + {Char16Prefix<(char16_t)97>} \ + {Char32Prefix<(char32_t)97>} \ + {Char8Prefix<(char8_t)97>} \ ] { gdb_test "ptype $name" \ "type = struct [string_to_regexp $name] \{.*\}" diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 1aae8cad1ae1..71e1e23bc041 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -5761,6 +5761,13 @@ gdb_caching_proc has_int128_cxx {} { return [gdb_int128_helper c++] } +# Return true if the C++ compiler supports C++20, through the +# -std=c++20 flag. +gdb_caching_proc cxx_compiler_supports_cxx20 {} { + return [gdb_can_simple_compile cxx_compiler_supports_cxx20 {int x;} \ + object {c++ additional_flags=-std=c++20}] +} + # Return true if the IFUNC feature is supported. gdb_caching_proc allow_ifunc_tests {} { if {[gdb_can_simple_compile ifunc { -- 2.56.0