Thread overview
Mingling string and identifier namespaces in nested extern(C++) decls
Sep 07, 2019
Max Samukha
Sep 07, 2019
Jonathan M Davis
Sep 07, 2019
Jonathan M Davis
September 07, 2019
extern(C++, "ns1") {
	extern(C++, ns2) {
		extern(C++, "ns3") {
			extern(C++, ns4) {
				void foo();
			}
		}
	}
}

pragma(msg, foo.mangleof); // _ZN3ns23ns43ns13ns33fooEv

That produces 'ns2::ns4::ns1::ns3::foo' path instead of the intuitively expected 'ns1::ns2::ns3::ns4::foo'. The identifier namespaces are grouped before the string ones. Bug or feature?
September 07, 2019
On Saturday, September 7, 2019 8:53:54 AM MDT Max Samukha via Digitalmars-d- learn wrote:
> extern(C++, "ns1") {
>   extern(C++, ns2) {
>       extern(C++, "ns3") {
>           extern(C++, ns4) {
>               void foo();
>           }
>       }
>   }
> }
>
> pragma(msg, foo.mangleof); // _ZN3ns23ns43ns13ns33fooEv
>
> That produces 'ns2::ns4::ns1::ns3::foo' path instead of the intuitively expected 'ns1::ns2::ns3::ns4::foo'. The identifier namespaces are grouped before the string ones. Bug or feature?

Given that the string version of extern(C++) is supposed to only affect mangling, whereas the other version does some other weird stuff, I'm not sure that you can really expect something sane if you try to mix them. That being said, unless the language disallows mixing them, the compiler should be doing something at least semi-sane. Either way, I don't see how having the result be anything other than ns1:ns2:ns3:ns4 is defensible. The type of weirdness that I would expect would resolve around the kind of issues that led to the string version being added in the first place (e.g. having to put the entire namespace in a single module). I'd suggest that you report it as a bug. It wouldn't surprise me if dmd doesn't even have any tests that try to mix the two types of extern(C++), since that really wasn't an intended use case. If anything, I expect that the hope was that the non-string version would eventually be deprecated, though I wouldn't bet on that actually happening.

- Jonathan M Davis



September 07, 2019
On Saturday, September 7, 2019 2:18:40 PM MDT Jonathan M Davis via Digitalmars-d-learn wrote:
> On Saturday, September 7, 2019 8:53:54 AM MDT Max Samukha via
> Digitalmars-d-
> learn wrote:
> > extern(C++, "ns1") {
> >
> >   extern(C++, ns2) {
> >
> >       extern(C++, "ns3") {
> >
> >           extern(C++, ns4) {
> >
> >               void foo();
> >
> >           }
> >
> >       }
> >
> >   }
> >
> > }
> >
> > pragma(msg, foo.mangleof); // _ZN3ns23ns43ns13ns33fooEv
> >
> > That produces 'ns2::ns4::ns1::ns3::foo' path instead of the intuitively expected 'ns1::ns2::ns3::ns4::foo'. The identifier namespaces are grouped before the string ones. Bug or feature?
>
> Given that the string version of extern(C++) is supposed to only affect mangling, whereas the other version does some other weird stuff, I'm not sure that you can really expect something sane if you try to mix them. That being said, unless the language disallows mixing them, the compiler should be doing something at least semi-sane. Either way, I don't see how having the result be anything other than ns1:ns2:ns3:ns4 is defensible. The type of weirdness that I would expect would resolve around the kind of issues that led to the string version being added in the first place (e.g. having to put the entire namespace in a single module). I'd suggest that you report it as a bug. It wouldn't surprise me if dmd doesn't even have any tests that try to mix the two types of extern(C++), since that really wasn't an intended use case. If anything, I expect that the hope was that the non-string version would eventually be deprecated, though I wouldn't bet on that actually happening.

Actually, thinking on this further, I would have thought that only the extern(C++, ns4) would be applied. No other D attributes have any kind of nesting. Applying incompatible attributes either results in one overriding the other or in an error (usually, one overrides the other when attributes are mass-applied, whereas you get an error if you put them directly on the symbol).

If we allow nesting like this with extern(C++), then I would expect that the
nesting would work in the same order as you'd get in C++, which would be
ns1:ns2:ns3:ns4 in this case, but I would have expected that you'd have to
apply the entire namespace in one go rather than have multiple extern(C++)
declarations - e.g. extern(C++, "ns1:ns2:ns3:ns4") or
extern(C++, ns1, ns2, ns3, ns4) for the non-string version IIRC (I've
avoided the non-string extern(C++) like the plague though, since it makes no
sense, and fortunately, most of the C++ that I've interacted with from D has
not had namespaces).

In any case, I would consider the current behavior to be nonsensical, and I have a hard time believing that it's on purpose. With that example, I would expect that you'd either get only ns4 applied or that you'd get ns1:ns2:ns3:ns4. Getting only ns4 would be consistent with the rest of D, whereas ns1:ns2:ns3:ns4 would be more consistent with what someone would expect if they were coming from C++ and translating the C++ declarations to D declarations directly.

- Jonathan M Davis