public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Lancelot SIX <lsix@lancelotsix.com>
To: Bruno Larsen <blarsen@redhat.com>
Cc: gdb-patches@sourceware.org, tom@tromey.com
Subject: Re: [PATCH v2 2/2] gdb/c++: Detect ambiguous variables in imported namespaces
Date: Wed, 16 Nov 2022 17:49:13 +0000	[thread overview]
Message-ID: <20221116174913.hd3l6yyh5z6w7n7y@ubuntu.lan> (raw)
In-Reply-To: <20221116141336.1160869-3-blarsen@redhat.com>

Hi Bruno,

Thanks for working on this.  I have included remarks inlined in the path.

On Wed, Nov 16, 2022 at 03:13:37PM +0100, Bruno Larsen via Gdb-patches wrote:
> When running gdb.cp/nsusing.cc and stopping at line 17, we can ask GDB
> to print x and get a compiler-dependent answer. Using gcc 12.2.1, GDB
> will print M::x, and using clang 16.0.0 prints N::x. Not only is this
> behavior confusing to users, it is also not consistent with compiler
> behaviors, which would warn that using x is ambiguous at this point.
> 
> This commit makes GDB behavior consistent with compilers. it achieves
> this by making it so instead of exiting early when finding any symbol
> with the correct name, GDB continues searching through all include
> directives, storing all matching symbols in a relational map betwen the
> mangled name and the found symbols.
> 
> If the resulting map has more than one entry, GDB says that the
> reference is ambiguous and lists all possibilities. Otherwise it returns
> the map containing zero or one entries.
> 
> The commit also changes gdb.cp/nsusing.exp to test the ambiguous
> detection.
> ---
>  gdb/cp-namespace.c               | 69 +++++++++++++++++++++++---------
>  gdb/testsuite/gdb.cp/nsusing.exp | 13 +++++-
>  2 files changed, 61 insertions(+), 21 deletions(-)
> 
> diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c
> index 6ecb29fb1ac..df177b20d92 100644
> --- a/gdb/cp-namespace.c
> +++ b/gdb/cp-namespace.c
> @@ -32,6 +32,7 @@
>  #include "buildsym.h"
>  #include "language.h"
>  #include "namespace.h"
> +#include <map>
>  #include <string>
>  
>  static struct block_symbol
> @@ -372,7 +373,7 @@ cp_lookup_symbol_in_namespace (const char *the_namespace, const char *name,
>     SEARCH_SCOPE_FIRST is an internal implementation detail: Callers must
>     pass 0 for it.  Internally we pass 1 when recursing.  */

Should the function's description be updated to reflect the new return
type? i.e. describe a case where a map of more than 1 element is
returned.

>  
> -static struct block_symbol
> +static std::map<const char *, struct block_symbol>
>  cp_lookup_symbol_via_imports (const char *scope,
>  			      const char *name,
>  			      const struct block *block,
> @@ -386,13 +387,20 @@ cp_lookup_symbol_via_imports (const char *scope,
>    int len;
>    int directive_match;
>  
> +  /* All the symbols we found will be kept in this relational map between
> +     the mangled name and the block_symbol found.  We do this so that GDB
> +     won't incorrectly report an ambiguous symbol for finding the same
> +     thing twice.  */
> +  std::map<const char *,struct block_symbol> found_symbols;
> +
>    /* First, try to find the symbol in the given namespace if requested.  */
>    if (search_scope_first)
> -    sym = cp_lookup_symbol_in_namespace (scope, name,
> -					 block, domain, 1);
> -
> -  if (sym.symbol != NULL)
> -    return sym;
> +    {
> +      sym = cp_lookup_symbol_in_namespace (scope, name,
> +					   block, domain, 1);
> +      if (sym.symbol != nullptr)
> +	found_symbols[sym.symbol->m_name] = sym;
> +    }
>  
>    /* Due to a GCC bug, we need to know the boundaries of the current block
>       to know if a certain using directive is valid.  */
> @@ -446,7 +454,7 @@ cp_lookup_symbol_via_imports (const char *scope,
>  	  if (declaration_only || sym.symbol != NULL || current->declaration)
>  	    {
>  	      if (sym.symbol != NULL)
> -		return sym;
> +		  found_symbols[sym.symbol->m_name] = sym;

Looks like you have changed the indentation here with two more spaces.

>  
>  	      continue;
>  	    }
> @@ -467,23 +475,43 @@ cp_lookup_symbol_via_imports (const char *scope,
>  	      sym = cp_lookup_symbol_in_namespace (scope,
>  						   current->import_src,
>  						   block, domain, 1);
> +	      found_symbols[sym.symbol->m_name] = sym;
>  	    }
>  	  else if (current->alias == NULL)
>  	    {
>  	      /* If this import statement creates no alias, pass
>  		 current->inner as NAMESPACE to direct the search
>  		 towards the imported namespace.  */
> -	      sym = cp_lookup_symbol_via_imports (current->import_src,
> +	      std::map<const char *,struct block_symbol> sym_map =
> +		    cp_lookup_symbol_via_imports (current->import_src,
>  						  name, block,
>  						  domain, 1, 0, 0);

I think the usual coding style is to have the "=" after the line break.
This would become

    std::map<const char *,struct block_symbol> sym_map
      = cp_lookup_symbol_via_imports (…);

> +	      found_symbols.merge(sym_map);
>  	    }
>  
> -	  if (sym.symbol != NULL)
> -	    return sym;
>  	}
>      }
>  
> -  return {};
> +  if (found_symbols.size () > 1)
> +    {
> +      auto itr = found_symbols.begin();

Could it be `cbegin ()` ? (and you forgot a space before the parens).

> +      std::string error_str = "Reference to \"";
> +      error_str += name;
> +      error_str += "\" is ambiguous, possibilities are: ";
> +      error_str += itr->second.symbol->print_name ();
> +      for (itr++; itr != found_symbols.end (); itr++)
> +	{
> +	  error_str += " and ";
> +	  error_str += itr->second.symbol->print_name ();
> +	}
> +      error (_("%s"), error_str.c_str ());

I do not know if this something we care about, but your error message
will list the options in an unspecified order.  Iterating over the map
will follow the order of the keys, which are the addresses of the
mangled names of the symbols.

My instinct would prefer to list the options in lexicographical order
(of the demangled name).  This is probably not really important but it
could be annoying when writing test checking for the output message.

> +    }
> +  else
> +    return found_symbols;
> +
> +  /* This is needed to silence a -Werror=return-type warning, because
> +     the above if case doesn't have a return statement.  */
> +  gdb_assert_not_reached ();
>  }
>  
>  /* Helper function that searches an array of symbols for one named NAME.  */
> @@ -514,7 +542,6 @@ cp_lookup_symbol_imports_or_template (const char *scope,
>  				      const domain_enum domain)
>  {
>    struct symbol *function = block->function ();
> -  struct block_symbol result;
>  
>    if (symbol_lookup_debug)
>      {
> @@ -596,15 +623,19 @@ cp_lookup_symbol_imports_or_template (const char *scope,
>  	}
>      }
>  
> -  result = cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 1, 1);
> +  std::map<const char *,struct block_symbol> result =
> +      cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 1, 1);

Same remark for the "=" before the line break.

>    if (symbol_lookup_debug)
>      {
>        gdb_printf (gdb_stdlog,
>  		  "cp_lookup_symbol_imports_or_template (...) = %s\n",
> -		  result.symbol != NULL
> -		  ? host_address_to_string (result.symbol) : "NULL");
> +		  result.size() == 1
> +		  ? host_address_to_string (result[0].symbol) : "NULL");

If result is a std::map<const char *, T>, result[0] will look for the
key (const char *) 0, which is most probably in the map.  It therefore
creates a new instance of T using the default ctor, insert it in the map
for key 0 and returns a reference to it.

I think yoy want something like result.begin ().second.symbol or
something along those lines like you do…

>      }
> -  return result;
> +  if (result.empty ())
> +    return {};
> +  else
> +    return result.begin ()->second;

