From: Andrew Burgess <aburgess@redhat.com>
To: Bruno Larsen <blarsen@redhat.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH 2/2] gdb/c++: Improve error messages in overload resolution
Date: Mon, 31 Oct 2022 15:04:19 +0000 [thread overview]
Message-ID: <871qqoc9vw.fsf@redhat.com> (raw)
In-Reply-To: <20221013160114.4143323-3-blarsen@redhat.com>
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.
> +
> +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.
> + {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.
> + 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
}
next prev parent reply other threads:[~2022-10-31 15:04 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 [this message]
2022-11-04 13:55 ` Bruno Larsen
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=871qqoc9vw.fsf@redhat.com \
--to=aburgess@redhat.com \
--cc=blarsen@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).