April 04, 2019
On Thu, Apr 4, 2019 at 4:15 AM Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 4/4/19 4:33 AM, Manu wrote:
> > On Tue, Apr 2, 2019 at 4:10 AM Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> >>
> >> On 4/2/19 4:11 AM, Walter Bright wrote:
> >>> On 4/1/2019 5:55 PM, Atila Neves wrote:
> >>>> Factually incorrect.
> >>>
> >>> I suspect Rubn has confused the syntactical conflation of arrays with pointers in C, and the implicit coercion of arrays to pointers, as them being the same. They simply aren't the same, as they are semantically separated by one level of indirection.
> >>
> >> You forgot to mention that top-level "const" is ignored in function parameters (for the signature and mangling but not the implementation).
> >
> > Actually, it's sadly not ignored, and MSVC mangles it!
>
> I'd be surprised. It would be a gross bug. It didn't in 2004 when I still was using it. Herb advocates using top-level const in implementation, pointing out that it's ignored in the signature.

Herb appears to be wrong.
And this is his native STL, he should know ;)

> > It's been a
> > massive PITA when writing some of the STL bindings in druntime!
> > I've had to manually build mangled function names to extern, because I
> > can't express it in D >_<
> >
> > https://github.com/dlang/druntime/blob/master/src/core/stdcpp/allocator.d#L50
> >   <- you're loving it!
>
> Could that be a "pointer to const"? That's not top-level.

Straight from the STL: xmemory0:989

template<class _Ty>
class allocator
{ // generic allocator for objects of class _Ty
public:
  ...
  void deallocate(_Ty * const _Ptr, const size_t _Count)
  { // deallocate object at _Ptr
    // no overflow check on the following multiply; we assume
_Allocate did that check
    _Deallocate<_New_alignof<_Ty>>(_Ptr, sizeof(_Ty) * _Count);
  }
}

I spent hours trying to work out why the mangling was failing before I
noticed the const was in the wrong place in the C++ code. Only
solution is the code I linked above.
Also, FWIW, I think this is a fairly recent change (ie, last 5 years).
April 04, 2019
On 4/4/19 1:20 PM, Manu wrote:
> Also, FWIW, I think this is a fairly recent change (ie, last 5 years).

Good to know, thanks! Found this, too:

https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512

I'll email Herb.
April 06, 2019
On Thursday, 4 April 2019 at 17:25:05 UTC, Andrei Alexandrescu wrote:
> On 4/4/19 1:20 PM, Manu wrote:
>> Also, FWIW, I think this is a fairly recent change (ie, last 5 years).
>
> Good to know, thanks! Found this, too:
>
> https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512
>
> I'll email Herb.

Andrei sent me mail and I did a little digging. Much of the following is courtesy Jonathan Caves (thanks Jon!).

Pasting what I sent to Andrei with light edits:


Short answer: Yes, but most C++ code won't (and doesn't) notice because to see the different mangling you have to declare the function differently in two translation units, *and* have different "first" declarations in those TUs. So this is invisible for any function you declare in a shared header (because everyone sees the same first declaration). And it's invisible when you declare without top-level const and then define with top-level const on a parameter (because everyone uses the first declaration).


Longer answer: Yes, this is a long standing known issue – our name decorator was developed long before C++98 codified a lot of stuff.

Note that there are two mangling issues involved that come up in your example (or minor variations of it): [dcl.fct]/5 says that function parameters ignore top-level const and treat [] as *:

> After determining the type of each parameter, any parameter of type
> “array of T” or of function type T is adjusted to be “pointer to T”.
> After producing the list of parameter types, any top-level cv-qualifiers
> modifying a parameter type are deleted when forming the function type.

So these are supposed to be equivalent redeclarations of f and g:

~~~
// Case 1
void f(int);
void f(int const); // same, redeclaration

// Case 2
void g(int*);
void g(int[]); // same, redeclaration
~~~

And in VC++ they are correctly equivalent, in the language. However, for name mangling, VC++ selects mangling based on the first declaration it seems, and it does mangle each of these pairs differently, so if two TUs have different first declarations of each of f and g, they can’t link.

Again, to make this actually visible to users, they have to see different *first* declarations. For starters that means they’re not using a shared header.

I understand that someone you know found it caused trouble with linking foreign code with C++, though presumably that’s because they had to write their own declaration by hand (in C++, or in the other language)? I’ve mostly seen that sort of thing when people have to write their own declarations, and in those cases they also had general fragility (e.g., writing int vs long, that sort of thing – because they have to be careful to get the declarations exactly right because they’re writing them manually, same trouble that Java JNI and .NET P/Invoke have in general, you have to make the parameters perfect).

