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

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Mon May 23 13:13:47 PDT 2016


On Sat, May 21, 2016 at 11:48 PM, Evan Driscoll via cfe-dev <
cfe-dev at lists.llvm.org> 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
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20160523/9aa65295/attachment.html>


More information about the cfe-dev mailing list