public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
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 = &comp;
>> +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
>       }
>


      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).