… here!

This however raises the question: if cp_lookup_symbol_via_imports can
return multiple block_symbol, shouldn't
cp_lookup_symbol_imports_or_template do the same when it basically
forwards what cp_lookup_symbol_via_imports returns?

I am not really familiar with this part of the code, so I do not know if
this can happen (but I guess it could).

I would say that:
- If the map can only contain one element and you only introduce it to
  hold the result of cp_lookup_symbol_via_imports, then you should
  assert that its size is 1 here (and maybe have a comment explaining
  why it must be a map of size 1).
- If it can be anything else, I guess you would need to return the
  entire map, or have a comment explaining why any member could do.

As the function's comment says "Like cp_lookup_symbol_via_imports, but
[…]" I am tempted to think that it should also return a map.

I hope those help.
All the best,
Lancelot.

>  }
>  
>  /* Search for NAME by applying relevant import statements belonging to BLOCK
> @@ -616,13 +647,13 @@ cp_lookup_symbol_via_all_imports (const char *scope, const char *name,
>  				  const struct block *block,
>  				  const domain_enum domain)
>  {
> -  struct block_symbol sym;
> +  std::map<const char *,struct block_symbol> sym;
>  
>    while (block != NULL)
>      {
>        sym = cp_lookup_symbol_via_imports (scope, name, block, domain, 0, 0, 1);
> -      if (sym.symbol)
> -	return sym;
> +      if (sym.size() == 1)
> +	return sym.begin ()->second;
>  
>        block = block->superblock ();
>      }
> diff --git a/gdb/testsuite/gdb.cp/nsusing.exp b/gdb/testsuite/gdb.cp/nsusing.exp
> index b79f3d26084..4ef0f48c507 100644
> --- a/gdb/testsuite/gdb.cp/nsusing.exp
> +++ b/gdb/testsuite/gdb.cp/nsusing.exp
> @@ -127,11 +127,20 @@ gdb_test_multiple "print x" "print x, before using statement" {
>      -re -wrap "No symbol .x. in current context.*" {
>  	pass $gdb_test_name
>      }
> -    -re -wrap "= 911.*" {
> +    -re -wrap "Reference to .x. is ambiguous.*" {
>  	# GCC doesn't properly set the decl_line for namespaces, so GDB believes
>  	# that the "using namespace M" line has already passed at this point.
>  	xfail $gdb_test_name
>      }
>  }
>  gdb_test "next" ".*" "using namespace M"
> -gdb_test "print x" "= 911" "print x, only using M"
> +gdb_test_multiple "print x" "print x, only using M" {
> +    -re -wrap "= 911.*" {
> +	pass $gdb_test_name
> +    }
> +    -re -wrap "Reference to .x. is ambiguous.*" {
> +	xfail $gdb_test_name
> +    }
> +}
> +gdb_test "next" ".*" "using namespace N"
> +gdb_test "print x"  "Reference to .x. is ambiguous.*" "print x, with M and N"
> -- 
> 2.38.1
> 

  reply	other threads:[~2022-11-16 17:49 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-16 14:13 [PATCH v2 0/2] Improve handling of using directives Bruno Larsen
2022-11-16 14:13 ` [PATCH v2 1/2] gdb/c++: validate 'using' directives based on the current line Bruno Larsen
2022-11-16 16:14   ` Lancelot SIX
2022-11-17  9:12     ` Bruno Larsen
2022-11-16 14:13 ` [PATCH v2 2/2] gdb/c++: Detect ambiguous variables in imported namespaces Bruno Larsen
2022-11-16 17:49   ` Lancelot SIX [this message]
2022-11-17  9:58     ` Bruno Larsen
2022-11-17 22:07       ` Lancelot SIX

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=20221116174913.hd3l6yyh5z6w7n7y@ubuntu.lan \
    --to=lsix@lancelotsix.com \
    --cc=blarsen@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=tom@tromey.com \
    /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).