Modules TS and module ownership rule

118 views
Skip to first unread message

Richard Smith

unread,
Oct 5, 2017, 9:38:55 PM10/5/17
to C++ Core Language Working Group, mod...@isocpp.org, Gabriel Dos Reis
Hi,

[Within the examples below, a blank line indicates a transition to a different translation unit.]

The Modules TS [basic.def.odr] (6.2)/6.7 is supposed to implement the "module ownership rule": the same entity (or set of declarations or something) cannot be owned by multiple modules (including the global module as a module for this purpose).

But... this wording was incorrectly placed in [basic.def.odr] (which is wrong because this wording is intended to apply to all declarations and [basic.def.odr] only applies to definitions). Partly as a result, it's unclear which declarations this rule *should* apply to.

For example, does it apply to typedefs:

using T = int;
module M;
using T = int;

? (Is that ill-formed because T is owned by the global module and by module M? Or is it valid because... something?)

And if we permit that, is that two different Ts (so any use of T is ambiguous) or a single T that resolves to 'int'?

Clearly this is intended to be valid:

namespace N {}
module M;
namespace N {}

... but what about this:

namespace N {}
namespace N2 = N;
module M;
namespace N2 = N;

? (Two ambiguous N2's, a single N2, or an error?)

I think it's clear that different owning modules for variables, functions, classes, enums, and templates should all be disallowed.

Given that we said

export module A;
export int n;

export module B;
import A;
export using ::n;

... is valid, is this valid:

int n;
export module M;
export using ::n;

... and likewise is this valid:

namespace X { int n; }
using X::n;
export module M;
using X::n;

... and what about this:

namespace X { int n; }
using X::n;
export module M;
export using X::n;

?

Given the content of 17.7/8, it's unclear how explicit or partial template specializations should be treated. For example:

export module A;
export template<typename T> struct X {};
template<> struct X<int>;

import A;
template<> struct X<int>; // ok?
module B;
template<> struct X<int>; // ok?

In the terminology of 17.7/8, we say the template specialization X<int> is owned by module A. 10.7/4 (read in reverse) suggests that that might mean the same thing as the declaration being in the purview of A, which might mean the above case is OK. Or perhaps not.

If this is *not* the intended effect of the rule in 17.7/7 and /8, then what purpose do they serve?

Gabriel Dos Reis

unread,
Oct 6, 2017, 12:03:57 PM10/6/17
to Nathan Sidwell, C++ Core Language Working Group, mod...@isocpp.org, Richard Smith


| -----Original Message-----
| From: Nathan Sidwell [mailto:nathanm...@gmail.com] On Behalf Of
| Nathan Sidwell
| Sent: Friday, October 6, 2017 7:10 AM
| To: C++ Core Language Working Group <co...@lists.isocpp.org>;
| mod...@isocpp.org; Gabriel Dos Reis <g...@microsoft.com>
| Cc: Richard Smith <richar...@google.com>
| Subject: Re: [isocpp-core] Modules TS and module ownership rule
|
| On 10/05/2017 09:38 PM, Richard Smith via Core wrote:
|
| > But... this wording was incorrectly placed in [basic.def.odr] (which is
| > wrong because this wording is intended to apply to all declarations and
| > [basic.def.odr] only applies to definitions). Partly as a result, it's
| > unclear which declarations this rule *should* apply to.
|
| A closely related question is whether the following is well-formed:
|
| // TU A. Not a module
| extern int bob;
| int some_func () {return bob;}
|
| // TU B, A module
| export module frob;
| export int bob = 5;
|
| My reading of the wording added to basic.def.odr, is that that's an ODR
| violation.

It is indeed an ODR violation.

But I think it is something implementations want to work, as
| it's a transition path from non-modules -> modules. I gave it as a
| motivating example for the ABI changes in my cppcon talk.

I am not so sure. Why can't you write

int bob - 5;
export module frob;
export using ::bob;

which is simpler, cleaner, and maintains ABI?
The module 'frob' clearly states it is providing view over an existing code.

That is what I would recommend for transition.

-- Gaby

Gabriel Dos Reis

unread,
Oct 6, 2017, 12:20:22 PM10/6/17
to Nathan Sidwell, C++ Core Language Working Group, mod...@isocpp.org


| -----Original Message-----
| From: Nathan Sidwell [mailto:nathanm...@gmail.com] On Behalf Of
| Nathan Sidwell
| Sent: Friday, October 6, 2017 9:17 AM
| To: C++ Core Language Working Group <co...@lists.isocpp.org>;
| mod...@isocpp.org
| Cc: Gabriel Dos Reis <g...@microsoft.com>
| Subject: Re: [isocpp-core] Modules TS and module ownership rule
|
| On 10/06/2017 12:03 PM, Gabriel Dos Reis via Core wrote:
|
| > int bob - 5;
| > export module frob;
| > export using ::bob;
| >
| > which is simpler, cleaner, and maintains ABI?
| > The module 'frob' clearly states it is providing view over an existing code.
| >
| > That is what I would recommend for transition.
|
| I (the library author) want to provide a header file to users who do not
| have a module compiler OR I don't want to break my external API when
| converting to modules.

Exactly! The above will achieve that.

|
| nathan
|
| --
| Nathan Sidwell

Richard Smith

unread,
Oct 6, 2017, 4:29:39 PM10/6/17
to C++ Core Language Working Group, mod...@isocpp.org, Gabriel Dos Reis
On 5 October 2017 at 18:38, Richard Smith <richar...@google.com> wrote:
Hi,

[Within the examples below, a blank line indicates a transition to a different translation unit.]

The Modules TS [basic.def.odr] (6.2)/6.7 is supposed to implement the "module ownership rule": the same entity (or set of declarations or something) cannot be owned by multiple modules (including the global module as a module for this purpose).

But... this wording was incorrectly placed in [basic.def.odr] (which is wrong because this wording is intended to apply to all declarations and [basic.def.odr] only applies to definitions). Partly as a result, it's unclear which declarations this rule *should* apply to.

For example, does it apply to typedefs:

using T = int;
module M;
using T = int;

? (Is that ill-formed because T is owned by the global module and by module M? Or is it valid because... something?)

And if we permit that, is that two different Ts (so any use of T is ambiguous) or a single T that resolves to 'int'?

Clearly this is intended to be valid:

namespace N {}
module M;
namespace N {}

... but what about this:

namespace N {}
namespace N2 = N;
module M;
namespace N2 = N;

? (Two ambiguous N2's, a single N2, or an error?)

I think it's clear that different owning modules for variables, functions, classes, enums, and templates should all be disallowed.

What happens if such a declaration is the subject of a friend declaration:

export module A;
export struct X {};
export void f();

module B;
import A;
struct Y { friend struct X; }; // ok?
struct Z { friend void f(); }; // ok?

Richard Smith

unread,
Oct 6, 2017, 4:46:58 PM10/6/17
to C++ Core Language Working Group, mod...@isocpp.org, Gabriel Dos Reis
What about this:

export module A;
export struct X { friend struct Y; }; // is Y exported?
struct Z;

module B;
import A;
void f(struct X); // presumably this is A's "struct X", because it's visible and tag lookup finds it
void f(struct Y); // is this A's "struct Y" or B's "struct Y"?
void f(struct Z); // presumably this is B's "struct Z", because A's "struct Z" is not exported

Gabriel Dos Reis

unread,
Oct 7, 2017, 12:13:01 PM10/7/17
to Richard Smith, C++ Core Language Working Group, mod...@isocpp.org

Just an FYI: I am not ignoring these questions.  I feel they are best addressed with an explanation of the ownership model in the background, so it is turning into a paper.

There are a few places where the TS uses ‘entity’ when it should have used ‘declaration’ – I believe some of your ballot comments capture that.

Reply all
Reply to author
Forward
0 new messages