Cling's Dynamic Scope

The Problem

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

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

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++.

Escaping to Late Evaluation

We can convert above code to

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

This is now valid C++; the code hpx->Draw() will be evaluated at a later time.

Communication With First Pass Binary

Variables

Variables can be passed to the escaped code:

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

Here, a reference to "i" needs to be passed to the escaped code, like we do for CINT with

sprintf("*(int*)(0x%x) = func()", &i)

Context

A possible solution is the introduction of a context. Above code would get converted to

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

Function Calls

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);
}

As we don't know the type of obj at compile time, we don't know which overload of Func() to use. We also cannot escape it to the interpreter:

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

void Klass::Call() {
  TFile::Open("f.root");
  Cling::Interpreter().Eval("Func(obj)");
}

because Func() might be a member function of Klass - i.e. the lookup context might be different for the escaped Sema.

Option 1: 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*.

Option 2: Discontinue Support

We could detect the case of multiple overload candidates and issue an error.

Option 3: Pass Candidate List

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.

Option 4: Only Allow One Candidate

We have problems determining the proper overload. By refusing to accept multiple overloads we can simplify the situation.

// NOT supported:
void Func(TTree*);
void Func(TH1*);
void t() { Func(T); }

Instead, this can be converted to

// NOT supported:
void Func(TH1*);
void t() { Func(T); }

// YES supported:
void Func(TH1*);
void t() { Func(Cling::Interpreter().EvalExpression<TH1*>("T")); }

Option 5: Parse and Execute Line By Line

Split the code into separate lines, and interpret them line by line.

class X {
private:
  void impl(TH1*);
  void impl(TTree*);
public:
  void GetSomething() {  impl(T); }
};

would become

class X {
private:
  void impl(TH1*);
  void impl(TTree*);
public:
  void GetSomething() {  Cling::Interpreter().Eval("impl(T)"); }
};

But here, too, we need to pass the lookup context: Cling::Interpreter().Eval() needs to know that "we are in X".

Question

I think that we need to distinct 3 cases:

  1. Trivial - we have unknown member invocation with know parameter types, i.e. the type can be determined during compile time
  2. Mixed - we have unknown and known parameter types during compilation. Is it possible to evaluate the correct binding only with the known types?
  3. All unknown - Can we use reflection or other metadata?

Expressions Versus Statements

The amount of code sent to the delayed Sema needs to be defined. We obviously need to pass everything that depends on the type of the undeclared variable:

class TH1;
void Func() {
  TFile::Open("f.root");

  // 1
  hpx->Draw();

  // 2
  TH1* h = hpx;

  // 3
  if (!*hpx) {}

  // 4
  for (int i = 0; i < hpx->GetEntries(); ++i) {}
}

will need to become

class TH1;
void Func() {
  TFile::Open("f.root");

  // 1
  Cling::Interpreter().Eval("hpx->Draw()");

  // 2
  TH1* h;
  {
    Cling::InterpreterContext ctx;
    ctx.add("h", &h);
    Cling::Interpreter().Eval("h = hpx;", ctx);
  }

  // 3
  // We know we want "bool" here - do we always know the type of the expression?
  // We need to move the whole expression to late binding, because typeof(*hpx) might implement operator!()
  if (Cling::Interpreter().EvalExpression<bool>("!*hpx")) {}

  // 4
  // We need to escape the comparison because there might be an
  //    operator < (int, typeof(hpx->GetEntries())
  // that the unescaped Sema isn't aware of.
  // Note also how awkward the creation of the context object is in this case...
  for (int i = 0; Cling::Interpreter().EvalExpression<bool>("i < hpx->GetEntries()", Cling::InterpreterContext().Add("i", i)); ++i) {}
}

Other question and reminders

  • How we deal with side effects of the functions?
    class TH1;
             void Func() {
             TFile::Open("f.root");
              hpx->toBeInterpreted();
              ///Declaration of toBeInterpreted:
              void toBeInterpreted() {
                  SomeCompilerClass.SomeGlobalVar++;//Class, which is compiled by the compiler
              }
          
    How are we going to analyze this and determine what to put in the context, because obviously we should put this address. Do we have to use the brute-force way. Export the whole compile-time context or there is more efficient solution?
    Answer: We don't need to do special treatment here: toBeInterpreted() is compiled and will thus have its globals already resolved.
  • When we say "compiler" or "lookup context": can we actually capture it from clang? clang's (Sema | Lexer | ...) context is partially encoded in the function call stack.

-- AxelNaumann - 11-Aug-2010

Edit | Attach | Watch | Print version | History: r7 < r6 < r5 < r4 < r3 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r7 - 2010-08-31 - AxelNaumann
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    Main All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback