Weird issues (bug(s)?) with friend name injection and extern C and ambiguity

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Weird issues (bug(s)?) with friend name injection and extern C and ambiguity

Vassil Vassilev via cfe-dev
I apologize if this is the wrong venue for this, and apologize for the
wall of text. :-) I thought about filing a bug, except I realized I
don't really know what to report; there are two different things I am
not actually familiar enough with to understand whether or not this
code is actually legal or how it's legal. GCC, Clang++, and MSVC have
three different interpretations of this and a slight modification of
it. I was originally going to report this issue as just a "Clang's
error message was extremely unhelpful", but I'm not convinced there
isn't more going on than just that.

Without further ado, here's the code:

  namespace namespace_with_injected_name {
    class Boo {
      friend struct ExternCStruct;
    };
  }

  using namespace namespace_with_injected_name;

  extern "C" {
    struct ExternCStruct;
    struct ExternCStruct; // yes, a second one (!)
  }

  ExternCStruct * p;


GCC accepts this code without question, at least on 4.8 and 4.9

MSVC claims the final declaration of p is an error because
"ExternCStruct" is ambiguous and gives a good explanation as to why:

  1>... error C2872: 'ExternCStruct' : ambiguous symbol
  1>      could be '...(10) : ExternCStruct'
  1>      or       '...(3) : namespace_with_injected_name::ExternCStruct'


Clang also fails, thinking it's ambiguous, but its error is not helpful:

  14 : error: reference to 'ExternCStruct' is ambiguous
  ExternCStruct * p;

  11 : note: candidate found by name lookup is 'ExternCStruct'
  struct ExternCStruct;

  10 : note: candidate found by name lookup is 'ExternCStruct'
  struct ExternCStruct;

so it "helpfully" pointed out I have two declarations in global scope,
but it does *not* indicate anything about Boo's friend, which seems
like the root cause of the error. I didn't even *know* that was the
problem until I minimized it to send it to you folks to report the
unhelpful error; unless I lost something during the translation,
apparently a coworker fixed it by accident, because we thought it was
a different issue.

(The above error output is based on https://gcc.godbolt.org/ and
claims to be 3.8; I was using an older version that didn't produce the
two notes.)


But it gets weirder.

Not only does Clang report the two extern C declarations in the
warning, but it actually *needs* both of them to give an error at all:
if you remove one, it compiles! At least according to the online
implementation.


Furthermore, I just noticed that the version above actually *does* on
my older Clang 3.6. Here's a slightly more complex version that
complies with neither version:

  namespace namespace_with_injected_name {
    class Boo {
      friend struct ExternCStruct;
    };
  }

  using namespace namespace_with_injected_name;

  extern "C" {
    struct ExternCStruct;
  }

  void f(ExternCStruct * state);

  extern "C" {
    struct ExternCStruct;

    void g(ExternCStruct *ps) {
      ::f(ps);
  }

(The :: actually improves the error quality a hair I think, but isn't
necessary to seeing what is happening.) This one is actually truer to
the error we were hitting.


Anyway, I'd be happy to file a bug if that's what would be preferred
and it looks like one to you all, provided that someone can inform me
as to what that bug might be so I don't have to call it "weird funky
thing with friend name injection or maybe extern C structs or
something". ;-)

Evan
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Weird issues (bug(s)?) with friend name injection and extern C and ambiguity

Vassil Vassilev via cfe-dev
On Sat, May 21, 2016 at 11:48 PM, Evan Driscoll via cfe-dev <[hidden email]> wrote:
I apologize if this is the wrong venue for this, and apologize for the
wall of text. :-) I thought about filing a bug, except I realized I
don't really know what to report; there are two different things I am
not actually familiar enough with to understand whether or not this
code is actually legal or how it's legal. GCC, Clang++, and MSVC have
three different interpretations of this and a slight modification of
it. I was originally going to report this issue as just a "Clang's
error message was extremely unhelpful", but I'm not convinced there
isn't more going on than just that.

Without further ado, here's the code:

  namespace namespace_with_injected_name {
    class Boo {
      friend struct ExternCStruct;
    };
  }

  using namespace namespace_with_injected_name;

  extern "C" {
    struct ExternCStruct;
    struct ExternCStruct; // yes, a second one (!)
  }

  ExternCStruct * p;


GCC accepts this code without question, at least on 4.8 and 4.9

GCC is right. There's only one visible entity named ExternCStruct.
 
MSVC claims the final declaration of p is an error because
"ExternCStruct" is ambiguous and gives a good explanation as to why:

  1>... error C2872: 'ExternCStruct' : ambiguous symbol
  1>      could be '...(10) : ExternCStruct'
  1>      or       '...(3) : namespace_with_injected_name::ExternCStruct'

MSVC incorrectly makes the names of friends visible in the enclosing context.
 
Clang also fails, thinking it's ambiguous, but its error is not helpful:

  14 : error: reference to 'ExternCStruct' is ambiguous
  ExternCStruct * p;

  11 : note: candidate found by name lookup is 'ExternCStruct'
  struct ExternCStruct;

  10 : note: candidate found by name lookup is 'ExternCStruct'
  struct ExternCStruct;

so it "helpfully" pointed out I have two declarations in global scope,
but it does *not* indicate anything about Boo's friend, which seems
like the root cause of the error. I didn't even *know* that was the
problem until I minimized it to send it to you folks to report the
unhelpful error; unless I lost something during the translation,
apparently a coworker fixed it by accident, because we thought it was
a different issue.

Fixed in r270482. Our redeclaration lookup for the second ExternCStruct in the extern "C" finds both the previous declaration and the friend declaration, notices the problem, then tries to filter out the friend declaration. However, it got confused by the extern "C" and filtered out both declarations and decided that it was declaring a brand-new ExternCStruct. We had a similar failure for this testcase (also fixed in r270482):

  namespace N {
    struct X *p;
    namespace {
      class K {
        friend struct X;
      };
    }
  }
  namespace N {
    struct X;
    X *q = p;
  }

In this case, clang got confused during the filtering because the two declarations of 'struct X' came from different declarations of the same namespace.
 
(The above error output is based on https://gcc.godbolt.org/ and
claims to be 3.8; I was using an older version that didn't produce the
two notes.)


But it gets weirder.

Not only does Clang report the two extern C declarations in the
warning, but it actually *needs* both of them to give an error at all:
if you remove one, it compiles! At least according to the online
implementation.


Furthermore, I just noticed that the version above actually *does* on
my older Clang 3.6. Here's a slightly more complex version that
complies with neither version:

  namespace namespace_with_injected_name {
    class Boo {
      friend struct ExternCStruct;
    };
  }

  using namespace namespace_with_injected_name;

  extern "C" {
    struct ExternCStruct;
  }

  void f(ExternCStruct * state);

  extern "C" {
    struct ExternCStruct;

    void g(ExternCStruct *ps) {
      ::f(ps);
  }

(The :: actually improves the error quality a hair I think, but isn't
necessary to seeing what is happening.) This one is actually truer to
the error we were hitting.


Anyway, I'd be happy to file a bug if that's what would be preferred
and it looks like one to you all, provided that someone can inform me
as to what that bug might be so I don't have to call it "weird funky
thing with friend name injection or maybe extern C structs or
something". ;-)

Evan
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev