C++ Logo

sg15

Advanced search

Re: [SG15] [isocpp-modules] Determining identity like #pragma once

From: Richard Smith <richardsmith_at_[hidden]>
Date: Wed, 10 Jul 2019 16:20:33 -0700
On Wed, Jul 10, 2019 at 4:03 PM Gabriel Dos Reis <gdr_at_[hidden]> wrote:

> On Jul 10, 2019, at 3:40 PM, Richard Smith <richardsmith_at_[hidden]>
> wrote:
>
> On Wed, Jul 10, 2019 at 3:15 PM Michael Spencer <bigcheesegs_at_[hidden]>
> wrote:
>
>> On Wed, Jul 10, 2019 at 2:51 PM Gabriel Dos Reis via SG15 <
>> sg15_at_[hidden]> wrote:
>>
>>>
>>>
>>> | -----Original Message-----
>>> | From: Bryce Adelstein Lelbach aka wash <brycelelbach_at_[hidden]>
>>> | Sent: Wednesday, July 10, 2019 2:37 PM
>>> | To: Gabriel Dos Reis <gdr_at_[hidden]>
>>> | Cc: Tom Honermann <tom_at_[hidden]>; modules_at_[hidden];
>>> | sg15_at_[hidden]
>>> | Subject: Re: [isocpp-modules] [SG15] Determining identity like #pragma
>>> once
>>> |
>>> | On Wed, Jul 10, 2019 at 11:02 AM Gabriel Dos Reis <gdr_at_[hidden]>
>>> | wrote:
>>> | >
>>> | > You are implying that the include translation is mandatory. Is that
>>> your
>>> | argument?
>>> |
>>> | It is my belief that include translation is mandatory.
>>> |
>>> | "If the header identified by the header-name denotes an importable
>>> | header ([module.import]), the preprocessing directive is instead
>>> | replaced by the preprocessing-tokens: import header-name ;"
>>> |
>>> https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Feel.is
>>> <http://eel.is>
>>> | %2Fc%2B%2Bdraft%2Fcpp.include%237&amp;data=02%7C01%7Cgdr%40mic
>>> | rosoft.com
>>> <https://nam06.safelinks.protection.outlook.com/?url=http%3A%2F%2Frosoft.com&data=02%7C01%7Cgdr%40microsoft.com%7C65c60a7cb411418a573c08d705879d5b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636983952331426276&sdata=CVxYgPI7ql6HcosoLYyLX3v%2B3JVvy1LBW4uHO2RMNEE%3D&reserved=0>
>>> %7C57adb75b608743ebf6e208d7057ed5d4%7C72f988bf86f141af
>>> | 91ab2d7cd011db47%7C1%7C0%7C636983914618863287&amp;sdata=JsvGf
>>> | 0RfvcjOSMF6q%2FfsW60hhKx5TI%2F1K42M8PdRbOU%3D&amp;reserved=0
>>> |
>>> | To me, that reads as "including an importable header must be
>>> | translated to an import".
>>> |
>>> | Was that the original design intent?
>>>
>>> The original intent, as I understood it, was to *permit* include
>>> translation.
>>>
>>> A mandatory include translation is practically unworkable for the MSVC
>>> audience. I wouldn't be surprised at all if that is the case of other
>>> compilers too.
>>>
>>> A permission, as opposed to a requirement, would allow adoption of
>>> header import as better and principled replacement of PCH. Otherwise, it
>>> would create more problems than it solves.
>>>
>>> -- Gaby
>>>
>>>
>> I was there for most of the LEWG discussion on D1502R0 (Standard library
>> header units for C++20), and I can not say with confidence that the room
>> understood the implication that this would require include translation.
>> There don't seem to be any minutes of the discussion on the wiki.
>>
>> However, the paper does explicitly point this out:
>>
>> > This has a number of consequences, such as:
>> > * ...
>> > * Existing #includes of standard library headers transparently turn
>> into module imports in C++20
>>
>> It also in the wording explicitly adds the headers to the set of
>> implementation-defined importable headers.
>>
>> > An importable header is a member of an implementation-defined set of
>> headers that includes all importable C++ library headers (16.5.1.2
>> [headers])
>>
>> So I believe the author's intent was to require that include translation
>> occurs.
>>
>> + Richard so we can ask them :)
>>
>
> Yes, my intent was to (effectively) require include translation for these
> parts of the standard library.
>
>
> Thanks for clarifying the intent.
>
> In practice, an implementation could satisfy that requirement by instead
> providing standard library wrapper headers that simply contain a relevant
> import declaration -- this does not strictly require an implementation to
> actually do include translation during preprocessing.
>
>
> That is indeed one possible conforming workaround. Another is what MSVC
> has been doing, but I would like to proceed differently with a more
> standards-blessed technique.
>
>
> An implementation that doesn't actually do include translation at all is
> still permissible using this technique. Such an implementation would need
> to document that its implementation-defined set of importable headers is
> empty other than the standard library headers. If they did so, then any
> "import header-name;" construct that didn't name a standard library header
> would violate [module.import]/5's "H shall identify an importable header."
> Such an implementation could still choose to accept such an import as a
> language extension. I would not encourage such an approach, but I think
> it's consistent with the wording we have.
>
>
> The problem with mandatory include translation is that it precludes
> existing conforming implementation techniques of headers where certain
> parts are driven from the outside by macros. Those techniques are
> necessary for incremental adoption.
>
>
> If we want to permit "import header-name;" without requiring include
> translation, and we want to allow re-exporting of imported header units,
> we'll need to fix the robustness issues that arise when a legacy
> header's macros (particularly include guards) are divorced from its
> semantic contents. (Nathan pointed this out during prior EWG discussion,
> and this non-robustness is the reason we require include translation for
> importable headers.) For example:
>
> // foo.h
> #fndef FOO_H
> #define FOO_H
> struct X {};
> #endif
>
> // TU A
> export module M;
> export import "foo.h";
>
> // TU B
> import M;
> // suppose we make a language change that allows this #include to be
> textually included rather than subject to include translation
> // then because the FOO_H macro is not visible here (because modules can't
> export macros), we get...
> #include "foo.h" // error, redefinition of X
>
> The same issue arises with the global module fragment:
>
> // TU C
> module;
> #include "foo.h"
> export module M;
> export X f(); // definition of X is reachable through this TU
>
> // TU D
> import M;
> #include "foo.h" // error, redefinition of X
>
> I think there's a reasonable fix to both of these; instead of rejecting a
> redefinition when a prior definition is reachable, we should only reject a
> redefinition of an entity attached to the global module when there's a
> prior definition in the same translation unit. The ODR would apply as
> normal to such global-module-owned redefinitions. (I think I've suggested
> this on either the evolution or modules reflector before.)
>
>
> That roughly matches what MSVC has been doing for the last 3 years (in
> addition to fabricating a hash based on the content of the header for
> “include guard” purposes.)
>
> I am supportive of exploring this path of bugfix for C++20.
>

OK! Shall we plan to coauthor a paper for the post-Cologne mailing, with
the intent of filing an NB comment to make this change, or failing that,
trying to resolve it as a DR? (We could also see if Ville is prepared to
lend us some EWG time at Cologne, but I wouldn't bet on it.)

> If we make that change, both the global module fragment and header units
> would become more robust, and we would no longer need to require include
> translation for all importable headers. (I think we should still require
> include translation for the standard library, though as noted above, an
> implementation can model that by providing header files that contain
> imports.)
>
>
> And that would help with adoption of header imports as better replacement
> of PCHs.
>
> — Gaby
>
>

Received on 2019-07-10 18:22:41