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:
- module cache
- single file
- separate module file
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):
.cpp
vs.mpp
extensions, though it shouldn't matter- clearing the cache before each compilation (ref)
global module fragment (7)
placement i.e.module; [stuff]; export module mymodule;
- compiling separately (
-c mymodule.cpp
+-c main.cpp
) to link manually (fails the same way onmain.cpp
) - (re)moving
export
from the function - not calling the function, just importing (
import mymodule;
to trigger compilation) - switching from
std::vector<std::string>
tostd::vector<int>
to see whether the template's argument list causes the problem - switching from
std::vector<std::string>
tostd::pair<int, int>
and then tostd::pair<std::string, std::string>
(with<utility>
header module cache) to see whether just<vector>
is broken for me
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?
- using
-m64
does nothing for the module cache, remains 32bit - using
-m32
(apk install -y g++-multilib
on 64bit) returns the same output
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
.
a.out
in 64bit. But that doesn't seem to impact it for thestd::pair
swap. – Farther-m64
does nothing for the module cache and-m32
returns the same output withBad file data
. – Farthermodules
are pretty new and I expect that the level of standard compliance isn't 100% in every compiler. – Kassalagthr-default.h
andweakref declaration must have internal linkage
, so I'll get some sleep and try to check again with a clear head tomorrow. I'll update the question. Maybe it's just GCC specific, perhaps a regression or something. Except possible clang++ alternative, I don't have access to other compilers, so suggestions are welcome. The modules have quite a pull, it'd be nice to have them working. :) – Fartheralpine:20220316
withgcc version 11.2.1 20220219 (Alpine 11.2.1_git20220219)
. Few notes: 1. It is a member of a struct with ofstd::vector<std::string_view>
2.std::vector<int>
is OK 3.std::vector<std::string>
is NOK 4.std::vector<empty_class_in_same_module>
is NOK 5.using simple_type_alias=std::vector<std::string_view>
is NOK 6.template<class t> struct vector_factory {using type=...};
is NOK 7.std::deque<std::string_view>
is NOK 8.std::list<std::string_view>
is NOK UPDATE: I think it is more related to `std::string_view, std::string, const – Crossed