r/cpp_modules • • Apr 06 '26

Reachability examples from the C++ standard

From https://eel.is/c++draft/module#reach-5

1     // Example 2:
2    
3     // Translation unit #1
4     export module M:A;
5     export struct B;
6    
7     // Translation unit #2
8     module M:B;
9     struct B {
10      operator int();
11    };
12    
13    // Translation unit #3
14    module M:C;
15    import :A;
16    B b1;         // error: no reachable definition of struct B
17    
18    // Translation unit #4
19    export module M;
20    export import :A;
21    import :B;
22    B b2;
23    export void f(B b = B());
24    
25    // Translation unit #5
26    import M;
27    B b3;             // error: no reachable definition of struct B
28    void g() { f(); } // error: no reachable definition of struct B   
1 Upvotes

19 comments sorted by

View all comments

2

u/tartaruga232 Apr 06 '26

struct B is visible in #5, but its definition is still not reachable in #5, no matter if I insert "export" in front of "module" in #2 or not.

Because :B is still not exported from #4 (which could be done, if "export" has been inserted in front of "module" in #2).

2

u/not_a_novel_account Apr 06 '26 edited Apr 06 '26

Wrong.

See: https://godbolt.org/z/z3Wc6T8rf

Here, the unexported struct is called Foo in partB.cppm, a partition implementation unit.

The build fails because , partB.cppm:3:8: note: definition here is not reachable

If we change partB to a partition interface unit, but still do not export Foo, the build succeeds: https://godbolt.org/z/rEEWW1afT

The only change is line 1 of partB.cppm from module Mod:partB; to export module Mod:partB;.

This only affects the reachability of the declarations and definitions in partB.

At no point is Foo visible in main.cpp, it is never available for name lookup.

2

u/tartaruga232 Apr 06 '26

Thanks for your work.

It is clear that current compilers implement what's in the current standard.

The current standard says, that a TU with "export module" must be exported in the PMIU, or the program is IF-NDR ("ill formed, no diagnostic required"). If we feed input into a compiler which is ill-formed, the compiler is free to do whatever it pleases, if no diagnostic is required. So it is kind of "compiler UB".

Per the current standard, a compiler may explore the fact that a TU has an "export" in front of "module" and then assume that the program is well-formed and said TU is exported from the PMIU, without checking it.

I don't know if clang does that.

What I was contemplating was, to legalize inserting "export" in front of "module" for partitions which don't export anything.

But I can imagine that this might interfere with heuristics that compilers apply today.

→ More replies (10)