From: Bruno Larsen <blarsen@redhat.com>
To: Andrew Burgess <aburgess@redhat.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH 2/2] gdb/c++: Improve error messages in overload resolution
Date: Fri, 4 Nov 2022 14:55:38 +0100 [thread overview]
Message-ID: <a18ed1b3-9b1a-ceac-fe20-f120b2511b68@redhat.com> (raw)
In-Reply-To: <871qqoc9vw.fsf@redhat.com>
Cheers,
Bruno
On 31/10/2022 16:04, Andrew Burgess wrote:
> Bruno Larsen via Gdb-patches <gdb-patches@sourceware.org> writes:
>
>> when resolving overloaded functions, GDB relies on knowing relationships
>> between types, i.e. if a type inherits from another. However, some
>> compilers may not add complete information for given types as a way to
>> reduce unnecessary debug information. In these cases, GDB would just say
>> that it couldn't resolve the method or function, with no extra
>> information.
>>
>> The problem is that sometimes the user may not know that the type
>> information is incomplete, and may just assume that there is a bug in
>> GDB. To improve the user experience, we attempt to detect if the
>> overload match failed because of an incomplete type, and warn the user
>> of this.
>>
>> This commit also adds a testcase confirming that the message is only
>> triggered in the correct scenario. This test was not developed as an
>> expansion of gdb.cp/overload.cc because it needed the dwarf assembler,
>> and porting all of overload.cc seemed unnecessary.
>> ---
>> .../gdb.cp/incomplete-type-overload.cc | 45 +++++
>> .../gdb.cp/incomplete-type-overload.exp | 183 ++++++++++++++++++
>> gdb/valops.c | 49 ++++-
>> 3 files changed, 273 insertions(+), 4 deletions(-)
>> create mode 100644 gdb/testsuite/gdb.cp/incomplete-type-overload.cc
>> create mode 100644 gdb/testsuite/gdb.cp/incomplete-type-overload.exp
>>
>> diff --git a/gdb/testsuite/gdb.cp/incomplete-type-overload.cc b/gdb/testsuite/gdb.cp/incomplete-type-overload.cc
>> new file mode 100644
>> index 00000000000..a7b85c92bbe
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.cp/incomplete-type-overload.cc
>> @@ -0,0 +1,45 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> + Copyright 2022 Free Software Foundation, Inc.
>> +
>> + This program is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License as published by
>> + the Free Software Foundation; either version 3 of the License, or
>> + (at your option) any later version.
>> +
>> + This program is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + GNU General Public License for more details.
>> +
>> + You should have received a copy of the GNU General Public License
>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>> +class
>> +base {
>> +public:
>> + int member;
>> +};
> I think this should be written as:
>
> struct base
> {
> int member;
> };
>
> Having 'class' immediately followed by 'public:' is, if I understand
> correctly, what 'struct' does, so we could just use that. I might be
> tempted to switch the 'class'-es below to struct too, but that's up to
> you.
Ok, I'll do that
>
>> +
>> +class complete: public base { };
>> +
>> +class incomplete: public base { };
>> +
>> +complete comp;
>> +complete *cp = ∁
>> +incomplete *inc;
>> +int *ip;
>> +
>> +int
>> +foo (base* b)
>> +{
>> + asm ("foo_label: .globl foo_label");
>> + return 1;
> Indent by 2 spaces.
>
>> +}
>> +
>> +int
>> +main (void)
> No need for 'void' here in C++ code.
>
>> +{
>> + asm("main_label: .globl main_label");
> Missing space before '('.
>
>> + comp.member = 0;
>> + return 0;
>> +}
>> diff --git a/gdb/testsuite/gdb.cp/incomplete-type-overload.exp b/gdb/testsuite/gdb.cp/incomplete-type-overload.exp
>> new file mode 100644
>> index 00000000000..96ed25dd5d1
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.cp/incomplete-type-overload.exp
>> @@ -0,0 +1,183 @@
>> +# Copyright 2022 Free Software Foundation, Inc.
>> +
>> +# This program is free software; you can redistribute it and/or modify
>> +# it under the terms of the GNU General Public License as published by
>> +# the Free Software Foundation; either version 3 of the License, or
>> +# (at your option) any later version.
>> +#
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> +# GNU General Public License for more details.
>> +#
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
>> +
>> +# This file is part of GDB's testsuite.
>> +
>> +# This test intends to check the error message that GDB emits when unable
>> +# to determine the correct overloaded function due to incomplete types.
>> +
>> +load_lib dwarf.exp
>> +
>> +if { [skip_cplus_tests] } { return }
>> +
>> +if { ![dwarf2_support] } { return }
>> +
>> +standard_testfile .cc .S
>> +set asm_file [standard_output_file ${srcfile2}]
>> +
>> +if [prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}] {
>> + return
>> +}
>> +
>> +if {[test_compiler_info clang-*-*]} {
> Missing 'c++' language for test_compiler_info call.
>
>> + untested "gcc is required for dwarf assembler tests"
>> + return
>> +}
>> +
>> +if ![runto_main] {
>> + return
>> +}
>> +
>> +# Get important sizes to create fake dwarf for the test
>> +set int_size [get_sizeof "int" -1]
>> +set addr_size [get_sizeof "void *" -1]
>> +set struct_base_size [get_sizeof "base" 4]
>> +set struct_complete_size [get_sizeof "complete" 4]
>> +get_func_info foo
> Not that it makes much difference, but get_func_info has the ability to
> take compiler options, so I think this should really be:
>
> get_func_info foo {debug c++}
>
>> +
>> +# Create fake DWARF for the .cc file.
>> +# This is the best way to ensure we have an incomplete type.
>> +Dwarf::assemble ${asm_file} {
>> + global srcdir subdir srcfile srcfile2 foo_start foo_end
>> + global int_size addr_size struct_base_size struct_complete_size
>> + declare_labels L
>> +
>> + cu {} {
>> + DW_TAG_compile_unit {
>> + {DW_AT_language @DW_LANG_C_plus_plus}
>> + {name $srcfile}
>> + {stmt_list $L DW_FORM_sec_offset}
>> + } {
>> + declare_labels int_label base_label complete_label incomplete_label
>> + declare_labels ptr_base_label ptr_inc_label ptr_comp_label ptr_int_label
>> +
>> + int_label: DW_TAG_base_type {
>> + {DW_AT_byte_size $int_size DW_FORM_sdata}
>> + {DW_AT_encoding @DW_ATE_signed}
>> + {DW_AT_name "int"}
>> + }
>> +
>> + base_label: DW_TAG_class_type {
>> + {DW_AT_byte_size $struct_base_size DW_FORM_sdata}
>> + {DW_AT_name "base"}
>> + } {
>> + DW_TAG_member {
>> + {DW_AT_name "member"}
>> + {DW_AT_type :$int_label}
>> + {DW_AT_data_member_location 0 DW_FORM_sdata}
>> + }
>> + }
>> +
>> + complete_label: DW_TAG_class_type {
>> + {DW_AT_byte_size $struct_complete_size DW_FORM_sdata}
>> + {DW_AT_name "complete"}
>> + } {
>> + DW_TAG_inheritance {
>> + {DW_AT_type :$base_label}
>> + {DW_AT_data_member_location 0 DW_FORM_sdata}
>> + {DW_AT_accessibility 1 DW_FORM_data1}
>> + }
>> + }
>> +
>> + incomplete_label: DW_TAG_class_type {
>> + {DW_AT_name "incomplete"}
>> + {DW_AT_declaration 1 DW_FORM_flag_present}
>> + }
>> +
>> + ptr_base_label: DW_TAG_pointer_type {
>> + {DW_AT_byte_size $addr_size DW_FORM_udata}
>> + {DW_AT_type :$base_label}
>> + }
>> +
>> + ptr_inc_label: DW_TAG_pointer_type {
>> + {DW_AT_byte_size $addr_size DW_FORM_udata}
>> + {DW_AT_type :$incomplete_label}
>> + }
>> +
>> + ptr_comp_label: DW_TAG_pointer_type {
>> + {DW_AT_byte_size $addr_size DW_FORM_udata}
>> + {DW_AT_type :$complete_label}
>> + }
>> +
>> + ptr_int_label: DW_TAG_pointer_type {
>> + {DW_AT_byte_size $addr_size DW_FORM_udata}
>> + {DW_AT_type :$int_label}
>> + }
>> +
>> + DW_TAG_variable {
>> + {DW_AT_name "comp"}
>> + {DW_AT_type :$complete_label}
>> + {DW_AT_location {DW_OP_addr [gdb_target_symbol "comp"]} SPECIAL_expr}
>> + {DW_AT_external 1 DW_FORM_flag}
>> + }
>> +
>> + DW_TAG_variable {
>> + {DW_AT_name "cp"}
>> + {DW_AT_type :$ptr_comp_label}
>> + {DW_AT_location {DW_OP_addr [gdb_target_symbol "cp"]} SPECIAL_expr}
>> + {DW_AT_external 1 DW_FORM_flag}
>> + }
>> +
>> + DW_TAG_variable {
>> + {DW_AT_name "inc"}
>> + {DW_AT_type :$ptr_inc_label}
>> + {DW_AT_location {DW_OP_addr [gdb_target_symbol "inc"]} SPECIAL_expr}
>> + {DW_AT_external 1 DW_FORM_flag}
>> + }
>> +
>> + DW_TAG_variable {
>> + {DW_AT_name "ip"}
>> + {DW_AT_type :$ptr_int_label}
>> + {DW_AT_location {DW_OP_addr [gdb_target_symbol "ip"]} SPECIAL_expr}
>> + {DW_AT_external 1 DW_FORM_flag}
>> + }
>> +
>> + DW_TAG_subprogram {
>> + {MACRO_AT_func {"main"}}
>> + {DW_AT_external 1 flag}
>> + }
>> + DW_TAG_subprogram {
>> + {MACRO_AT_func {"foo"}}
> While looking at this patch I wondered about these two MACRO_AT_ calls.
> These surely should be passing the language through too, just like the
> get_func_info call earlier?
>
> I've attached a patch at the end of this email that I wrote that allows
> these to change to:
>
> {MACRO_AT_func {"main" {debug c++}}}
> {MACRO_AT_func {"foo" {debug c++}}}
>
> I think this change would be better, but doesn't actually change the
> results of the tests or not, so feel free to use this or not, up to
> you.
This makes sense, but since it doesn't change anything I think you
should post the patch separately and maybe have a second patch changing
the relevant functions. Just so the patch series doesn't balloons.
>
>> + {DW_AT_type :$int_label}
>> + {DW_AT_external 1 flag}
>> + } { formal_parameter {
>> + {DW_AT_name "b"}
>> + {DW_AT_type :$ptr_base_label}
>> + }
>> + }
>> + }
>> + }
>> +
>> + lines {version 2} L {
>> + include_dir "$srcdir/$subdir"
>> + file_name $srcfile 1
>> + }
>> +}
>> +
>> +if [prepare_for_testing "failed to prepare" $testfile [list $asm_file $srcfile] {}] {
>> + return
>> +}
>> +
>> +if ![runto_main] {
>> + return
>> +}
>> +
>> +gdb_test "print foo(cp)" "= 1" "successful invocation"
>> +gdb_test "print foo(inc)"\
>> + "The type. 'incomplete .' isn't fully known to GDB.*"\
>> + "unsuccessful because declaration"
>> +gdb_test "print foo(ip)"\
>> + "Cannot resolve function foo to any overloaded instance."\
>> + "unsuccessful because incorrect"
>> diff --git a/gdb/valops.c b/gdb/valops.c
>> index 748f154252a..2054e9ec303 100644
>> --- a/gdb/valops.c
>> +++ b/gdb/valops.c
>> @@ -41,6 +41,7 @@
>> #include "extension.h"
>> #include "gdbtypes.h"
>> #include "gdbsupport/byte-vector.h"
>> +#include "typeprint.h"
>>
>> /* Local functions. */
>>
>> @@ -2933,14 +2934,54 @@ find_overload_match (gdb::array_view<value *> args,
>>
>> if (match_quality == INCOMPATIBLE)
>> {
>> + /* If we couldn't find a decent overload, it may be because we don't
>> + have enough information about the requested types. Warn user if
>> + this is the case, so they can manually cast the types. */
>> + int incomplete_types = 0;
>> + std::string incomplete_arg_names = "";
> I think the ' = ""' can be removed here.
>
>> + for (struct value *arg: args)
> I think GDB style is to have a space before and after ':'. Also I think
> you can make ARG const.
>
>> + {
>> + struct type *t = value_type (arg);
>> + while (t->code () == TYPE_CODE_PTR)
>> + {
>> + t = t->target_type();
>> + }
> Single statement while body should have the '{' and '}' removed.
>
>> + if (t->is_stub ())
>> + {
>> + string_file buffer;
>> + if(incomplete_types > 0)
> Missing space before '(' here.
>
>> + incomplete_arg_names += ", ";
>> +
>> + current_language->print_type(value_type(arg), "", &buffer,
>> + -1, 0, &type_print_raw_options);
> ... and here twice.
>
>> +
>> + incomplete_types++;
>> + incomplete_arg_names += buffer.string();
> ... and here again.
>
>> + }
>> + }
>> + std::string hint = "";
> I don't think the ' = ""' is needed here.
>
>> + if (incomplete_types > 1)
>> + {
>> + hint = string_printf (_("\nThe types: '%s' aren't fully known to GDB."
>> + " Please cast them directly to the desired"
>> + " typed in the function call."),
>> + incomplete_arg_names.c_str ());
>> + }
> This, and the following single statement if blocks should have the '{'
> and '}' removed.
Ah, I thought the bit about multiple lines in the coding standard also
applied to wrapping lines (at the end
ofhttps://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards#Whitespaces)
>
>> + else if (incomplete_types == 1)
>> + {
>> + hint = string_printf (_("\nThe type: '%s' isn't fully known to GDB."
>> + " Please cast it directly to the desired"
>> + " typed in the function call."),
>> + incomplete_arg_names.c_str ());
>> + }
> I think I would move this entire block of code into a new static
> function. It only requires the ARGS gdb::array_view parameter, and
> returns a std::string.
>
>> if (method == METHOD)
>> - error (_("Cannot resolve method %s%s%s to any overloaded instance"),
>> + error (_("Cannot resolve method %s%s%s to any overloaded instance.%s"),
>> obj_type_name,
>> (obj_type_name && *obj_type_name) ? "::" : "",
>> - name);
>> + name, hint.c_str());
>> else
>> - error (_("Cannot resolve function %s to any overloaded instance"),
>> - func_name);
>> + error (_("Cannot resolve function %s to any overloaded instance.%s"),
>> + func_name, hint.c_str());
>> }
>> else if (match_quality == NON_STANDARD)
>> {
>> --
>> 2.37.3
> ---
>
> commit d98626400f0e0a5721e8ea9ebdc0ea0edc480af3
> Author: Andrew Burgess <aburgess@redhat.com>
> Date: Mon Oct 31 12:57:51 2022 +0000
>
> gdb/testsuite: allow options for MACRO_AT_range and MACRO_AT_func
>
> This commit allows the MACRO_AT_range and MACRO_AT_func, as used by
> the DWARF assembler, to accept an options list. These options will
> then be forwarded to the function_range call that is contained within
> the implementation of MACRO_AT_range.
>
> This will allow tests to pass through the correct compilation flags if
> needed when these macros are used, though no such uses are added in
> this commit.
>
> diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
> index b843b1acf75..593f4ea72f1 100644
> --- a/gdb/testsuite/lib/dwarf.exp
> +++ b/gdb/testsuite/lib/dwarf.exp
> @@ -925,14 +925,20 @@ namespace eval Dwarf {
> proc _handle_macro_at_range { attr_value } {
> variable _cu_is_fission
>
> - if {[llength $attr_value] != 1} {
> - error "usage: MACRO_AT_range { func }"
> + if {[llength $attr_value] < 1 || [llength $attr_value] > 2} {
> + error "usage: MACRO_AT_range { func {options {debug}} }"
> + }
> +
> + if {[llength $attr_value] == 2} {
> + set options [lindex $attr_value 1]
> + } else {
> + set options {debug}
> }
>
> set func [lindex $attr_value 0]
> global srcdir subdir srcfile
> set src ${srcdir}/${subdir}/${srcfile}
> - set result [function_range $func $src]
> + set result [function_range $func $src $options]
>
> set form DW_FORM_addr
> if { $_cu_is_fission } {
> @@ -947,9 +953,10 @@ namespace eval Dwarf {
> # Handle macro attribute MACRO_AT_func.
>
> proc _handle_macro_at_func { attr_value } {
> - if {[llength $attr_value] != 1} {
> - error "usage: MACRO_AT_func { func file }"
> + if {[llength $attr_value] < 1 || [llength $attr_value] > 2} {
> + error "usage: MACRO_AT_func { func {options {debug}} }"
> }
> +
> _handle_attribute DW_AT_name [lindex $attr_value 0] DW_FORM_string
> _handle_macro_at_range $attr_value
> }
>
prev parent reply other threads:[~2022-11-04 13:55 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-13 16:01 [PATCH 0/2] Improve error messages with incomplete variables Bruno Larsen
2022-10-13 16:01 ` [PATCH 1/2] gdb/testsuite: allowed for function_range to deal with mangled functions Bruno Larsen
2022-10-31 14:46 ` Andrew Burgess
2022-11-03 15:46 ` Bruno Larsen
2022-10-13 16:01 ` [PATCH 2/2] gdb/c++: Improve error messages in overload resolution Bruno Larsen
2022-10-31 15:04 ` Andrew Burgess
2022-11-04 13:55 ` Bruno Larsen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a18ed1b3-9b1a-ceac-fe20-f120b2511b68@redhat.com \
--to=blarsen@redhat.com \
--cc=aburgess@redhat.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).