On Mon, Jul 03, 2017 at 01:32:46PM -0400, David Malcolm wrote:
> On Mon, 2017-07-03 at 10:25 -0600, Jeff Law wrote:
> > On 05/05/2017 11:51 AM, David Malcolm wrote:
> > > In various places we use lookup_name_fuzzy to provide a hint,
> > > and can report messages of the form:
> > >   error: unknown foo named 'bar'
> > > or:
> > >   error: unknown foo named 'bar'; did you mean 'SUGGESTION?
> > > 
> > > This patch provides a way for lookup_name_fuzzy to provide
> > > both the suggestion above, and (optionally) additional hints
> > > that can be printed e.g.
> > > 
> > >   note: did you forget to include <SOME_HEADER.h>?
> > > 
> > > This patch provides the mechanism and ports existing users
> > > of lookup_name_fuzzy to the new return type.
> > > There are no uses of such hints in this patch, but followup
> > > patches provide various front-end specific uses of this.
> > > 
> > > gcc/c-family/ChangeLog:
> > >   * c-common.h (class deferred_diagnostic): New class.
> > >   (class name_hint): New class.
> > >   (lookup_name_fuzzy): Convert return type from const char *
> > >   to name_hint.  Add location_t param.
> > > 
> > > gcc/c/ChangeLog:
> > >   * c-decl.c (implicit_decl_warning): Convert "hint" from
> > >   const char * to name_hint.  Pass location to
> > >   lookup_name_fuzzy.  Suppress any deferred diagnostic if the
> > >   warning was not printed.
> > >   (undeclared_variable): Likewise for "guessed_id".
> > >   (lookup_name_fuzzy): Convert return type from const char *
> > >   to name_hint.  Add location_t param.
> > >   * c-parser.c (c_parser_declaration_or_fndef): Convert "hint"
> > > from
> > >   const char * to name_hint.  Pass location to lookup_name_fuzzy.
> > >   (c_parser_parameter_declaration): Pass location to
> > >   lookup_name_fuzzy.
> > > 
> > > gcc/cp/ChangeLog:
> > >   * name-lookup.c (suggest_alternatives_for): Convert
> > > "fuzzy_name" from
> > >   const char * to name_hint, and rename to "hint".  Pass location
> > > to
> > >   lookup_name_fuzzy.
> > >   (lookup_name_fuzzy): Convert return type from const char *
> > >   to name_hint.  Add location_t param.
> > >   * parser.c (cp_parser_diagnose_invalid_type_name): Convert
> > >   "suggestion" from const char * to name_hint, and rename to
> > > "hint".
> > >   Pass location to lookup_name_fuzzy.
> > 
> > > ---
> > >  gcc/c-family/c-common.h | 121
> > > +++++++++++++++++++++++++++++++++++++++++++++++-
> > >  gcc/c/c-decl.c          |  35 +++++++-------
> > >  gcc/c/c-parser.c        |  16 ++++---
> > >  gcc/cp/name-lookup.c    |  17 +++----
> > >  gcc/cp/parser.c         |  12 ++---
> > >  5 files changed, 163 insertions(+), 38 deletions(-)
> > > 
> > > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> > > index 138a0a6..83c1a68 100644
> > > --- a/gcc/c-family/c-common.h
> > > +++ b/gcc/c-family/c-common.h
> > > @@ -1009,7 +1009,126 @@ enum lookup_name_fuzzy_kind {
> > >    /* Any name.  */
> > >    FUZZY_LOOKUP_NAME
> > >  };
> > > -extern const char *lookup_name_fuzzy (tree, enum
> > > lookup_name_fuzzy_kind);
> > > +
> > > +/* A deferred_diagnostic is a wrapper around optional extra
> > > diagnostics
> > > +   that we may want to bundle into a name_hint.
> > > +
> > > +   The emit method is called when no name_hint instances reference
> > > +   the deferred_diagnostic.  In the simple case this is when the
> > > name_hint
> > > +   goes out of scope, but a reference-counting scheme is used to
> > > allow
> > > +   name_hint instances to be copied.  */
> > > +
> > > +class deferred_diagnostic
> > > +{
> > > + public:
> > > +  virtual ~deferred_diagnostic () {}
> > > +  virtual void emit () = 0;
> > > +
> > > +  void incref () { m_refcnt++; }
> > > +  void decref ()
> > > +  {
> > > +    if (--m_refcnt == 0)
> > > +      {
> > > + if (!m_suppress)
> > > +   emit ();
> > > + delete this;
> > > +      }
> > > +  }
> > > +
> > > +  location_t get_location () const { return m_loc; }
> > > +
> > > +  /* Call this if the corresponding warning was not emitted,
> > > +     in which case we should also not emit the
> > > deferred_diagnostic.  */
> > > +  void suppress ()
> > > +  {
> > > +    m_suppress = true;
> > > +  }
> > > +
> > > + protected:
> > > +  deferred_diagnostic (location_t loc)
> > > +  : m_refcnt (0), m_loc (loc), m_suppress (false) {}
> > > +
> > > + private:
> > > +  int m_refcnt;
> > > +  location_t m_loc;
> > > +  bool m_suppress;
> > > +};
> > So what stands out here is "delete this" and the need for explicit
> > reference counting.  Also doesn't that imply that deferred_diagnostic
> > objects must be allocated on the heap?  Is there another way to get
> > the
> > behavior you want without resorting to something like this?
> > 
> 
> Thanks for looking at this.
> 
> Yes: deferred_diagnostic instances are heap-allocated.  This is because
> it's an abstract base class; each concrete subclass is an
> implementation detail within the frontends, for isolating the special
> -case logic for each different kind of hint, and thus these concrete
> subclasses are hidden within the FE code.
> 
> My initial implementation of the above had the name_hint class directly
> "own" the deferred_diagnostic ptr, with a:
>   delete m_deferred;
> within name_hint's dtor.
> 
> This worked OK, until I encountered places in the C and C++ frontends
> where the natural thing (to avoid complicating the control flow) was to
> have a default-constructed name_hint as we enter the error-handling,
> and then optionally copy over it with a name_hint instance returned
> from a call to lookup_name_fuzzy on some of the paths (specifically,
> this was in c/c-decl.c's  implicit_decl_warning and in and
>  cp/parser.c's cp_parser_diagnose_invalid_type_name).
> 
> AIUI, the idiomatic way to do this assignment of an "owned" ptr in
> modern C++ would be to use std::unique_ptr (if I understand things
> right, this is move-asssignment), the issue being that when we return a
> name_hint:

That would be one way, though you can avoid allocating on the heap by
doing something like this.

std::option<name_hint> hint;
if (something)
  foo (&something);

    void
    foo(std::option<name_hint> *hint)
    {
      hint->emplace (name_hint (5));
      }

>   name_hint hint;
>   if (sometimes)
>     hint = lookup_name_fuzzy (...);
>   // potentially use hint
>   // hint's dtor runs, showing any deferred diagnostic it "owns"
> 
> we have two name_hint instances: the return value, and the local
> "hint", and the return value's dtor runs immediately after the local is
> assigned to, but before we use the deferred diagnostic.
> 
> I believe that the above with a std::unique_ptr within class name_hint
> requires the "move semantics" of C+11 in order to be guaranteed to work
> correctly, which is beyond what we currently require (C++98 iirc).

Well, you can use something like std::auto_ptr that makes this "work"
without move assignment by having a copy constructor that modifies the
original object.

I have patches to add something like that that I need to get to posting,
but have been delayed by moving and such things.

> Hence I coded up something akin to std::shared_ptr "by hand" here (and
> yes, it is perhaps overkill).

fwiw when its necessary to refcount things I do prefer "intrusive"
refcounting like this to the way shared_ptr does it.

> > Or is your argument that deferred_diagnostic is only used from within
> > class name_hint and thus the concerns around heap vs stack, explicit
> > counting, etc are all buried inside the name_hint class?  If so, is
> > there any reasonable way to restrict the use of deferred_disagnostic
> > to
> > within the name_hint class?
> 
> I guess I could try moving the scope of the name deferred_diagnostic to
> be within class name_hint.
> 
> Other approaches to simplify the code:
> 
> (a) GC-allocate the deferred_diagnostic, and let it be collected next
> time the GC collects (as nothing would ever have a root to it).

I'd prefer the current approach to this the refcounting code is ugly,
though perhaps we'll need a generic refcounting framework some day.
However I think its easier to reason about ownership with refcounting
than gc, and hopefully make it easier to switch to something like
unique_ptr when I finish those patches.

> (b) convert the deferred_diagnostic subclasses to be singletons, and
> then return a ptr to the singleton (and clobber them each time we need
> a supposedly "new" one).

This also seems to really complicate ownership which is more of a global
problem than the refcounting?

> Alternatively, we could hide the copy-assignment operator for
> name_hint, and uglify/complicate the control flow in the two sites that
> need it.
> 
> Any other ideas?

I think my highest preference would be the std::option thing, though I'm
not sure we have code for that yet.  Then would be the unique_ptr using
auto_ptr approach I'm working on, and then what you have here.  With the
assumption we'll switch it to one of the first two when possible.

thanks

Trev

Reply via email to