c++gccvectorc++20

Vector in C++ module causes useless Bad file data GCC output


TL;DR: GCC 11.2.0 (image f7ea55625e09) + C++20 + <vector>'s std::vector<anything> cause useless output. How to get out something I can work with?

Compilation works in:

In module imported at main.cpp:4:1: import mymodule;
mymodule: error: failed to read compiled module: Bad file data eh???????
mymodule: note: compiled module file is 'gcm.cache/mymodule.gcm' exists, 124 912 Bytes 
mymodule: fatal error: returning to the gate for a mechanical issue ????????
compilation terminated.

For the fatal (gate) I found only these references (1, 2), from which everything looks okay for my case.


I've tried various simple things with the new C++ modules (C++20, GCC 11.2) and it makes me wonder whether I'm just encountering a compiler bug / missing implementation or not getting something very simple.

Here is a simple C++ code with vector<string>, it compiles just fine with basic flags and outputs what's expected:

# create module cache for system headers
for item in iostream string vector
do
    g++ -fmodules-ts -std=c++20 -x c++-system-header $item
done
g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts main.cpp
// main.cpp
import <iostream>;
import <string>;
import <vector>;

int main() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}
$ ./a.out 
Hello
world

Here I move the vector creation into a new function, compiles fine, works fine. Still no separate module except for the system headers.

// main.cpp
import <iostream>;
import <string>;
import <vector>;

std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

int main() {
    std::vector<std::string> vec = create();

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}

And here I move the function to a separate, exported function in a separate module file.

g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts -c mymodule.cpp
// mymodule.cpp
export module mymodule;
import <string>;
import <vector>;

export std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

which compiles just fine, but when adding to the main.cpp,

import <iostream>;
import <string>;
import <vector>;
import mymodule;

int main() {
    std::vector<std::string> vec = create();

    for (auto& item : vec) {
        std::cout << item << std::endl;
    }
}

I get only this:

g++ -Wall -Wextra -Wpedantic -std=c++20 -fmodules-ts mymodule.cpp main.cpp
In module imported at main.cpp:4:1: import mymodule;
mymodule: error: failed to read compiled module: Bad file data eh???????
mymodule: note: compiled module file is 'gcm.cache/mymodule.gcm' exists, 124 912 Bytes 
mymodule: fatal error: returning to the gate for a mechanical issue ????????
compilation terminated.
# file gcm.cache/mymodule.gcm
ELF 32-bit LSB no file type, no machine, version 1 (SYSV)
# file gcm.cache/usr/local/include/c++/11.2.0/iostream.gcm
ELF 32-bit LSB no file type, no machine, version 1 (SYSV)
# file a.out
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped                                                                             

And it doesn't seem to be a problem with simple containers nor with the import declarations of the <vector> alone:

// mymodule.cpp
export module mymodule;
import <string>;
import <vector>;

export std::string create() {
    return "world";
}

// main.cpp
import <iostream>;
import <string>;
import <vector>;
import mymodule;

int main() {
    std::string vec = create();
    std::cout << vec << std::endl;
}

And I've tried playing with (and with no real effect):

And it looks like <vector> header is causing the problem. Any ideas how can I pry open GCC to give me something better than "naaah, can't do"? At the least I can generate assembly with -S (3k+ lines) or use hexdump / objdump for gcm.cache/mymodule.gcm and look at the binary, but I'm not sure what to look for because of the useless output.

Edit: It looks like a problem with the architectures perhaps?

Edit 2: So I rewrote it a bit to make it compatible with Clang 12 (b978a93) with a help of this article but it's not 1:1 and is rather kind of butchering (string_view note), but maybe I'm not seeing the broader picture or something is missing.

I don't think I should be including <string_view> though as that should have been included automatically. Otherwise even if I write my module, I can just start copy-pasting every #include from the implementation until there's none left so I can ensure the file order (then again what'd be the module's point).

// mymodule.cpp
module;
// no proper "import" available yet, so switching to the old includes
#include <string>
#include <vector>
#include <iostream>
export module mymodule;

export std::vector<std::string> create() {
    std::vector<std::string> vec = std::vector<std::string>{};
    vec.push_back("Hello");
    vec.push_back("world");
    return vec;
}

// need to wrap printing for a string_view for some reason
export void printme(std::string &item) {
    std::cout << item << std::endl;
}
// main.cpp
// includes needed for "auto" and usage of those types
// and were needed also for print(create()) call
// so something seems broken over here
#include <vector>
#include <string>
import mymodule;

int main() {
    auto vec = create();
    for (std::string item : vec) {
        // string_view:142:2: note: declaration of
        // 'basic_string_view<_CharT, _Traits>' does not match
        // std::cout << item << std::endl;
        printme(item);
    }
}
clang++ -std=c++20 -c mymodule.cpp -Xclang -emit-module-interface -fimplicit-modules -fimplicit-module-maps -o mymodule.pcm
clang++ -std=c++20 -fprebuilt-module-path=. -fimplicit-modules -fimplicit-module-maps mymodule.cpp main.cpp

So the issue seems to be GCC specific and most likely is a bug judging by the architecture switching (hardcoding/wrong code branch in GCC?). Maybe worth revisiting after >11.2.0.


Solution

  • I'm writing a shared library using C++20 modules (gcc 11.2.0) and have the same "Bad file data" error for std::vector with custom type. In my case, as already mentioned by @balázs-Árva this error occurs for most containers: maps, lists, etc.

    Following your references I found this issue and now have a temporary solution with just this line in module.

    namespace std _GLIBCXX_VISIBILITY(default){}
    

    This works in my library with other containers. I've looked for a proper solution using compiler options but haven't any success.

    Files / commands

    // mymodule.cpp
    export module mymodule;
    import <string>;
    import <vector>;
    
    namespace std _GLIBCXX_VISIBILITY(default){}
    
    export std::vector<std::string> create() {
        std::vector<std::string> vec = std::vector<std::string>{};
        vec.push_back("Hello");
        vec.push_back("world");
        return vec;
    }
    
    // main.cpp
    import <iostream>;
    import <string>;
    import <vector>;
    import mymodule;
    
    int main() {
        std::vector<std::string> vec = create();
        for (auto& item : vec) {
            std::cout << item << std::endl;
        }
    }
    
    g++ -fmodules-ts -std=c++20 -x c++-system-header iostream
    g++ -fmodules-ts -std=c++20 -x c++-system-header string
    g++ -fmodules-ts -std=c++20 -x c++-system-header vector
    g++ -fmodules-ts -std=c++20 mymodule.cpp main.cpp
    
    $ ./a.out 
    Hello
    world
    

    P.S. I can't comment, so posted it as answer, hope it will bring up them.