BUG: Too many unnested loops incorrectly causes a C1061 compiler error in Visual C++

Applies to: Visual Studio 2010 ProfessionalVisual Studio 2010 PremiumVisual Studio 2010 Ultimate More

Symptoms


If a function contains more than approximately 250 unnested loops, you receive the following error message, which should not appear in this scenario:
"fatal error C1061: compiler limit : blocks nested too deeply"
The problem occurs only when you compile the source code as a C++ source file. The source code compiles without errors when you compile it as a C source file.

Status


Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

Steps to Reproduce the Behavior

The following sample code demonstrates the error:
/* Compile options needed: /TP /c
*/
#include <stdio.h>

// The code blocks in this function have only two nesting levels.
// C1061 should not occur.
void func1()
{
int a;
int i = 0;
int count = 0;

count++;
a = count;
for (i=0; i<5; i++) {
a += a*i;
}
printf("a=%d\n", a);

// Copy and paste the following code 250 times.
/*
for (i=0; i<5; i++) {
a += a*i;
}
printf("a=%d\n", a);
*/

count++;
a = count;
for (i=0; i<5; i++) {
a += a*i;
}
printf("a=%d\n", a);
}

void main(){
func1();
}

Workaround


Surround each for loop with braces to create an enclosing scope:
    {
for (i=0; i<5; i++) {
a += a*i;
}
}

More Information


The C++ compiler must keep track of all for-loops in the same scope in order to issue warning C4258 when /Zc:forScope is enabled:
int i;
void foo()
{
for (int i = 0; i < 10; ++i)
{
if (i == 5) break
}
if (i == 5) ... // C4258: this 'i' comes from an outer scope, not the for-loop
}
With /Zc:ForScope disabled, that ‘i’ on the commented line would come from the for-loop instead. Users must be informed of this difference in behavior.

Unfortunately, scopes are non-trivial data structures, so the compiler has a limit on the number of scopes it can keep track of at the same time, which leads to the bug described here.

The workaround solves this problem by isolating each for-loop so that it is no longer in the same scope as other for-loops, thereby removing the compiler’s need to keep track of all of their scopes at the same time. Another way of looking at the workaround is that it avoids the possibility of encountering warning C4258 outside an extra enclosing scope:
int i;
void foo()
{
{
for (int i = 0; i < 10; ++i)
{
if (i == 5) break;
}
}
if (i == 5) ... // this 'i' comes from the outer scope regardless of whether /Zc:forScope is enabled
}