3

Consider this example

// translation C
export module M:C;
// translation U
export module M:B;
import :C;
// translation unit T
export module M;
export import :B;  // #1 imports `M:B` and `M:C`

[module.unit] says

All module partitions of a module that are module interface units shall be directly or indirectly exported by the primary module interface unit ([module.import]). No diagnostic is required for a violation of these rules.

[module.import] p7 says

When a module-import-declaration imports a translation unit T, it also imports all translation units imported by exported module-import-declarations in T; such translation units are said to be exported by T. Additionally, when a module-import-declaration in a module unit of some module M imports another module unit U of M, it also imports all translation units imported by non-exported module-import-declarations in the module unit purview of U. These rules can in turn lead to the importation of yet more translation units.

The first part says T exports the translation units if an exported module-import-declaration in T imports them.

Since there is a non-exported module-import-declaration import :C; in U, and the module-import-declaration at #1 in the primary module unit of M imports another module unit U of M, therefore the module-import-declaration at #1 also imports C, according to the part after "Additionally".

Again, since the module-import-declaration at #1 in T is exported, it directly imports M:B and indirectly imports M:C, therefore M:B and M:C are exported by T.

So, all partition module interface units are exported by the primary module interface unit. However, when compiling the example with GCC: g++ -std=c++20 -fmodules-ts c.cpp u.cpp t.cpp, it reports the error:

interface partition is not exported

Is it a bug of GCC or the partition C is indeed not exported by T and the rule is not clear here?

Update:

// file c
export module C;
export int fun(){return 0;}

// file b
export module M:B;
import C;

// file a
export module M;
export import :B;  // #1
int d = fun();  // #2

// file main
import M;
int main(){}

#2 in this example is well-formed. The reason is the exported module-import-declaration at #1 not only imports M:B but also imports C such that the function fun can be found at #2. So, why don't we agree that TU C imported by the exported module-import-declaration at #1 in TU a is exported by a, which is exactly said in the below?

all translation units imported by exported module-import-declarations in T; such translation units are said to be exported by T.

This is the part the standard is ambiguous here even though GCC implements the right things which cannot be read from the rule.

1 Answer 1

2

Also, since there is a non-exported module-import-declaration import :C; in U, the module-import-declaration at #1 that imports U therefore also imports C.

That is the opposite of what the standard you quoted says. The definition of "exported by T" requires the use of an "exported module-import-declaration in T".

You may have gotten confused because of your use of non-indicative names. I will use the actual module names instead of your translation unit names. What we care about here is the relationships between modules M, M:B, and M:C. M imports M:B, which imports M:C. However, when applying the wording of the standard, we can see that M:B import of M:C does not use an "exported module-import-declaration". Therefore, M:C is not "exported by" M:B.

When M imports and exports M:B, the set of translation units exported by this does not include M:C because it was not "exported by" M:B.

But M:C is an interface partition of the module M. And by the first rule you quoted, it must be "exported by" M, either directly or indirectly.

Therefore, your code is ill-formed.

11
  • @NicoBolas No, the second part says when a module-import-declaration in a module unit of some module M imports another module unit U of M, it also imports all translation units imported by non-exported module-import-declarations in the module unit purview of U. The module-import-declaration at #1 imports B(which is U), since B is module unit of M, the module-import-declaration at #1 also imports the translation units imported by non-exported module-import-declarations in the module unit purview of U (which is import :C;).
    – xmh0511
    Commented Jun 15, 2022 at 5:36
  • @NicoBolas Since the module-import-declaration at #1 that directly imports B and indirectly imports C is exported in T, therefore B is directly and C is indirectly exported by T.
    – xmh0511
    Commented Jun 15, 2022 at 5:38
  • @xmh0511: Yes, it imports them, but it doesn't export them. Is M:C "exported by" M:B? If the answer to that question is "no", then it isn't exported by someone who imports M:B, even if they use export import to import it. Nothing in that text makes export transitive; indeed, the other text makes it clear that "exported by" requires the direct use of the syntax. Commented Jun 15, 2022 at 5:58
  • Is M:C "exported by" M:B? we do not care about whether B exports C. Do you agree the module-import-declaration at #1 imports C? You agree that(Yes, it imports them). The first part only says: When a module-import-declaration imports a translation unit T, it also imports all translation units imported by exported module-import-declarations in T; such translation units are said to be exported by T. The export import :B; indirectly imports C, why do you think C is not exported by T?
    – xmh0511
    Commented Jun 15, 2022 at 6:05
  • 1
    @xmh0511: "This means all translation units that are imported by such a module-import-declaration in T" Again, you are confusing your Ts. The question is very simple: does M:B export M:C? If the answer to that question is "no", then anything which imports M:B will not export M:C, because M:C is not "exported by" M:B. Because there is no export declaration which specifies M:C. Commented Jun 15, 2022 at 6:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.