Skip to content

Implementation of Templates in the CPG #319

Description

@vfsrfs

The purpose of this issue is to document the decisions @konradweiss and I made on how to implement CPP Templates.

The implementation of the CPP Templates will be split up into two parts: A first representation of the Template in the Graph and a Pass that refines the Template usage, with the same approach used by the CPP Compiler. The main reason for choosing this approach is the fact, that an extensive resolution of the templates leads to a more refined graph, but it might also increase the size of the graph considerably. Therefore, the refinement is implemented as a pass, in order to make the refinement optional if the graph size is too big.

Java Generics can also be modelled as a subset of CPP Templates and therefore the current Java Generics representation will be modified to be consistent with the CPP Templates approach. The main differences are:

  1. Java Generics can only be applied on Classes, whereas CPP Templates can be applied on Classes and Functions

  2. In Java the parameters of the classes can only be Types, compared to CPP Templates, where values can also be provided to the Template.

Representation

To explain how Templates will be represented in the graph, we will be using the following snippet as an example. This snippet represent a template applied on a function, but this is sufficient as applying templates to classes is very similar.

Code:

#include <iostream>
using namespace std;

template <class T, int N>
T fixed_multiply (T val)
{
  return val * N;
}

int main() {
  std::cout << fixed_multiply<int,2>(10) << '\n';
  std::cout << fixed_multiply<float,3>(10.0) << '\n';
}

Graph:
initial

As we can see, the graph now includes a new node called Template (exact naming is open for discussion) containing three outgoing edges. The first one is the realization edge to the function, which is implemented by the template. The second and third edges are parameters-Edges pointing towards the two parameters of the template. The first parameter is a ParameterizedType as introduced by #317 and defined in #293. This represents the abstract Type T and contains two edges two both types it has been initialized in the code (int and float). The second argument is a ConstantDeclaration, as the values passed to Templates must be known at compile time and therefore they must be constant. This node also contains possibleInitialization-Edges to the Literals that have been found in the initialization of the Template.

Refinement Pass

As explained before, a more refined graph has a bigger size, since every usage of the Template leads to a custom function/class. However, a more refined graph is useful for the analysis of the code, since it may enable us to have a more exact resolution of the code (e.g. exact resolution of a call is possible since we now have type information).

Therefore, this pass modifies the graph and 'realizes' every usage of the Template following the same approach as the CPP Compiler.

Graph:
pass

As we can see in the graph above, now the template contains two FunctionDeclarations (of the same function) with the different realizations, and each CallExpression invokes the correct realized FunctionDeclaration. Additionally, the FunctionDeclarations now contain pointer to the exact values that were used to realize the template.

Metadata

Metadata

Assignees

Labels

discussionLabel for issues or future features to be discussed before putting them onto the road-mapenhancementNew feature or requestgraph-changingThis change breaks the current graph structure, i.e. it changes semantic of properties and edges

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions