Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

[C++20] [Module] The combination of iostream and file with .cppm suffix would crash #51498

Open Quuxplusone opened 3 years ago

Quuxplusone commented 3 years ago
Bugzilla Link PR52531
Status NEW
Importance P enhancement
Reported by Chuanqi Xu (yedeng.yd@linux.alibaba.com)
Reported on 2021-11-17 03:56:50 -0800
Last modified on 2021-11-22 09:51:59 -0800
Version trunk
Hardware PC All
CC blitzrakete@gmail.com, dblaikie@gmail.com, erik.pilkington@gmail.com, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also

I met an interesting bug which would happen only if iosstream and module file end with .cppm. Here is the reproducer:

// Hello.cppm
module;
#include <iostream>
export module Hello;
export void SayHello()
{
    std::cout << "Hello World.\n";

}
// main.cpp
import Hello;
int main() {
   SayHello();
   return 0;
}

And if I compile it as:

clang++ -std=c++20 --precompile Hello.cppm -o Hello.pcm
clang++ -std=c++20 -fprebuilt-module-path=. Hello.pcm main.cpp

And the generated executable would crash.

Interestingly, it wouldn't crash if we use std::printf instead of std::cout

// Hello.cppm
module;
#include <cstdio>
export module Hello;
export void SayHello()
{
    std::printf("Hello World.\n");

}
// main.cpp
import Hello;
int main() {
   SayHello();
   return 0;
}

Also, it wouldn't crash if the suffix is cpp instead of cppm. For example:

// Hello.cpp
module;
#include <iostream>
export module Hello;
export void SayHello()
{
    std::cout << "Hello World.\n";

}
// main.cpp
import Hello;
int main() {
   SayHello();
   return 0;
}

Compile argument:

clang++ -std=c++20 -Xclang -emit-module-interface Hello.cpp -o Hello.pcm
clang++ -std=c++20 -fprebuilt-module-path=. say_hello.cpp main.cpp

I am not sure if this is a bug in compiler or in runtime. It is appreciate if someone could help to confirm this.

Quuxplusone commented 3 years ago

Oh, I found the reason here. The standard library I am using is libstdc++. And its implementation for <iostream> would use a static variable in the header:

...
// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
...

And static variable implies this variable wouldn't be used outside the current TU, so the module wouldn't export such variables. And this variable would be eliminated in PCM files.

And as the comment says, __ioinit would construct filebuffers std::cout and so on. And since it is eliminated, std::cout wouldn't be initialized correctly. So here is the crash.

I am wondering how should we handle this...

Quuxplusone commented 3 years ago

Yep, you'd probably need a modulemap for the standard library to deal with this. (a modulemap would classify the header as an importable header unit and I think Clang already does the right thing for static variables in modulemapped headers/importable header units)