Static objects imported via using directives in lambdas cause compilation issues!


I am in the habit of writing little test programs every now and then. Lambdas are perhaps the sort of thing that makes this habit worse. So, on I went, writing this demo little code to dump the contents of my rather complex container (for the purposes of this post I’ll use an array) on to the console.

#include
#include

int main() {
using namespace std;
int a[] = { 1, 2, 3 };
for_each(&a[0], &a[0] + 3, [](int i){ cout << i << “\n”; });
}

I was happily using Visual Studio 2010 up to this point when my compiler complained, loudly, about that `cout` and refused to proceed.

error C2065: ‘foo’ : undeclared identifier

At this point, I was sure I didn’t really understand lambdas as well as I thought I did. So, it was time to test the lambda in isolation and I added a line to the above:

#include
#include

int main() {
using namespace std;
 []{ cout << “Hello, world!\n”; }(); /* define and invoke */
}

This pretty little thing compiled just fine! Now, I had two problems: 1) I had to fix a bug (either in my code or prove the compiler wrong) and 2) I had to find why the second worked. I had now no other option but to refer to ‘The Standard’ (the draft, at least) and wade through some extremely terse and incredibly confusing (to me, to ‘The Committee’s’ defense) jargon.
Beginning with Lambda expressions I had a good start with §5.1.2/3:

[…]The closure type is declared in the smallest block scope, class scope, or namespace scope
that contains the corresponding lambda-expression. [ Note: this determines the set of namespaces and classes associated with the closure type (3.4.2). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. —end note ]

Translation: Think of lambdas as anonymous function objects. The scope of these unnamed (identifiers are for dummies) function objects happens to be the nearest scope delimited by ‘{‘and ‘}’ such as a function definition.
For us, this means that the lambda containing the `cout` is ‘as-if’ declared in `main`, after the using directive. But lambdas are supposed to capture stuff, aren’t they? Mostly, except for when an empty capture list — `[]` — is provided. Reason: `cout` is a static object and can’t be captured as per §5.1.2/3:

[…][E]ach such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression.[…]

To double check this was an issue with all such static objects imported in scope via the using directive I made a third little program:

#include
#include

namespace foo {
static int bar;
}

int main() {
using namespace foo;
using namespace std;
int a[] = { 1, 2, 3 };
for_each(&a[0], &a[0] + 3, [](int /*dummy*/){ bar = bar; });
}
 

Voila! VC10 hits the exact same error. We have a reproduction and understanding of the issue.

Naturally – having found a bug in one compiler and armed with The Standard’s knowledge – it was time to go compiler-shopping. This is where things get even more interesting:

G++ 4.5.1: OK | Visual Studio 2011 beta: OK | Comeau 4.3.10.1: Error! |Clang 3.0: Error!

With a bit of trimming I was able to boil my code down to nothing but a no-op lambda. This had to work on any system supporting lambdas. Off I went at everything I could lay my hands on and bam! Both Comeau and clang grumble with my empty-lambda-test:

int main() {
 []{}();
}

Time to file some bugs! Yay!

Update:

  • Filed a bug with MS
  • Dug up an exact same bug filed with clang. Looks like lambdas will be supported in 3.1. However, the sate of 3.0 (accessible through the browser is confusing!)
  • Mailed the Comeau guys

Update #2:

The compiler guys are fantastic! I received replies from all parties mentioned above (and a fix too from none other than MS!). Here’s a mini-update:

  • Clang issue #11359: David Blaikie was awesome! He wrote back in less than ten minutes:

    LLVM/Clang basically uses a “fire and forget” release process, so no fixes for
    lambdas have or will be backported to 3.0. It was unfortunate we released without
    Eli’s fix, but that’s how it went. In 3.1 lots of good lambda things work –
    anything that doesn’t should be reported as a bug. (but, equally, 3.1 will release
    soon – so unless it’s a really awesome (crashy, probably) bug it probably won’t
    make it into 3.1 & we’ll just have to live with it and fix it for 3.2)Oh, and we’ll
    update the web demo to 3.1 when it releases.

    (& to answer your question more clearly: the state of lambdas in 3.0 is “not
    implemented”. Only some small parts of the lambda functionality had been
    implemented at that stage & we forgot to turn those off before we 3.0 was
    released)

    David was also kind enough to provide me with the Release Notes for 3.1

  • MS VS 2010 bug:

    [..] A fix for this issue has been checked into the compiler sources. The fix should show up in the next release of Visual C++.

    This was just over twelve hours after I had logged the bug. Way to go MS!

  • Comeau: I had emailed them with the empty-lambda snippet and received a response from Greg Comeau in a couple of hours:

    4.3.10.1 does not include lambda support, that’s in a later version, which is currently unreleased and not sure yet when it will be.

Advertisements

About this entry