[cfe-dev] Weird issues (bug(s)?) with friend name injection and extern C and ambiguity

Evan Driscoll via cfe-dev cfe-dev at lists.llvm.org
Sat May 21 23:48:43 PDT 2016


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



More information about the cfe-dev mailing list