phase 8

219 views
Skip to first unread message

James Widman

unread,
Mar 3, 2014, 2:18:32 PM3/3/14
to mod...@isocpp.org, Bjarne.S...@morganstanley.com, Gabriel Dos Reis
[Keeping the CC line from before since I don’t know whether the subscriptions went through yet.]

Re phase 8, and assuming we wind up with import rules that say that an import-directive cause the importation of names/entities of the indicated TU as if it had just completed phases 7…

Has anyone considered a design where a use of a specialization (of a class template or function template) implicitly causes the importation of an Instantiation Unit (IU) where the only names visible are those that would be brought in by an import-directive that names each of the modules that defines either the template or one of the types/values/templates used as a template argument?

E.g., if module A defines class template A, and if B imports A and then uses A<int>, then that use would implicitly import the IU for module A. Names from B would not be visible during that separate translation.

The point is that each specialization would by implicitly declared in exactly one place, and if its definition is required, then it would be instantiated in an environment where the set of declared names is given by the specialization name itself (and therefore predictable).

To some extent, there would be no need for ODR checking, because (in a fully-modularized program) A<int> would be instantiated exactly once during translation for the entire program—instead of once per TU where its definition is required.

So, if Clang was doing this, it would reach A<int>, find that it had no type entry for A<int>, then behave as if a separate compiler instance had processed the forward-declaration of that type, and then allow translation to continue from the point of use (this time with type information for A<int> generated from the sub-translation).

And unless the definition of A changes, the post-phase-8 translation of the instantiated class A<int> could be used in subsequent builds by reading from a serialized AST (without instantiating) for any TU that needs A<int> to be defined.

This differs from the initially-assumed model where the TU that uses A<int> would generate a type node for that class, and then if necessary, perform an instantiation (which might need to be merged with duplicates from other TUs later on).

—James


Richard Smith

unread,
Mar 3, 2014, 2:52:10 PM3/3/14
to mod...@isocpp.org, Bjarne.S...@morganstanley.com, Gabriel Dos Reis
On 3 March 2014 11:18, James Widman <james....@gmail.com> wrote:
[Keeping the CC line from before since I don't know whether the subscriptions went through yet.]

Re phase 8, and assuming we wind up with import rules that say that an import-directive cause the importation of names/entities of the indicated TU as if it had just completed phases 7...


Has anyone considered a design where a use of a specialization (of a class template or function template)  implicitly causes the importation of an Instantiation Unit (IU) where the only names visible are those that would be brought in by an import-directive that names each of the modules that defines either the template or one of the types/values/templates used as a template argument?

E.g., if module A defines class template A, and if B imports A and then uses A<int>, then that use would implicitly import the IU for module A.  Names from B would not be visible during that separate translation.

The point is that each specialization would by implicitly declared in exactly one place, and if its definition is required, then it would be instantiated in an environment where the set of declared names is given by the specialization name itself (and therefore predictable).

To some extent, there would be no need for ODR checking, because (in a fully-modularized program) A<int> would be instantiated exactly once during translation for the entire program--instead of once per TU where its definition is required.


So, if Clang was doing this, it would reach A<int>, find that it had no type entry for A<int>, then behave as if a separate compiler instance had processed the forward-declaration of that type, and then allow translation to continue from the point of use (this time with type information for A<int> generated from the sub-translation).

And unless the definition of A changes, the post-phase-8 translation of the instantiated class A<int> could be used in subsequent builds by reading from a serialized AST (without instantiating) for any TU that needs A<int> to be defined.

This differs from the initially-assumed model where the TU that uses A<int> would generate a type node for that class, and then if necessary, perform an instantiation (which might need to be merged with duplicates from other TUs later on).

In Clang's implementation, we already have to deal with a closely-related question: which names should be visible during argument-dependent name lookup during a template instantiation? There are two "obvious" options:
 * All names that are found through any associated class or namespace, whether or not they are visible
 * All names that are visible at the point of instantiation.

The first option is fragile, since it depends on which modules happen to be "known" to the implementation at the point of instantiation. The second option is broken, because it means that a template cannot refer to its *own* implementation details through argument-dependent name lookup.

More generally, a template X can be passed a type Y from a context Z, and declarations that Z made visible can allow X to operate on Y (and this happens transitively when templates trigger the instantiation of other templates). So Clang's implementation extends the approach described in [temp.point] to handle this:

[temp.point]p1 and p4 define the point of instantiation for recursive template instantiations as follows:
 1) We start from a template definition for a specialization that is being instantiated.
 2) We walk to the point at which the instantiation of that specialization was required.
 3) If that instantiation was referenced from a context that depends on a template parameter, goto 2, otherwise stop.
The place where we stop determines our point of instantiation. However, this process also gives us a sequence of locations (potentially spanning multiple modules) which, for convenience, I will call the path of instantiation. This is easily recognized as the path that implementations report in their diagnostics when producing a diagnostic during a template instantiation.

Clang's implementation says that the set of names found by ADL are those names that are both (a) in an associated namespace, and (b) either visible at the point of instantiation or visible at the end of any module in the path of instantiation.

I claim this is, in some sense, both necessary and sufficient: it is exactly the set of declarations that the authors of the relevant templates and template specializations have guaranteed will be available to their instantiations.


Note in particular that the names visible at the point of instantiation *are* considered by this lookup, but for your case of A<int>, argument-dependent lookups are unlikely to find interesting results due to this because the template argument 'int' doesn't have any associated namespaces.
Reply all
Reply to author
Forward
0 new messages