Link error in clang but not g++

Dear clang list,

first thank you a lot for providing clang, clang is really so much
better for error messages (especially templates) than gcc!

I build a shared library essentialy with the CMake part

add_library(integration quadraturePy.cpp quadratureRulesPy.cpp)

In the file `quadratureRulesPy.cpp` the constructor for the template
QuadratureRules<double, 0> is define. It is also part of the object
file:

nm -C CMakeFiles/integration.dir/quadratureRulesPy.cpp.o | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

00000000 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

In the file `quadraturePy.cpp` this symbol is not defined, and thus
also not part of the object file:

nm -C CMakeFiles/integration.dir/quadraturePy.cpp.o | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

         U parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

In the final library both object files are combined and the symbol
appears twice:

nm -C libintegration.so | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

         U parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)
000ad400 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

Once this library is loaded from python, I get the following linker error:

ImportError: libintegration.so: undefined symbol:
_ZN6parfem15QuadratureRulesIdLi0EEC1Ej
(where c++filt shows that the problem is exactly this symbol.

Dear clang list,

first thank you a lot for providing clang, clang is really so much
better for error messages (especially templates) than gcc!

I build a shared library essentialy with the CMake part

add_library(integration quadraturePy.cpp quadratureRulesPy.cpp)

In the file `quadratureRulesPy.cpp` the constructor for the template
QuadratureRules<double, 0> is define. It is also part of the object
file:

nm -C CMakeFiles/integration.dir/quadratureRulesPy.cpp.o | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

00000000 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

In the file `quadraturePy.cpp` this symbol is not defined, and thus
also not part of the object file:

nm -C CMakeFiles/integration.dir/quadraturePy.cpp.o | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

    U parfem::QuadratureRules&lt;double, 0&gt;::QuadratureRules\(unsigned int\)

In the final library both object files are combined and the symbol
appears twice:

nm -C libintegration.so | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

    U parfem::QuadratureRules&lt;double, 0&gt;::QuadratureRules\(unsigned int\)

000ad400 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

Something very strange is happening in your link step; if I'm reading
this right, it should not be possible...

Have you tried building the code with clang, and linking with gcc?

-Eli

Eli,

I tried to replace the linker with g++, but no success.

Out of the three steps:
1) build quadratureRulesPy.cpp
2) quadraturePy.cpp
3) link

I need to use g++ for step 1) (which creates the symbol
parfem::QuadratureRules<double, 0>::QuadratureRules).

I can then use clang for steps 2 and/or 3 which works nicely. If I use
clang for step 1 then I always end up with the reported error.

-Holger

Hmm... it's possible clang is using the wrong linkage.

Are you using clang from trunk? If not, can you try?
(http://clang.llvm.org/get_started.html)

-Eli

Eli,

I tried to replace the linker with g++, but no success.

Out of the three steps:
1) build quadratureRulesPy.cpp
2) quadraturePy.cpp
3) link

I need to use g++ for step 1) (which creates the symbol
parfem::QuadratureRules<double, 0>::QuadratureRules).

I can then use clang for steps 2 and/or 3 which works nicely. If I use
clang for step 1 then I always end up with the reported error.

Hmm... it's possible clang is using the wrong linkage.

Are you using clang from trunk? If not, can you try?
(http://clang.llvm.org/get_started.html)

-Eli

Eli,

ok I downloaded it from the SVN and compiled it with the trunk version:

Debug+Asserts/bin/clang --version

clang version 3.0 (trunk 139719)
Target: i386-pc-linux-gnu
Thread model: posix

The error stays exactly the same (the compilation takes significantly
longer as it is the debug version, but I guess that is to be
expected).

-Holger

Okay... what exactly does the definition of
parfem::QuadratureRules<double, 0>::QuadratureRules look like?

-Eli

Eli,

it did not really matter much, how the constructor looks like. So I
was able to shorten it very much and still keep the error, namely:

  template<class ScalarT, int type>
  QuadratureRules<ScalarT, type>::QuadratureRules(unsigned int n)
  {
    bool cond = (n >= 2);
    if (!(cond)) {
      std::stringstream errorMsg;
      errorMsg << "by violating condition <n >= 2>";
      throw std::logic_error(errorMsg.str());
    }
  }

I can not shorten it much more, as otherwise the constructor is
removed from the object file completely (I guess due to optimization).
This is probably also bad by itself (the other file still references
the optimized-out constructor!), but my current goal is to simplify
the code as much as possible and still keep the error.

nm -C libintegration.so | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

         U parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)
