Avoid multiple definitions of `lst::info` (MinGW compile fix)
authorAlexey Sheplyakov <asheplyakov@altlinux.org>
Wed, 6 Jan 2021 07:20:46 +0000 (11:20 +0400)
committerAlexey Sheplyakov <asheplyakov@altlinux.org>
Wed, 6 Jan 2021 13:29:52 +0000 (17:29 +0400)
commitc3195f0b5a7ac9fdbfdd04e5f4acf6a836063de0
tree737220c140c910c44bbbb525060fa8e115244362
parent79f30c335f1ddbd3c76dfee5d76128b992b6b19c
Avoid multiple definitions of `lst::info` (MinGW compile fix)

[55/59] Linking CXX shared library bin/libginac.dll
FAILED: bin/libginac.dll ginac/libginac.dll.a
[skipped long list of object files]
/usr/bin/x86_64-w64-mingw32-ld: ginac/CMakeFiles/ginac.dir/lst.cpp.obj: in function `GiNaC::ptr<GiNaC::basic>::~ptr()':
/home/asheplyakov/work/sw/ginac/_build_w64/../ginac/container.h:150: multiple definition of `GiNaC::container<std::__cxx11::list>::info(unsigned int) const'; ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.obj:/home/asheplyakov/work/sw/ginac/_build_w64/../ginac/container.h:116: first defined here

integration_kernel.cpp makes use of GiNaC::lst without including the `lst.h`
header. That's possible since there's a typedef in `registrar.h` (included
by virtually all GiNaC sources) and a defintion in `container.h` (included
via `add.h` -> `expairseq.h` -> `indexed.h` -> `exprseq.h`), so the compiler
can instantiate container<std::list>. However the explicit specialization
of `lst::info` is not available (in integration_kernel.cpp). This violates
17.8.3.6 [templ.expl.spec] which demands

If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared before
the first use of that specialization that would cause an implicit
instantiation to take place, in every translation unit in which such
a use occurs; no diagnostic is required. If the program does not provide
a definition for an explicit specialization and either the specialization
is used ina way that would cause an implicit instantiation to take place
or the member is a virtual member function, the program is ill-formed,
no diagnostic required.

On ELF platforms libginac appears to link just fine despite having two
instantiations of `lst::info` since the of them (in `integration_kernel.o`)
is a weak symbol:

$ find ginac -type f -name '*.o' | xargs -n 1 nm --print-file-name --defined | c++filt | grep -e 'list>::info('
ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.o:0000000000000000 W GiNaC::container<std::__cxx11::list>::info(unsigned int) const
ginac/CMakeFiles/ginac.dir/lst.cpp.o:0000000000000000 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const

so the linker discards the wrong instantiation of `lst::info` method.
However on MinGW there are no weak symbols (in ELF sense):

$ find ginac -type f -name '*.obj' | xargs -n 1 x86_64-w64-mingw32-nm --print-file-name --defined | c++filt | grep -e 'list>::info('
ginac/CMakeFiles/ginac.dir/lst.cpp.obj:0000000000000010 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const
ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.obj:0000000000000000 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const

Hence the above multiple definition error.

To avoid the problem #include "lst.h" (so explicit specialization is available).
While at it explicitly instantiate lst::info method in lst.cpp

A better solution would be to remove declaration of lst from registrar.h,
but that's too disruptive since GiNaC uses lst a lot: subs, unarchive, etc.
ginac/integration_kernel.h
ginac/lst.cpp
ginac/lst.h