C++ Logo


Advanced search

Re: [isocpp-core] unimported implementation partitions

From: Daniel Ruoso <daniel_at_[hidden]>
Date: Thu, 9 Jun 2022 09:13:11 -0400
Em qua., 8 de jun. de 2022 às 18:17, Mathias Stearn
<redbeard0531_at_[hidden]> escreveu:
> The standard seems pretty precise in its definition of that term. If you disagree
> then I think you should file a core issue because its definitions should be precise.

Yeah, maybe reporting a core issue is worth it. The standard text uses
the term "implementation" freely without making the distinction
between "implementation unit" and "internal partition" very clear,
when there is clearly a significant difference in semantics between
the two.

That is why, on the Modules Ecosystem TR we are going with the term
"internal partition", since it more clearly communicates that it
doesn't have the same semantics of implementation units.

>> The implementation unit
> You’ve said the phrase “the implementation unit” a few times and that
> implies there is exactly one. Is this a typo or are you trying to describe
> something specific other than one of the zero or more implementation units?

Typo. There can be zero or more implementation units.

And, in another example of how the semantics are different, an
"internal partition" needs to have an unique name within the module,
you can't have "export module foo:bar" and then an implementation
"module foo:bar".

> I don’t think that is true. For example I think this is a valid program.

Yeah, but I don't think that's the interesting case.

> Don’t mistake me as being a fan of implementation partitions. I
> unsuccessfully argued that we should remove them from c++20,
> and still wish they weren’t a thing.

I do think there's a couple of good use cases.

 1. Internal partitions reachable by interface units, but that are
explicitly designed not to be a part of the external interface. This
can aid in the design process of the library to separate things that
should never be part of the external interface of the module.

 2. Internal partitions not reachable by interface units. Those are
useful to make code reusable within the module implementation, so I
would rewrite your original example as:

module m:internal;
void foo() {}

module m;
import :internal;
void bar() {foo();}

export module m;
export void bar();

> But since they are, I think we
> should be careful with terminology and not use words to mean
> something different from their definition in the standard
> (“specialization” and “static init” are bad precedents that lead
> to a lot of ambiguity and confusion).

Again, that's why we've built consensus around the term "internal
partition" instead.

> Neither of those programs is well formed. Both violate the first sentence of
> http://eel.is/c++draft/module.interface#1.
> If I add an export to make :interfacepart be an interface, then I think I see
> what you are saying

Yes, that was what I intended to do, but mistyped.

> but I still disagree. I forgot to mention that when adding a partition to an
> implementation unit’s modules declaration you also need to add the
> explicit import of the PMI that you were getting implicitly via
> http://eel.is/c++draft/module.unit#8. If you add that (and the
> missing export) then I think the first program becomes valid.

I don't think so, because the declaration is seen first as non
exported in `m:part1` and then seen as exported in `m:interfacepart`.

> Also I think in the first program there is no need to import :part1,
> and in the second there is no need to have any explicit imports
> in the pure implementation unit.

Yeah, but my point is that the semantics of the internal partition
being importable have other consequences, which is how they are
semantically different from the implementation units.

> Considering the concrete example of the program I included below
> the line you quoted, assuming that the object files for all TUs are
> supplied to the linker

That is missing the question that started this thread.

What if you have an internal partition with:

module m:part1;
int foo() { return 42 }
int x = foo();
int bar() { return x }


module m;
import :part1;
int y = bar();

If the semantics of the initialization are defined by the importation,
we can guarantee that the initialization of `int x` happens before the
call to `bar()`.

If the semantics of the initialization are defined by the presence of
the object file on the linker inputs, there are no guarantees about
the order.

I, personally, as much as I dislike global initializers, prefer the
stronger model of providing guarantees based on the importation.

And this is where the distinction between importable and
non-importable units make a huge difference, because in the case of
importable units there are concerns about the reachability of the
declarations, where in a non-importable units, we know that any local
declaration is only reachable within the same translation unit.


Received on 2022-06-09 13:13:27