[PATCH] Don't mistakenly capture byref in block nested in lambda

Richard Smith richard at metafoo.co.uk
Wed Jan 28 15:32:28 PST 2015


On Wed, Jan 28, 2015 at 2:11 PM, Ben Langmuir <blangmuir at apple.com> wrote:

>
> On Jan 28, 2015, at 1:21 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>
> Hi Ben,
>
> On Tue, Jan 27, 2015 at 3:06 PM, Ben Langmuir <blangmuir at apple.com> wrote:
>
>> Hi Richard,
>>
>> Could you take a look at this patch?  I haven’t messed with capturing in
>> a long time and I don’t want to break it ;-)
>
>
> LGTM as a diagnostics improvement (but see below).
>
>
>>     Don't mistakenly capture byref in block nested in lambda
>>
>>     Even if an enclosing lambda captures a variable by reference, we
>> capture
>>     it by *copy* in a block unless the variable itself was declared with a
>>     reference type. Codegen was already correct, but we were not
>> diagnosing
>>     attempts to modify the variable in Sema.
>>
>
> Hmm, I think I'm missing something here
>
> D'oh, what I was missing was the change to captureInBlock!

> -- it looks like we would diagnose it, but we'd use a suboptimal
> diagnostic: CheckForModifiableLvalue would diagnose as
> err_typecheck_assign_const in this case.
>
>
> It wouldn’t be const at all, because we created the (bogus) byref
> capture.  It occurs to me that my tests don’t make this obvious, so I’ll
> add:
>
> int x = 0;
> [&]() {
>   ^{
>     x = 1; // this diagnoses now, but didn’t before
>    }();
> }();
>

This part doesn't seem like an obviously-correct change to me. Consider the
desugaring of the lambda:

struct Lambda {
  int &x;
  Lambda(int &x) : x(x) {}
  auto operator()() const {
    ^{
    x = 1;
    }();
  }
};
Lambda(x)();

In this world, the block is referring to an entity with reference type, so
it should capture by reference instead of by value. As far as I can see,
there's no other way of observing this difference, so we don't have a lot
of guidance here, but treating 'x' like a variable of reference type seems
more consistent here. Especially when you consider:

[&x = x]() {
  ^{
  x = 1; // obviously ok, x obviously has reference type
  }();
}();

There are some strong proponents of the idea that [&x] should be exactly
equivalent to [&x = x], and that [x] should be exactly equivalent to [x =
x]; they *very nearly* are equivalent (they only differ in cv-qualifiers
today); this change gets us further from that position.

>     This also fixes a crash on invalid when trying to modify a variable
>> with
>>     non-trivial copy constructor and assignment operator in such a
>>     situation.
>>
>
> I can't reproduce this crash with current trunk, assuming this is supposed
> to be covered by the uses of 'A' in test7 in SemaCXX/blocks.cpp.
>
>
> Ah my mistake, the crash was reading from the value, not writing to it.
> I’ll remove that ‘A’ case in the Sema test and add an IRGen test to
> exercise it.  If you want to reproduce it:
>
> $ cat red.cpp
> struct A {
>   A() = default;
>   A(const A &); // non-trivial copy
>   A &operator=(const A &); // non-trivial assign
> };
>
> void test() {
>   A a;
>   [&]() { ^{ (void)a; }(); }();
> }
>
> $ clang++ -std=c++11 -fblocks red.cpp
>

Given the above, maybe this is an IRGen bug?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20150128/a42d987d/attachment.html>


More information about the cfe-commits mailing list