0007b7b0 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

I also notice that the error disappears if I turn of optimization. So
the error is there if I use
`clang -ferror-limit=2 -g -O2` [...]
and also for -O3. Without any optimization the result is

nm -C libintegration.so | grep "parfem::QuadratureRules<double, 0>::QuadratureRules"

000d3ac0 W parfem::QuadratureRules<double,
0>::QuadratureRules(parfem::QuadratureRules<double, 0> const&)
000d2a70 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)
000d3b30 W parfem::QuadratureRules<double,
0>::QuadratureRules(parfem::QuadratureRules<double, 0> const&)
000d2e10 W parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

-Holger

Eli,

I managed to minimalize the error by taking out almost all the code
and removing all the dependencies to externa libraries. The final code
is in the attachment. This code produces the error for me.

There are now a few ways to remove the error.

1) Remove the second or third of those template instantiations in
quadraturePy.cpp:
template class QuadraturePy<double, 0>;
template class QuadraturePy<double, 1>;
template class QuadraturePy<int, 0>;

2) remove the second case here:
      case GAUSS_JACOBI: init<GAUSS_JACOBI>(dims);
      case GAUSS_LOBATTO: init<GAUSS_LOBATTO>(dims);

3) uncomment the line
//template class QuadratureRules<double, 0>; // CHECK: uncommenting
this line fixes the error

4) remove optimization (as mentioned in the previous mail)

Especially the case 3) seem to be a good lead. It might also be a bad
practise what I did there. I explicitly instantiate the class
QuadratureRulesPy
template class QuadratureRulesPy<double, 0>;
which then implicitly instantiates the symbol in the error
(parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)).
But I do not explicitly instantiate the class QuadratureRules, which I
probably should have done.

The error is still quite strange with a symbol beeing undefined and
defined inside the same symbol, but with the approach 3) I am able to
solve this even for my big project.

-Holger

clang_bug.tar.bz2 (42 KB)

Have you tried without -C? Demangling is not an injective operation, and _ZN6parfem15QuadratureRulesIdLi0EEC1Ej, _ZN6parfem15QuadratureRulesIdLi0EEC2Ej, _ZN6parfem15QuadratureRulesIdLi0EEC3Ej all print the same parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

Other interesting tests include testing with a recent gcc (default visibility changed, more privatization, etc) to make sure the bug doesn't also appear there.

Doh; that's almost certainly the issue.

-Eli

Marc,

this is precisely what is going on, without -C the symbols are:

000014c0 W _ZN6parfem15QuadratureRulesIdLi0EEC2Ej
         U _ZN6parfem15QuadratureRulesIdLi0EEC1Ej

and they are both demangled to the same:

c++filt _ZN6parfem15QuadratureRulesIdLi0EEC2Ej
parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

c++filt _ZN6parfem15QuadratureRulesIdLi0EEC1Ej
parfem::QuadratureRules<double, 0>::QuadratureRules(unsigned int)

Out of curiosity, what does the `C1Ej` and `C2Ej` part that decodes a
different symbol, but does not appear in the demangling stand for?

-Holger

Constructor variants. C2 is used when constructing a base class
sub-object, C1 is used when constructing a complete object. They're
semantically identical unless your class has virtual bases, but we have
to provide both according to the Itanium ABI. Clang implements C1
in terms of C2 whenever it can.

John.