Skip to content

Instantly share code, notes, and snippets.

@DanielaE
Last active July 5, 2023 20:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielaE/39e270adb6ab27d12c61e1491d355d30 to your computer and use it in GitHub Desktop.
Save DanielaE/39e270adb6ab27d12c61e1491d355d30 to your computer and use it in GitHub Desktop.
Internal module partitions cause grief
// https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Modules.html
// gcc 12.2
# do NOT compile m-a.cpp, it will fail immediately!
g++ -std=c++2b -fmodules-ts -x c++ m-pif.cppm m.cppm m-b.cppm m-c.cppm main.cpp
// https://clang.llvm.org/docs/StandardCPlusPlusModules.html
// clang 15.0
clang++ -std=c++2b -fprebuilt-module-path=. m-pif.cppm m.cppm m-b.cppm m-c.cppm --precompile
clang++ -std=c++2b -fprebuilt-module-path=. -fmodule-file=m.pcm m-a.cpp m-b.pcm m-c.pcm m.pcm m-pif.pcm main.cpp
// msvc 17.5
cl /nologo /EHsc /std:c++latest /TP /c /interface m-pif.cppm m.cppm
cl /nologo /EHsc /std:c++latest /TP /c /internalPartition m-b.cppm m-c.cppm
cl /nologo /EHsc /std:c++latest main.cpp m-a.cpp m-b.obj m-c.obj m-pif.obj m.obj
// All compilers compile, link, and produce the "expected" result despite the program being ill-formed.
// For well-formedness add -DWANT-WELLFORMED
module m; // a module implementation unit, makes exported a@m(), b@m(), c@m() visible
// - is a module implementation unit [module.unit]/2
// - IMPLICITLY imports transitively partition :pif, [module.unit]/8 + [module.import]/7
// a@m()
// - is attached to module 'm' [module.unit]/7.3 + [basic.link]/10
// - is owned by module 'm'
// - 'corresponds' to a@m in partition :pif, [basic.scope]/4
// - IS the same entity as a@m with external linkage in partition :pif #1, [basic.link]/8
// -> has external linkage, [basic.link]/2.1 + [basic.link]/4.9
int a() {
return 42;
}
// TU not accepted by gcc,
// complains about contents of BMI (m.gcm), see below
// but accepts the same BMI for import into main.cpp
/*
$ g++ -std=c++2b -fmodules-ts -c m-a.cpp
In module imported at m-a.cpp:1:1:
m: error: failed to read compiled module: Bad file data
m: note: compiled module file is 'gcm.cache/m.gcm'
m: fatal error: returning to the gate for a mechanical issue
compilation terminated.
*/
module m:b; // an internal module partition that imports the module interface partition
// - is a module implementation unit [module.unit]/2
// - is a module partition [module.unit]/3
// - EXPLICITLY imports partition :pif, [module.import]/4
import :pif; // #1 // make exported a@m(), b@m(), c@m() visible
// b@m()
// - is attached to module 'm' [module.unit]/7.3 + [basic.link]/10
// - is owned by module 'm'
// - 'corresponds' to b@m in partition :pif, [basic.scope]/4
// - IS the same entity as b@m with external linkage in partition :pif #2, [basic.link]/8
// -> has external linkage, [basic.link]/2.1 + [basic.link]/4.9
#ifndef _MSC_VER // not accepted by msvc if import declaration #1 is present 😮
int b() {
return 42;
}
#endif
/* msvc chokes on the attempt to compile function b@m() like this
m-b.cppm(7,9): error C7619: cannot export 'b' as module partition 'b' does not
contribute to the exported interface of module unit 'm'
m-b.cppm(7,9): error C7619: int b() {
m-b.cppm(7,9): error C7619:
*/
module m:c; // an internal module partition
// - is a module implementation unit [module.unit]/2
// - is a module partition [module.unit]/3
// none of a@m(), b@m(), c@m() are visible
#ifndef WANT_WELLFORMED // sheer presence of c() in translation phase 9 makes program IF-NDR
// c@m()
// - is attached to module 'm' [module.unit]/7.3 + [basic.link]/10
// - is owned by module 'm'
// - is NOT exported
// - has module linkage, [basic.link]/2.2 + [basic.link]/4.8
// - 'corresponds' to c@m in partition :pif, [basic.scope]/4
// - is NOT the same entity as c@m with external linkage in partition :pif #3, [basic.link]/8
// - is NOT an ODR violation
int c() {
return 42;
}
#endif
export module m:pif; // a module interface partition unit
// - is a module interface unit [module.unit]/2
// - is a module partition [module.unit]/3
// all function declarations
// - are attached to module 'm' [basic.link]/10
// - are owned by module 'm'
// - have external linkage, [basic.link]/2.1 + [basic.link]/4.9
export {
int a(); // a@m() entity #1
int b(); // b@m() entity #2
int c(); // c@m() entity #3
}
export module m;
// - is the primary module interface unit [module.unit]/2
export import :pif; // exports a@m(), b@m(), c@m()
// interface dependency on partition :pif
// no interface dependency on partitions :b and :c
import m; // make exported a@m(), b@m(), c@m() visible
int main() {
int rc = 0;
#if !defined(__GNUC__) || defined(__clang__)
rc += a();
#endif
#ifndef _MSC_VER
rc += b();
#endif
#ifndef WANT_WELLFORMED
rc += c();
#endif
return rc;
}
@iains
Copy link

iains commented Jul 5, 2023

well, philosophically, in m and m:pif the module author has promised that the three functions exist (so the consumers of the interfaces have to believe it). That promise turns out to be a "white lie" (or a typo even missing the :pif in m-c.cpp).

m:b and m:c both produce functions with the correct signature (the exportedness of b is only significant within the compiler, by the time the code is emitted the mangling of b@m is the same as c@m).

we have no mechanism to check that the module is "complete" (in an analogous manner to checking that a class impl. is complete) .. and IDK if it's even possible.

The linker (as things stand) cannot tell the difference between the b@m and c@m cases and therefore links happily.

Thinking of ways to (a) prove that an implementation is complete (b) to differentiate exported-ness in the mangling (which would allow the linker to complain) might be a way forward ..

.. I have a sneaking suspicion that [b] here was tried in the modules-ts and then withdrawn in favour of the scheme we have now (but ICBW).

perhaps this is a strange corner-case (where the user made the correct thing by accident) - have we evidence that people have made this mistake in the wild?

edit: I actually checked that the AST clang produces has the correct linkage for m-b and m-c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment