On Thu, Jun 9, 2022 at 2:16 PM Gabriel Dos Reis via Core <core@lists.isocpp.org> wrote:

[Mathias]

  • So the most logical conclusion is that implementation partitions are made part of the program image in the same way as every other TU in the program (outside the purview of the ISO C++ standard, ie in the build system).

 

By the same token, they would be subject to the same lack of guarantees that other Tus enjoy.  No?


Sure, if we are talking strictly about the wording of the standard. However, I would expect that in practice the guarantees that real-world tools provide today will continue to be provided with modules, at least by default. That includes that dynamic init for all .o files passed to the linker will run. And that if you use the whole-archive flag when linking, every initializer in that static lib will also run. I think that should apply regardless of the type of TU that the .o is derived from (and that includes header units and implementation partition units). That said, I can buy that it doesn't apply to interface partitions not transitively imported by the PMI because http://eel.is/c++draft/module.unit#3 says that they can't exist (ideally this would be diagnosed by the linker but I can accept the usual IFNDR rules applying)

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, what would you say the behavior should be 1) Both "init" and "foo" are printed in that order, 2) Diagnosed error because foo() isn't defined in the program image, 3) No error, but only "foo" is printed indicating that foo() is in the program image but its TU's dynamic init didn't run (this feels like either non-conformance or malicious conformance to me), 4) something else?
  

  • I'm guessing it is too late to DR implementation partitions out of existence as more trouble than they are worth? ;) 

 

I would prefer a fix that makes the interface vs. implementation correspondence more useful, e.g. make them implementation partitions of interface partition, and maybe provide a special syntax for what is specified in C++20 to avoid confusion.

 

-- Gaby

 

 

From: SG15 <sg15-bounces@lists.isocpp.org> On Behalf Of Mathias Stearn via SG15
Sent: Thursday, June 9, 2022 1:56 AM
To: C++ Core Language Working Group <core@lists.isocpp.org>
Cc: Mathias Stearn <redbeard0531+isocpp@gmail.com>; sg15@lists.isocpp.org; Nathan Sidwell <nathan@acm.org>
Subject: Re: [SG15] [isocpp-core] unimported implementation partitions

 

 

 

On Thu, Jun 9, 2022 at 10:31 AM Daniela Engert via Core <core@lists.isocpp.org> wrote:

Am 09.06.2022 um 09:55 schrieb Boris Kolpackov via SG15:
> Gabriel Dos Reis via SG15 <sg15@lists.isocpp.org> writes:
>
>> How would a program observe that an implementation partition isn't
>> imported but is relevant to the behavior of the entire program?
> The implementation partition can register itself (as part of the
> static initialization) in some registry.
>
> For example, the implementation partition may include a unit test
> (because that's the only way to access the private parts) which
> is registered with the test harness.

The standard is totally silent about module partitions [module.unit]/3
that are also module implementation units [module.unit]/2, and which are
not required to be reachable [module.reach]/2 because they are not part
of the interface dependency [module.import]/10 graph of a module. They
may or may not have semantic effects.

If you want the behaviour from your example be portable you need to form
an interface dependency to that partition.

 

The standard _isn't_ really silent on that. Or at least any more than it is silent on how only other TU is made part of the "program image" in http://eel.is/c++draft/lex.phases#1.9. So the most logical conclusion is that implementation partitions are made part of the program image in the same way as every other TU in the program (outside the purview of the ISO C++ standard, ie in the build system). Consider the following program:

 

module m:part;

import m;

static auto _ = puts("init");

void foo() { puts("foo"); }

 

export module m;

export void foo();

 

import m;

int main() { foo(); }

 

 

Either the m:part TU is part of the program, and it is well defined that "init" and "foo" will be printed (the http://eel.is/c++draft/basic.start.dynamic#5 loophole doesn't apply here), or that TU isn't part of the program and you have an ODR violation because there was no definition of foo (and in practice, you would expect a linker error rather than successful compilation). I don't think there is any way to read the standard such that it allows "foo" to be printed but not "init". I'm intentionally ignoring the trivial sense that you could declare this an ODR violation and under IFNDR anything can happen.

 

To be clear, I'm not saying that that TU _should_ say m:part rather than just m, just that it shouldn't change the behavior if it does.

 

I'm guessing it is too late to DR implementation partitions out of existence as more trouble than they are worth? ;) 

 


_______________________________________________
Core mailing list
Core@lists.isocpp.org
Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/core
Link to this post: http://lists.isocpp.org/core/2022/06/12661.php

_______________________________________________
Core mailing list
Core@lists.isocpp.org
Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/core
Link to this post: http://lists.isocpp.org/core/2022/06/12664.php