The worry about ‘fixing’ the name decorator is that it would be an ABI breaking change and so we have always be reticent in making any changes. That said, if we had customer examples that this was causing serious problems, we’d consider it. If you have production examples you could share or link to that would be helpful. Mostly, we don’t hear about this and I couldn’t find a report of it in our current online bug tracking systems (but I may have missed it) – most people are like you and rarely notice it even though it’s been like this for ~25 years. :) Because you have to be writing code in a fairly special way, effectively writing somebody else's function declaration by hand instead of using the .h they provided, and write it differently than in that .h, to be able to notice.
April 06, 2019
On Sat, Apr 6, 2019 at 8:30 AM Herb Sutter via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On Thursday, 4 April 2019 at 17:25:05 UTC, Andrei Alexandrescu wrote:
> > On 4/4/19 1:20 PM, Manu wrote:
> >> Also, FWIW, I think this is a fairly recent change (ie, last 5
> >> years).
> >
> > Good to know, thanks! Found this, too:
> >
> > https://stackoverflow.com/questions/32447978/odd-vs-name-mangling-behavior/32448512
> >
> > I'll email Herb.
>
> Andrei sent me mail and I did a little digging. Much of the following is courtesy Jonathan Caves (thanks Jon!).
>
> Pasting what I sent to Andrei with light edits:
>
>
> Short answer: Yes, but most C++ code won't (and doesn't) notice because to see the different mangling you have to declare the function differently in two translation units, *and* have different "first" declarations in those TUs. So this is invisible for any function you declare in a shared header (because everyone sees the same first declaration). And it's invisible when you declare without top-level const and then define with top-level const on a parameter (because everyone uses the first declaration).
>
>
> Longer answer: Yes, this is a long standing known issue – our name decorator was developed long before C++98 codified a lot of stuff.
>
> Note that there are two mangling issues involved that come up in your example (or minor variations of it): [dcl.fct]/5 says that function parameters ignore top-level const and treat [] as *:
>
> > After determining the type of each parameter, any parameter of
> > type
> > “array of T” or of function type T is adjusted to be “pointer
> > to T”.
> > After producing the list of parameter types, any top-level
> > cv-qualifiers
> > modifying a parameter type are deleted when forming the
> > function type.
>
> So these are supposed to be equivalent redeclarations of f and g:
>
> ~~~
> // Case 1
> void f(int);
> void f(int const); // same, redeclaration
>
> // Case 2
> void g(int*);
> void g(int[]); // same, redeclaration
> ~~~
>
> And in VC++ they are correctly equivalent, in the language. However, for name mangling, VC++ selects mangling based on the first declaration it seems, and it does mangle each of these pairs differently, so if two TUs have different first declarations of each of f and g, they can’t link.
>
> Again, to make this actually visible to users, they have to see different *first* declarations. For starters that means they’re not using a shared header.
>
> I understand that someone you know found it caused trouble with
> linking foreign code with C++, though presumably that’s because
> they had to write their own declaration by hand (in C++, or in
> the other language)? I’ve mostly seen that sort of thing when
> people have to write their own declarations, and in those cases
> they also had general fragility (e.g., writing int vs long, that
> sort of thing – because they have to be careful to get the
> declarations exactly right because they’re writing them manually,
> same trouble that Java JNI and .NET P/Invoke have in general, you
> have to make the parameters perfect).
>
> The worry about ‘fixing’ the name decorator is that it would be
> an ABI breaking change and so we have always be reticent in
> making any changes. That said, if we had customer examples that
> this was causing serious problems, we’d consider it. If you have
> production examples you could share or link to that would be
> helpful. Mostly, we don’t hear about this and I couldn’t find a
> report of it in our current online bug tracking systems (but I
> may have missed it) – most people are like you and rarely notice
> it even though it’s been like this for ~25 years. :) Because you
> have to be writing code in a fairly special way, effectively
> writing somebody else's function declaration by hand instead of
> using the .h they provided, and write it differently than in that
> .h, to be able to notice.

Thanks for the background.

For my effort (binding to STL), a bigger problem with MSVC
compatibility isn't that this mangling is weird (I have an awkward
solution in this case), it's that the MS C++ runtime ABI has
historically been somewhat volatile.
The significant ABI-breaking change was when allocator<T>::destroy()
was changed from `T*` to `T* const`, which appears to have happened
perhaps vc2013->2015? (I don't have those old headers on my system
anymore to check). There are numerous other cases of ABI breakage, but
this one happened to introduce `* const`, which I couldn't express.
Fortunately, it seems VS2015 and onward has made greater efforts
towards ABI stability, so I plan to simply deprecate distant
backwards-compatibility, and hope for a continued commitment from your
side ;)

For future reference, if you're making this decision about your ABI
when authoring the runtime; for foreign languages trying to link to
C++, I expect `T*` is generally much simpler. I suspect many FFI's
would have trouble with `* const`, and D is one such case.
We cross-language linker-er's would certainly appreciate trying to
avoid `* const` in public ABI's since MSVC has this odd mangling rule.

1 2 3 4 5 6 7
Next ›   Last »