ipr: Bodies of the mapping expressions (and Variable Templates)

IPR uses mapping expression to represent functions and templates. A mapping has a parameter list, type and a body.

The parameter_list of the mapping hosts a homogeneous scope/region that holds parameter declarations. Body of the function mapping is a block that takes an enclosing region (parameter_list scope/region) in its constructor.

Similarly, for a class template, the body of a mapping is an ipr::impl::Class that takes the enclosing region in its constructor.

For a function template, the body is a mapping expression for a function. Function mapping takes enclosing region in its constructor.

From the ipr interface, we have a couple of alternatives on how to represent the body of the mapping. Option 1 is to use the actual expression that will become the body/initializer of the entity after substitution. Option 2 is to create a full fledged declaration and to make it the body of the mapping.

Template Kind Option 1 Option 2
Function template Function Mapping Expr ipr::Fundecl
Class Template Class (Type) ipr::Typedecl
Alias Template Type ipr::Alias (Decl)
Variable Template ??? ipr::Var (Decl)

While Option 2 is possible from the perspective of ipr interface. It is cannot be created using ipr::impl as it exists today. To create a declaration, we have to have a heterogeneous scope in which to create it. We don’t have it. The enclosing scope of the body of the mapping is a homogenous parameter scope.

Option 1 is feasible for function, class and alias templates, but, does not have a node (that is not a declaration) that can represent the body of the mapping for a variable template.

Option 2 would need a resolution of where ipr::Fundecl, ipr::Typedecl, ipr::Alias or ipr::Var will be stored.

One alternative how option 2 can be addressed is to add a storage for declarations that can be bodies of templates (ipr::impl::Var at the minimum, or all four flavors) and host them in the master_decl_data<ipr::Template>

      template<>
      struct master_decl_data<ipr::Template>
         : basic_decl_data<ipr::Template>, overload_entry {
         using Base = basic_decl_data<ipr::Template>;
         // The declaration that is considered to be the definition.
         Optional<ipr::Template> def { };
         Optional<ipr::Linkage> langlinkage { };
         const ipr::Template* primary;
         const ipr::Region* home;

         // The overload set that contains this master declaration.  It
         // shall be set at the time the node for the master declaration
         // is created.
         impl::Overload* overload;

         // Sequence of specializations
         decl_sequence specs;

         // +++++++++++ ????????
         decl_factory<ipr::Var> vars;
         // ++++++++++++++????

         master_decl_data(impl::Overload*, const ipr::Type&);
      };

Option 2 is a bit wasteful. We do not need entire declaration machinery that deals with overloads and master declarations, since every declaration that is a body of the mapping is unique to that mapping, since it refers to parameters of that particular mapping.

Making Option 1 work for all template kinds needs some kind of node to represent a body of a variable template. Essentially a pair (type and initializer).

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Comments: 18 (17 by maintainers)

Most upvoted comments

On 2/25/2021 4:23 PM, Gabriel Dos Reis wrote:

Another question about Option 1 for variable templates is where
would we store arguments for partial and explicit specializations.

This question touches on another important point in the original design of the IPR that I now see I never explained in the IPR paper.

The IPR takes on generalization of a few concepts of Standard C++ and sought to cast them in a way that was more general.

That’s a key design idea - without that, we get lost in obscure language/WG21 details.

To name a few:

  1. Expressions, hence values, includes types; declarations are expressions
  2. Types, e.g. namespaces are UDT; function templates have types (something Standard C++ managed to define)
  3. Scopes are just sequence of declarations, not specifically indicating declarative regions;
  4. etc.

IPR decided to rely on types of expressions and declarations to account for various hacks in Standard C++, including the |struct stat| hack (a time-honored C hack from the old days) where by a function name can “hide” a type-name and you have to use an elaborated type-specifier (e.g. |struct stat|) to get the type – for example the function |stat()| returns a |struct stat*|. Using the concept of overload, the IPR declares that the name |stat| is overloaded, with an entry that has |class| as type, and another that has a function type. This is an important point in the design. Next, partial specializations and explicit specializations are now viewed as overloads; furthermore, if there ever was a proposal to add function template partial specializations (back in the late 1990s and early 2000s, that was a recurring request), the IPR was prepared.

The reason why that background is important is to understand how partial specializations – not just of variable templates, but of any templates – are to be represented. In the example we have:

  1. The primary template

|template <typename T, typename U> T var; |

has type |var: Forall(T: typename, U: typename) . T|.

  1. The partial partial specialization

|template <typename T> T var<T,int> = 4; |

has type |var: Forall(T: typename) . T|

  1. And the explicit specialization

|template <> int var<double,int> = 8; |

has type |var: Forall() . int|

They are all chained together in the overload set. That is how templates (and any other declarations) were to be represented. It was a much regular and system. Of course you would need some special rule (algorithms) projecting the barnacles of Standard C++ onto this general representation to get the result of your name lookup.

If we’re concerned with exactly mimicking the barnacles of Standard C++, we can keep the algebraic structure of the data structures and have dedicated names and accessors designed to reflect exactly those. But, the reliance on types to drive the design allow for simpler representations. We need to verify that the addition of variable templates (C++14) didn’t violate some of the hypothesis at the basis of the original design. And also, how partial ordering works in this scheme without too much trouble.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/GabrielDosReis/ipr/issues/122#issuecomment-786232287, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACTNGNDWUFVGVYNAKGCJ6BTTA25T5ANCNFSM4XOFVUJA.