This is an archive of the discontinued LLVM Phabricator instance.

Fix cv-qualification of '*this' captures (and nasty bug PR27507 introduced by commit 263921 "Implement Lambda Capture of *this by Value as [=,*this]")
ClosedPublic

Authored by faisalv on May 1 2016, 8:22 PM.

Details

Summary

The bug report by Gonzalo (https://llvm.org/bugs/show_bug.cgi?id=27507 -- which results in clang crashing when generic lambdas that capture 'this' are instantiated in contexts where the functionscopeinfo stack is not in a reliable state - yet getCurrentThisType expects it to be) - unearthed some additional bugs in regards to maintaining proper cv qualification through 'this' when performing by value captures of '*this'.

This patch attempts to correct those bugs and makes the following changes:

  • when capturing 'this', we do not need to remember the type of 'this' within the LambdaScopeInfo's Capture - it is never really used for a this capture - so remove it.
  • teach getCurrentThisType to walk the stack of lambdas (even in scenarios where we run out of LambdaScopeInfo's such as when instantiating call operators) looking for by copy captures of '*this' and resetting the type of 'this' based on the constness of that capturing lambda's call operator.

As an example, consider the member function 'foo' below and follow along with the static_asserts:

void foo() const {

  
  auto L = [*this] () mutable { 
    static_assert(is_same<decltype(this), X*>);
    ++d;
    auto M = [this] { 
      static_assert(is_same<decltype(this), X*>);  
      ++d;
      auto N = [] {
        static_assert(is_same<decltype(this), X*>); 
      };
    };
  };
  
  auto L1 = [*this] { 
    static_assert(is_same<decltype(this), const X*>);
    auto M = [this] () mutable { 
      static_assert(is_same<decltype(this), const X*>);  
      auto N = [] {
        static_assert(is_same<decltype(this), const X*>); 
      };
    };
  };
  auto L2 = [this] () mutable {
    static_assert(is_same<decltype(this), const X*>);  
  };
  auto GL = [*this] (auto a) mutable {
    static_assert(is_same<decltype(this), X*>);
    ++d;
    auto M = [this] (auto b) { 
      static_assert(is_same<decltype(this), X*>);  
      ++d;
      auto N = [] (auto c) {
        static_assert(is_same<decltype(this), X*>); 
      };
      N(3.14);
    };
    M("abc");
  };
  GL(3.14);
}

Please see the test file for additional tests.

Thanks!

Diff Detail

Event Timeline

faisalv updated this revision to Diff 55769.May 1 2016, 8:22 PM
faisalv retitled this revision from to Fix cv-qualification of '*this' captures (and nasty bug PR27507 introduced by commit 263921 "Implement Lambda Capture of *this by Value as [=,*this]").
faisalv updated this object.
faisalv added subscribers: cfe-commits, gnzlbg.

Is it too soon for a *ping* ;)

p.s. just added some comments.

lib/Sema/SemaExprCXX.cpp
904

That dyn_cast should be an 'isa'

1056

Hmm - I wonder if instead of using AdjustedThisTy, I should always use 'ThisTy' since it is being used in the initializer expression, and should probably inherit its cv qualifiers from an enclosing lambda? correct?

twoh added a subscriber: twoh.May 18 2016, 8:44 AM
rsmith added inline comments.May 18 2016, 10:15 AM
lib/Sema/SemaExprCXX.cpp
898

No camel case for ClosureClass.

910

Please add a comment explaining this, I have no idea what special case you're checking for here.

952

Should you really stop here if this is not captured? There could still be a surrounding lambda with a mutable *this capture.

1056

Yes, don't use AdjustedThisTy here. You can test for this bug by giving the class both a (const T&) and a (T&)=delete copy ctor.

rsmith edited edge metadata.May 18 2016, 10:35 AM
rsmith added a subscriber: rsmith.

I'd also like to know whether there are cases that this patch addresses but
Taewook Oh's patch does not, as the other patch involves a lot less
complexity.

twoh added a comment.May 18 2016, 11:46 AM

My patch passes check-clang and the test cases in this patch as well.

BTW, newly added test cases in the patch seem to be passed even without the patch. Isn't the bug appears when template instantiation generates nested lambdas (because template instantiation updates the 'context')?

OK - thanks - will take a closer look at this hopefuly this evening
or tomorrow - and respond to Richard's other comments too.
Faisal Vali

faisalv marked 5 inline comments as done.May 18 2016, 10:16 PM

OK - agree (and addressed in a forthcoming patch) all your comments - except for the one I could use some clarity on - please see below

lib/Sema/SemaExprCXX.cpp
910

That special case should have been removed before submission - it crept in as I was making incremental changes to the initial patch, and getCurrentThisType was still being called from RebuildLambdaScopeInfo. (Alternatively, I might have left it in to give Richard a sense of purpose ;)

952

Do you have the following example in mind? Or is it something different?

void foo() const {

auto L1 = [*this] { 
    static_assert(is_same<decltype(this), const X*>);

    auto M2 = [*this] () mutable { 
      static_assert(is_same<decltype(this), X*>);  
      auto N = [] {
        static_assert(is_same<decltype(this), X*>); 
      };
    };
  };

}

faisalv updated this revision to Diff 57733.May 18 2016, 10:20 PM
faisalv edited edge metadata.
faisalv marked an inline comment as done.

This patch addresses all of Richard's comments - except one (on which I'm awaiting some additional clarity on, before I make any changes).

faisalv added inline comments.May 19 2016, 4:29 AM
lib/Parse/ParseDeclCXX.cpp
3651

This whitespace change shouldn't have been included - as an aside - we still strive unix style line endings right?

twoh added a comment.May 19 2016, 11:33 PM

@faisalv, thank you for the update. I wonder if getCurrentThisType() is the best place to call adjustCVQualifiersForCXXThisWithinLambda(). It seems that getCurrentThisType() does more than its name suggests. Is there any other place that the adjustment function can be called?

I'm also a huge advocate of simple composable interfaces that do the
'one' task that they are expected (i.e. named) to do - and do it well.
I thought about this some, but perhaps not exhaustively - and settled
on being ok with getCurThisType, always returning the correct type for
'this' as typed/referred to in code (with the right 'cv' quals) -
regardless of where it is called from. Nevertheless, I'm open to
suggestions if you have a better alternative candidate for where the
call to adjustCVQuals should go?

Thanks for thinking about this.

Faisal Vali

twoh added a comment.May 21 2016, 10:58 AM

Hmm, yes it seems not so simple to move the call to another place. If I come up with a better idea I'll submit it as a separate patch, as I don't want to block this one. Thank you for your work, @faisalv!

This revision is now accepted and ready to land.Jun 11 2016, 9:50 AM
faisalv closed this revision.Jun 11 2016, 9:51 AM