Cling & CINT parse issue?

If I copy & paste the following code into the ROOT6 interpreter:

*  ROOT v6.02/00-rc1  *

double x[10] = {0};
for(unsigned int i = 0; i < 10; i++)
  {
    x[i] = i*i*i;
  }

I get the following errors:

root [0] double x[10] = {0};
root [1] for(unsigned int i = 0; i < 10; i++)
root [2]   {
root (cont'ed, cancel with .@) [3]    x[i] = i*i*i;
root (cont'ed, cancel with .@) [4]  }
ROOT_prompt_3:1:2: error: array subscript is not an integer
x[i] = i*i*i;
 ^~
ROOT_prompt_3:1:8: error: use of undeclared identifier 'i'
x[i] = i*i*i;
       ^
ROOT_prompt_3:1:10: error: use of undeclared identifier 'i'
x[i] = i*i*i;
         ^
ROOT_prompt_3:1:12: error: use of undeclared identifier 'i'
x[i] = i*i*i;
           ^

It still happens with a few alterations like using signed int, or different bracket style. In ROOT5, I get no error but the array is unchanged:

*  ROOT v5.34/18  *

root [0] 
root [0] double x[10] = {0};
root [1] for(unsigned int i = 0; i < 10; i++)
root [2]   {
end with '}', '@':abort >     x[i] = i*i*i; 
end with '}', '@':abort >   }
(double)1.00000000000000000e+03
root [3] x[9]
(double)0.00000000000000000e+00

I’m pretty sure I’m not doing some newbie mistake, as the expected thing happens in ideone: http://ideone.com/ffFWDs.

So what’s going on?

Jean-François

1 Like

Hi Jean-François,

cling is processing the line with the for loop and then the scope within curly braces.
I put an item in our bug tracker: sft.its.cern.ch/jira/browse/ROOT-6813
Meanwhile, you can workaround this slightly changing your code:
{code}

for(unsigned int i = 0; i < 10; i++){
x[i] = iii;
}

{code}

Oh that’s terrible…

I sort of expect this for CINT, which doesn’t claim to be a full C++ interpreter, but I thought part of the big idea with ROOT6 was that it would really compile C++ code.

I understand the convenience of not needing the semicolons, so end-of-line in the interpreter is seem as an end of statement, but honestly the disconnect between stuff you type into the ROOT prompt and your compiled macros is a huge hurdle for people learning ROOT. I really thought this would go away with ROOT6.

Is it possible for ROOT6 to understand that the block of code is copied & pasted, and tread the in-a-pasted-block \n differently than a real “I pushed enter” EOL character?

Jean-François

Same problem here: New to cling: should I report this as a bug?

Hi,

Let me give you a bit of background; I hope that will explain.

Any C++ interpreter is not a compiler: compilers cannot deal with

// MyCode.cxx
int  sum = 0;
printf("sum before: %d\n", sum);
for (int i = 0; i < 12; ++i) {
  printf("%d\n", i);
  sum += i;
}
sum

It can’t because

  • printf is not declared (okay, details)
  • that for loop is not legal on file scope.
  • “sum”?!

So yes, we do have a few extensions - already making C++ code available to the compiler line by line is a huge extension: a compiler expects to see the “full thing” (which is so crucial that it even has its own name: the translation unit) in one go, and does certain operations only after having seen the whole translation unit.

We wrap expressions into functions to make them executable. This converts

printf("sum before: %d\n", sum);

into something similar to

void f() {
  printf("sum before: %d\n", sum);
;
}

We then just-in-time compile f() and call it. There are a few tricks we need to play - e.g. naively the declaration of “int sum” would not be visible outside that wrapper function.

As you know, “sum” means “and print the value of sum”. But that’s of course ill formed:

void f() {
  sum
}

so we just paste an extra ‘;’ after each input line; if the input ends with an empty statement like such:

void f() {
  sum;
; // this is an empty statement
}

then we don’t print the value of sum, else we do. Now you see the connection of why that for-loop continuation breaks: we convert it into

void f() {
  for(unsigned int i = 0; i < 10; i++)
;
}

and that’s perfectly legal code, no continuation needed. Indeed it’s a cling bug. If you have an idea how to detect / fix / circumvent this then let us know!

Note that the ‘;’ pasting happens before parsing, i.e. we do not know yet that we have a for loop - so while we could paste the ‘;’ conditionally based on the previous statement, we would have to do a string analysis of the input - which is pretty terrible and fragile; think of someone using a do-while loop, or the C++17 range-based for coming in, or fancy nested templates in the for expressions that disguise as comparisons, or preprocessor macros getting involved.

Sorry for the long post - but now we are all on the same page and we might hear your ideas to find a solution!

Cheers, Axel.

Hi Axel and thank you for your great work on Cling!

I understand some things are difficult when you are trying to make C++ (pseudo-?)interpretable. But I suppose if you remove the auto-semicolon-pasting thingy, then the input routine will recognize that for(…) on its own cannot be a full statement and will wait for continuation in the next line just like when I end the for line with {. Why is this auto-semicolon behaviour there in the first place? For stuff like simple expressions, when the user may just want to see the value of the expression, it’s fine, but appending ; to a control structure like for? Seriously?

Apart from that, I’d suggest that you remove the claim that “Cling can parse anything Clang can” or at least qualify it like “with a few restrictions/exceptions” and add a link to a page or such explaining what the exceptions are and why. That way you’d probably be saving yourself from having to explain stuff like this repeatedly on the forum and users from asking in the first place.

But still the auto-semicolon on control statements is really un-C+±ish. If the user is running the for loop simple for the side effects of evaluating the expressions in the ;; clauses, then s/he should manually end the line with a semicolon, IMO, and the REPL should not insert one automatically.

But that’s just my off-the-(nonexistent-)hat thinking and there may be other problems with this (for instance I don’t entirely grok where you are going with all that do-while/C++17 stuff).

I think I backup the idea of dropping the “automatic extra ; after each line” feature completely.

With CINT, there exist at least two additional ways to print a value of an “expression” (try “.?” on the ROOT/CINT prompt):

Evaluation: p [expr] : evaluate expression (no declaration/loop/condition)
{[statements]} : evaluate statement (any kind)

So, if I want to “dump” some variable, I can simply:
root [0] int i = -1;
root [1] .p i
(int)(-1)
root [2] .{i;}
(int)(-1)

Would it be possible to add this to CLING?

Well, I’ve got an additional idea.
How about you implement TWO “modes” in CLING.
One “mode” called the “calculator mode” which would behave exactly like now (and like the CINT behaves, too), so that the “result” of a line without explicit “;” would be printed.
And another “mode” called “strict c++ interpreter mode” and in this “mode” the “automatic extra ; after each line” feature would be gone (one would need to use “.p” or “.{” in order to print values of “expressions”). Well, in this “mode”, possibly also another nonstandard “calculator mode” c++ features should be blocked.
Switching between these modes could be implemented in form of an additional CLING “dot command”.

Hi,

Thanks for your ideas; could be done. The suggestions so far have two major drawbacks:

  • People writing “sum” will not see anymore what they expect - they will claim ROOT is broken.
  • A solution that requires extra work / knowledge from the users instead of fixing this internally is really only a last resort; it’s close to a solution called “giving up” :wink:

Note that we already have .rawInput - but that won’t help you here because you cannot have a for loop on file scope, which is where .rawInput puts things - it disables all interpreter smartness.

So I think we haven’t found the optimal solution yet. I figured we can detect

for (;;)
;

after parsing - we know that the ‘;’ was patched by us behind an otherwise empty for loop, so we could react to that and somehow require continuation. But it’s rather late in the input processing. I’ll continue to think about it.

Cheers, Axel.

Hello Axel. I understand that if you totally remove it then existing users may complain. But Coyote has made the suggestion that it be a switchable parsing mode, which seems a reasonable compromise in my opinion. But, see below:

I recognize that for loops are not valid at file scope in the first place. But the point is, we are coming to Cling only because it is providing some extra functionality than Clang such as allowing us to write such code which is not parseable by Clang. I just verified that within a function we are indeed allowed to put the printf in a separate line from for and it isn’t an error. So I guess the semicolon-insertion is happening outside function scope only, and causes different behaviour in this case of for loop.

It’s just that we are expecting to be able to do the same line breakage (as within functions) outside function scope too. I think it is safe to say that most of the time (at least in what I understand to be well-written code) one does not run an empty (i.e. nothing in {}) for loop for the side-effects of the expressions within. So you still should consider not adding ; after a mere for (or other similar cases).

BTW I now realize you haven’t really broken your promise that Cling can parse anything that Clang can. It’s only in the parts which Cling can parse that Clang cannot that there are quirks. At least the quirks should be documented IMO…

Thanks again for Cling!

I think I would appreciate a “strict” mode for cling which would require me to do additional typing to print values and explicit semicolons after statements, if it still allows “file-scope” expressions. It could still support the CINT “.p foo” magic for printing values, since that’s not valid C++ anyways.

My user perspective is that my cling commands aren’t at file scope, but rather inside the main() function. So having a for loop isn’t so incongruous in my eyes. The fact that you wrap cling statements inside little functions does explain why you can’t define functions on-the-fly in cling.

Thanks for the explanations, and thanks for providing cling, it is indeed much better than CINT! We just want to make it even better!

Jean-François