c++solarisname-manglingsolaris-studio

Is this a mangling bug in Solaris Studio?


The source (at the end of the question) will provoke what I believe is a mangling error on Solaris Studio (and not on other compilers).

The error message was reformatted with new lines for clarity:

"overload.cpp", line 44: Error:
runGenEntries<std::vector<int>>(const GenEntryRuleDriven<int>&, const std::vector<int>&)
and
runGenEntries<std::vector<int>>(const GenEntryRulesDriven<int>&, const std::vector<int>&)
have same extern name
"__1cNrunGenEntries4nDstdGvector4Cin0AJallocator4Ci_____6FrkTArk1_v_".
1 Error(s) detected.

Note how the two functions runGenEntries's first parameter differ only by one character (the 's' at the end of Rule)

This seems to happen when the first parameter is of type:

const typename GenEntryRulesDrivenType<typename InputsType::value_type>::type

And doesn't happen when the first parameter is instead of type:

const typename GenEntryRulesDriven<typename InputsType::value_type>

Which resolves to the same type in the end!

Is this a consequence of some obscure C++ rule implemented only on Solaris? Or is this a Solaris Studio bug when it mangles symbols?

Complete source

The following source is compilable as is on any compiler.

The define will either activate the code that provokes the error, or activate code which would be supposed to produce the same result (but this time, without bug):

#include <iostream>
#include <vector>

template<typename T>
struct GenEntryRulesDriven
{
   void foo() const
   {
   }
};

template<typename T>
struct GenEntryRuleDriven
{
   void bar() const
   {
   }
   std::string toto; // to have a different size than GenEntryRulesDriven
};


template <typename T>
struct GenEntryRulesDrivenType
{
   typedef GenEntryRulesDriven<T> type;
};

template <typename T>
struct GenEntryRuleDrivenType
{
   typedef GenEntryRuleDriven<T> type;
};

#if 1 // Gives an error

template <typename InputsType>
void runGenEntries(const typename GenEntryRulesDrivenType<
                          typename InputsType::value_type>::type &genEntry,
                          const InputsType& inputs)
{
   genEntry.foo();
}

template <typename InputsType>
void runGenEntries(const typename GenEntryRuleDrivenType<
                          typename InputsType::value_type>::type &genEntry,
                          const InputsType& inputs)
{
   genEntry.bar();
}

#else // No error but same types as above!

template <typename InputsType>
void runGenEntries(const typename GenEntryRulesDriven<
                          typename InputsType::value_type> &genEntry,
                          const InputsType& inputs)
{
   genEntry.foo();
}

template <typename InputsType>
void runGenEntries(const typename GenEntryRuleDriven<
                          typename InputsType::value_type> &genEntry,
                          const InputsType& inputs)
{
   genEntry.bar();
}

#endif

int
main()
{
   std::vector<int> v;
   GenEntryRulesDriven<int> rulesDriven;
   runGenEntries(rulesDriven, v);

   GenEntryRuleDriven<int> ruleDriven;
   runGenEntries(ruleDriven, v);

   return 0;
}

This code was compiled on the following plaform:

bash$ uname -a
SunOS pegasus 5.10 Generic_118855-33 i86pc i386 i86pc
bash$ CC -V
CC: Sun C++ 5.10 SunOS_i386 128229-07 2010/03/24

Solution

  • Short answer

    Short answer: this appears to be "unfixable" bug 6532605 (I see it referenced on numerous google searches but I cannot open the bug itself on https://support.oracle.com/rs?type=bug&id=6532605).

    A workaround done by Qt developers is to put the method definition in a separate compilation unit (.cpp file).

    Background

    If you demangle the symbol name that CC is complaining about, you can see that it is compiling a const __type_0& argument as the first argument:

    $ echo __1cNrunGenEntries4nDstdGvector4Cin0AJallocator4Ci_____6FrkTArk1_v_ | c++filt
    void runGenEntries<std::vector<int> >(const __type_0&,const __type_0&)
    $
    

    With g++ 3.4.6 on the exact same Solaris 10 box, the symbols from block 1 demangle as:

    runGenEntries<std, vector<int, std::allocator,<int>void> >(const GenEntryRuleDrivenType<int::value_type>::type(const GenEntryRuleDrivenType<int::value_type>&)&)
    runGenEntries<std, vector<int, std::allocator,<int>void> >(const GenEntryRulesDrivenType<int::value_type>::type(const GenEntryRulesDrivenType<int::value_type>&)&)
    

    Why Oracle cannot achieve the same thing is beyond me.

    The Qt Workaround

    The Qt code where this bug/workaround are referenced is here.

    We can see there are two functions declared with a similar name:

    Expression::Ptr DocFN::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType);
    Expression::Ptr IdFN::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType);
    

    And each is compiled in its own source (here and here) to work around the bug.

    Unsupported solution: Change the mangling option in ccfe

    From here, you can also compile with -Qoption ccfe -abiopt=mangle6. With these flags the code compiles successfully and the symbols (demangled) are:

    void runGenEntries<std::vector<int> >(const GenEntryRuleDrivenType<__type_0::value_type>::type&,const __type_0&)
    void runGenEntries<std::vector<int> >(const GenEntryRulesDrivenType<__type_0::value_type>::type&,const __type_0&)
    

    The problem is that this compilation option is not supported, as written by Steve Clamage:

    Finally, the compiler has a hidden option to fix all known mangling bugs unconditionally. We don't publicize the option because

    • It is unstable. Future patches or releases could change mangling if more bugs are found.
    • You might need to recompile all of the C++ code, including 3rd-party libraries, using this option.
    • If you create a library with this option, it might be incompatible with code compiled without the option.
    • As with all hidden options, it is subject to change or removal without notice.
    • We view this option as "use at your own risk".

    If after all these caveats you still want to try the option, here it is:

    -Qoption ccfe -abiopt=mangle6
    

    Be sure to add it to every CC command, and recompile everything.

    Fortunately, none of the C++ system libraries shipped with Solaris or Studio is affected by this bug or the hidden option, so you don't need to worry about different versions of those libraries.