[Issue 16479] Missing substitution while mangling C++ template parameter for functions

d-bugmail at puremagic.com d-bugmail at puremagic.com
Tue Jun 12 10:30:18 UTC 2018


https://issues.dlang.org/show_bug.cgi?id=16479

--- Comment #4 from Mathias LANG <pro.mathias.lang at gmail.com> ---
This is actually quite a non-trivial problem. Take the following code in C++:
```
#include <array>

template<size_t S, class T>
std::array<T, S>* getArray(const T* data)
{
    auto ret = new std::array<T, S>;
    for (size_t idx = 0; idx < S; ++idx)
        (*ret)[idx] = data[idx];
    return ret;
}

void unused ()
{
    getArray<5, bool>(nullptr);
    getArray<3, int>(nullptr);
    getArray<5, char>(nullptr);
}
```

This gives the following symbols on OSX:
```
0000000000000000 T __Z6unusedv
00000000000000c0 T __Z8getArrayILm3EiEPNSt3__15arrayIT0_XT_EEEPKS2_
0000000000000040 T __Z8getArrayILm5EbEPNSt3__15arrayIT0_XT_EEEPKS2_
0000000000000140 T __Z8getArrayILm5EcEPNSt3__15arrayIT0_XT_EEEPKS2_
                 U __Znwm
```
I mentioned OSX because on Linux, the inlined namespace `__1` might not be
present, thus the symbol (and substitutions) will differ, but the bug is still
there on Linux.

The equivalent D code is as follow:
```
extern(C++, std)
extern (C++, __1) {
    public struct array (T, /*size_t*/ cpp_ulong N)
    {
        private T[N > 0 ? N : 1] __elems_;
    }
}

extern (C++)
array!(T, S)* getArray (cpp_ulong S, T) (const(T)* data);


void main ()
{
    const d1 = [true, false, true, false, true];
    const d2 = [42, 84, 1992];
    const d3 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

    getArray!5(d1.ptr);
    getArray!3(d2.ptr);
    getArray!6(d3.ptr); // Not 7 on purpose
}
```

This will produce the following symbols with DMD master:
```
nm types.o | grep getArray
     U __Z8getArrayILm3EiEPNSt3__15arrayIiLm3EEEPKi
     U __Z8getArrayILm5EbEPNSt3__15arrayIbLm5EEEPKb
     U __Z8getArrayILm6EcEPNSt3__15arrayIcLm6EEEPKc
```


There are 2 issues here:
- We don't do template parameter substituion, so we end up with the string
"[...]arrayI{i,b,c}Lm{3,5,6}E" to represent `array<{int,bool,char}, {3,4,6}>`
instead of `arrayIT0_XT_E` (using substitution for `getArray`'s template
parameter.
- We don't do substitution for the function parameter. clang++ will use `S2_`
as substitution and g++ `S1_` (because there's no inline namespace) instead of
`{i,b,c}`. It's surprising because substitution does not normally happen for
basic types, but I suppose template parameters are special.

Note that this is non-trivial to solve because of the following case:
```
template <int A, int B>
struct Bar
{
};

template <int A, int B>
Bar<B,A> foo ()
{
    return Bar<B, A>{};
}

void unused ()
{
    foo<1, 2>();
    foo<1, 1>();
}
```

This generates the following symbols:
```
0000000000000030 T __Z3fooILi1ELi1EE3BarIXT0_EXT_EEv
0000000000000020 T __Z3fooILi1ELi2EE3BarIXT0_EXT_EEv
0000000000000000 T __Z6unusedv
```

Which means we cannot solely rely on the value of the template parameters, we
have to track which one is used where, but I don't think we have this
information in the frontend at the moment...

--


More information about the Digitalmars-d-bugs mailing list