Cling: Dynamic Scopes

The Problem

The objects from the current directory (gDirectory) are injected into a magic scope that is available to CINT:

Important constrain: To not break old code we need to continue to support this. Of course the code is invalid C++ and thus cannot be parsed by clang. Upon clang’s Sema complaining about undeclared identifiers we need to convert it into proper C++.

Use Cases

1. Simple use case:

 void macro() {
   TFile::Open("f.root");
   hpx->Draw();
 }

2. Parameters

void macro() {
  TFile::Open("f.root");
  int i = 12;
  obj->Func(i);
}

3. Function Overloading

An additional complication is function overload resolution required in this example:

  void Klass::Func(TTree*);
  void NameSpace::Func(TH1*);
  void Func(TObject*);
  using namespace NameSpace;

  void Klass::Call() {
    TFile::Open("f.root");
    int i = 12;
    Func(obj);
  }

N. Can we have smth else?

Solutions

Escaping to Late Evaluation

In use case 1: the code can be converted to:

 void macro() {
   TFile::Open("f.root");
   Cling::Interpreter().Eval("hpx->Draw()");
 }

This is now valid C++; the code hpx->Draw() will be evaluated during runtime, when we know all the necessary information. In this case we have:

In use case number 2: we need to pass the variable to the interpreter as well. We need to introduce a Context

  void macro() {
    TFile::Open("f.root");
    int i = 12;
    {
      Cling::InterpreterContext ctx;
      ctx.add("i", &i);
      Cling::Interpreter().Eval("obj->Func(i);", ctx);
    }
  }

In use case number 3: we don’t know the type of obj at compile time, we don’t know which overload of Func() to use.

Here we have several options:

  • Let Cling decide which overload to use - We could pass the list of candidates that Sema found during overload resolution to the escaped interpreter, probably including a copy of the identifier table. This would require changes in Sema, because the escaped Sema needs to take an external identifier table and the candidates into account;
  • Assume void* - One could argue that this is a rare case. We know that unknown identifiers have to be pointers; we could simply select the void* overload of Func(), i.e. claim that obj is a void*;
  • Refuse the overloads - If we have problems determining the proper overload. By refusing to accept multiple overloads we can simplify the situation;
  • Discontinue Support - We could detect the case of multiple overload candidates and issue an error. This should be in case no other options are possible.

The page is based on ClingDynamicScope at tWiki