[PATCH] Fix to PR8880 (clang dies processing a for loop).

Richard Smith richard at metafoo.co.uk
Wed Dec 11 14:03:06 PST 2013


On Wed, Dec 11, 2013 at 9:55 AM, Serge Pavlov <sepavloff at gmail.com> wrote:

>
> 2013/12/10 Richard Smith <richard at metafoo.co.uk>
>
>> @@ -1291,6 +1289,7 @@
>>    // See comments in ParseIfStatement for why we create a scope for the
>>    // condition and a new scope for substatement in C++.
>>    //
>> +  getCurScope()->AddFlags(Scope::BreakScope | Scope::ContinueScope);
>>    ParseScope InnerScope(this, Scope::DeclScope,
>>                          C99orCXX && Tok.isNot(tok::l_brace));
>>
>> @@ -1342,6 +1341,7 @@
>>
>>    // Pop the body scope if needed.
>>    InnerScope.Exit();
>> +  getCurScope()->RemoveFlags(Scope::BreakScope | Scope::ContinueScope);
>>
>> These changes look redundant: you're adding and removing the flags to the
>> outer scope for exactly the period where the outer scope is not active.
>>
>> The outer scope defines BreakParent and ContinueParent for the inner
> scope. Setting flags to the inner scope is not possible in some cases. If
> the loop body is represented by compound statement, inner scope is made by
> the latter and break/continue flags wouldn't be set.
>

OK, I see. In C89, declarations from within the controlled statement *are*
visible in the while condition, but 'break' and 'continue' affect the outer
loop, in hideous contortions like this:

  extern void *p;
  void f() {
    while(1) {
      do
        (struct S { int n; }*)0;
      while (
        ((struct S*)p)->n && // use struct definition from within loop
        ({ break; 0; }) // break out of containing 'while' loop
      );
    }
  }

... so we do need to RemoveFlags. Yuck.

+  assert ((Flags & ControlScope) != 0 || "Must be control scope");
>>
>> No space after 'assert'.
>>
>> Fixed.
>
>>
>> +void Scope::RemoveFlags(unsigned FlagsToClear) {
>> +  assert((FlagsToClear & ~(BreakScope | ContinueScope)) == 0 ||
>> +         "Unsupported scope flags");
>> +  if (FlagsToClear & BreakScope) {
>> +    assert((Flags & ControlScope) != 0 || "Must be control scope");
>> +    assert((Flags & BreakScope) != 0 || "Already cleared");
>> +    BreakParent = 0;
>> + }
>> +  if (FlagsToClear & ContinueScope) {
>> +    assert ((Flags & ControlScope) != 0 || "Must be control scope");
>> +    assert((Flags & ContinueScope) != 0 || "Already cleared");
>> +    ContinueParent = 0;
>> +  }
>> +  Flags &= ~FlagsToClear;
>> +}
>>
>> These are incorrect: the BreakParent and ContinueParent should be
>> inherited from the parent here, rather than set to zero. However, with the
>> first change above, RemoveFlags will be unused and can just be deleted.
>>
>> You're missing test cases for 'break' and 'continue' in the condition of
>> a do-while, which would have caught the above bug.
>>
>> Sadly but you are absolutely right. Thank you for the catch. I tried
> making better test coverage.
>
>>
>> On Mon, Dec 9, 2013 at 4:06 AM, Serge Pavlov <sepavloff at gmail.com> wrote:
>>
>>> If there is no objections, I'll commit this fix.
>>>
>>
>> See http://llvm.org/docs/DeveloperPolicy.html#code-reviews bullet 5: "Do
>> not assume silent approval".
>>
> Yes, sure. Please forgive me my impatience.
>
>
> --
> Thanks,
> --Serge
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131211/9ecfb819/attachment.html>


More information about the cfe-commits mailing list