/// \brief Retrieve the capture type for this capture, which is effectively
/// the type of the non-static data member in the lambda/block structure
/// that would store this capture.
- QualType getCaptureType() const { return CaptureType; }
-
+ QualType getCaptureType() const {
+ assert(!isThisCapture());
+ return CaptureType;
+ }
+
Expr *getInitExpr() const {
assert(!isVLATypeCapture() && "no init expression for type capture");
return static_cast<Expr *>(InitExprAndCaptureKind.getPointer());
/*Cpy*/ nullptr));
}
- void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType,
+ // Note, we do not need to add the type of 'this' since that is always
+ // retrievable from Sema::getCurrentThisType - and is also encoded within the
+ // type of the corresponding FieldDecl.
+ void addThisCapture(bool isNested, SourceLocation Loc,
Expr *Cpy, bool ByCopy);
/// \brief Determine whether the C++ 'this' is captured.
inline void
CapturingScopeInfo::addThisCapture(bool isNested, SourceLocation Loc,
- QualType CaptureType, Expr *Cpy,
+ Expr *Cpy,
const bool ByCopy) {
- Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, CaptureType,
+ Captures.push_back(Capture(Capture::ThisCapture, isNested, Loc, QualType(),
Cpy, ByCopy));
CXXThisCaptureIndex = Captures.size();
}
return false;
}
+static QualType adjustCVQualifiersForCXXThisWithinLambda(
+ ArrayRef<FunctionScopeInfo *> FunctionScopes, QualType ThisTy,
+ DeclContext *CurSemaContext, ASTContext &ASTCtx) {
+
+ QualType ClassType = ThisTy->getPointeeType();
+ LambdaScopeInfo *CurLSI = nullptr;
+ DeclContext *CurDC = CurSemaContext;
+
+ // Iterate through the stack of lambdas starting from the innermost lambda to
+ // the outermost lambda, checking if '*this' is ever captured by copy - since
+ // that could change the cv-qualifiers of the '*this' object.
+ // The object referred to by '*this' starts out with the cv-qualifiers of its
+ // member function. We then start with the innermost lambda and iterate
+ // outward checking to see if any lambda performs a by-copy capture of '*this'
+ // - and if so, any nested lambda must respect the 'constness' of that
+ // capturing lamdbda's call operator.
+ //
+
+ // The issue is that we cannot rely entirely on the FunctionScopeInfo stack
+ // since ScopeInfos are pushed on during parsing and treetransforming. But
+ // since a generic lambda's call operator can be instantiated anywhere (even
+ // end of the TU) we need to be able to examine its enclosing lambdas and so
+ // we use the DeclContext to get a hold of the closure-class and query it for
+ // capture information. The reason we don't just resort to always using the
+ // DeclContext chain is that it is only mature for lambda expressions
+ // enclosing generic lambda's call operators that are being instantiated.
+
+ for (int I = FunctionScopes.size();
+ I-- && isa<LambdaScopeInfo>(FunctionScopes[I]);
+ CurDC = getLambdaAwareParentOfDeclContext(CurDC)) {
+ CurLSI = cast<LambdaScopeInfo>(FunctionScopes[I]);
+
+ if (!CurLSI->isCXXThisCaptured())
+ continue;
+
+ auto C = CurLSI->getCXXThisCapture();
+
+ if (C.isCopyCapture()) {
+ ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask);
+ if (CurLSI->CallOperator->isConst())
+ ClassType.addConst();
+ return ASTCtx.getPointerType(ClassType);
+ }
+ }
+ // We've run out of ScopeInfos but check if CurDC is a lambda (which can
+ // happen during instantiation of generic lambdas)
+ if (isLambdaCallOperator(CurDC)) {
+ assert(CurLSI);
+ assert(isGenericLambdaCallOperatorSpecialization(CurLSI->CallOperator));
+ assert(CurDC == getLambdaAwareParentOfDeclContext(CurLSI->CallOperator));
+
+ auto IsThisCaptured =
+ [](CXXRecordDecl *Closure, bool &IsByCopy, bool &IsConst) {
+ IsConst = false;
+ IsByCopy = false;
+ for (auto &&C : Closure->captures()) {
+ if (C.capturesThis()) {
+ if (C.getCaptureKind() == LCK_StarThis)
+ IsByCopy = true;
+ if (Closure->getLambdaCallOperator()->isConst())
+ IsConst = true;
+ return true;
+ }
+ }
+ return false;
+ };
+
+ bool IsByCopyCapture = false;
+ bool IsConstCapture = false;
+ CXXRecordDecl *Closure = cast<CXXRecordDecl>(CurDC->getParent());
+ while (Closure &&
+ IsThisCaptured(Closure, IsByCopyCapture, IsConstCapture)) {
+ if (IsByCopyCapture) {
+ ClassType.removeLocalCVRQualifiers(Qualifiers::CVRMask);
+ if (IsConstCapture)
+ ClassType.addConst();
+ return ASTCtx.getPointerType(ClassType);
+ }
+ Closure = isLambdaCallOperator(Closure->getParent())
+ ? cast<CXXRecordDecl>(Closure->getParent()->getParent())
+ : nullptr;
+ }
+ }
+ return ASTCtx.getPointerType(ClassType);
+}
+
QualType Sema::getCurrentThisType() {
DeclContext *DC = getFunctionLevelDeclContext();
QualType ThisTy = CXXThisTypeOverride;
ThisTy = Context.getPointerType(ClassTy);
}
}
- // Add const for '* this' capture if not mutable.
- if (isLambdaCallOperator(CurContext)) {
- LambdaScopeInfo *LSI = getCurLambda();
- assert(LSI);
- if (LSI->isCXXThisCaptured()) {
- auto C = LSI->getCXXThisCapture();
- QualType BaseType = ThisTy->getPointeeType();
- if ((C.isThisCapture() && C.isCopyCapture()) &&
- LSI->CallOperator->isConst() && !BaseType.isConstQualified()) {
- BaseType.addConst();
- ThisTy = Context.getPointerType(BaseType);
- }
- }
- }
+
+ // If we are within a lambda's call operator, the cv-qualifiers of 'this'
+ // might need to be adjusted if the lambda or any of its enclosing lambda's
+ // captures '*this' by copy.
+ if (!ThisTy.isNull() && isLambdaCallOperator(CurContext))
+ return adjustCVQualifiersForCXXThisWithinLambda(FunctionScopes, ThisTy,
+ CurContext, Context);
return ThisTy;
}
static Expr *captureThis(Sema &S, ASTContext &Context, RecordDecl *RD,
QualType ThisTy, SourceLocation Loc,
const bool ByCopy) {
- QualType CaptureThisTy = ByCopy ? ThisTy->getPointeeType() : ThisTy;
- FieldDecl *Field
- = FieldDecl::Create(Context, RD, Loc, Loc, nullptr, CaptureThisTy,
- Context.getTrivialTypeSourceInfo(CaptureThisTy, Loc),
- nullptr, false, ICIS_NoInit);
+ QualType AdjustedThisTy = ThisTy;
+ // The type of the corresponding data member (not a 'this' pointer if 'by
+ // copy').
+ QualType CaptureThisFieldTy = ThisTy;
+ if (ByCopy) {
+ // If we are capturing the object referred to by '*this' by copy, ignore any
+ // cv qualifiers inherited from the type of the member function for the type
+ // of the closure-type's corresponding data member and any use of 'this'.
+ CaptureThisFieldTy = ThisTy->getPointeeType();
+ CaptureThisFieldTy.removeLocalCVRQualifiers(Qualifiers::CVRMask);
+ AdjustedThisTy = Context.getPointerType(CaptureThisFieldTy);
+ }
+
+ FieldDecl *Field = FieldDecl::Create(
+ Context, RD, Loc, Loc, nullptr, CaptureThisFieldTy,
+ Context.getTrivialTypeSourceInfo(CaptureThisFieldTy, Loc), nullptr, false,
+ ICIS_NoInit);
+
Field->setImplicit(true);
Field->setAccess(AS_private);
RD->addDecl(Field);
- Expr *This = new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/true);
+ Expr *This =
+ new (Context) CXXThisExpr(Loc, ThisTy, /*isImplicit*/ true);
if (ByCopy) {
Expr *StarThis = S.CreateBuiltinUnaryOp(Loc,
UO_Deref,
This).get();
InitializedEntity Entity = InitializedEntity::InitializeLambdaCapture(
- nullptr, CaptureThisTy, Loc);
+ nullptr, CaptureThisFieldTy, Loc);
InitializationKind InitKind = InitializationKind::CreateDirect(Loc, Loc, Loc);
InitializationSequence Init(S, Entity, InitKind, StarThis);
ExprResult ER = Init.Perform(S, Entity, InitKind, StarThis);
"*this) by copy");
// FIXME: We need to delay this marking in PotentiallyPotentiallyEvaluated
// contexts.
-
+ QualType ThisTy = getCurrentThisType();
for (unsigned idx = MaxFunctionScopesIndex; NumCapturingClosures;
--idx, --NumCapturingClosures) {
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
Expr *ThisExpr = nullptr;
- QualType ThisTy = getCurrentThisType();
+
if (LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI)) {
// For lambda expressions, build a field and an initializing expression,
// and capture the *enclosing object* by copy only if this is the first
false/*ByCopy*/);
bool isNested = NumCapturingClosures > 1;
- CSI->addThisCapture(isNested, Loc, ThisTy, ThisExpr, ByCopy);
+ CSI->addThisCapture(isNested, Loc, ThisExpr, ByCopy);
}
return false;
}
// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fms-extensions %s -DMS_EXTENSIONS\r
// RUN: %clang_cc1 -std=c++1z -verify -fsyntax-only -fblocks -fdelayed-template-parsing -fms-extensions %s -DMS_EXTENSIONS -DDELAYED_TEMPLATE_PARSING\r
\r
+template<class, class> constexpr bool is_same = false;\r
+template<class T> constexpr bool is_same<T, T> = true;\r
\r
namespace test_star_this {\r
namespace ns1 {\r
b.foo(); //expected-note{{in instantiation}}\r
} // end main \r
} // end ns4\r
+namespace ns5 {\r
+\r
+struct X {\r
+ double d = 3.14;\r
+ X(const volatile X&);\r
+ void foo() {\r
+ \r
+ }\r
+ \r
+ void foo() const { //expected-note{{const}}\r
+ \r
+ auto L = [*this] () mutable { \r
+ static_assert(is_same<decltype(this), X*>);\r
+ ++d;\r
+ auto M = [this] { \r
+ static_assert(is_same<decltype(this), X*>); \r
+ ++d;\r
+ auto N = [] {\r
+ static_assert(is_same<decltype(this), X*>); \r
+ };\r
+ };\r
+ };\r
+ \r
+ auto L1 = [*this] { \r
+ static_assert(is_same<decltype(this), const X*>);\r
+ auto M = [this] () mutable { \r
+ static_assert(is_same<decltype(this), const X*>); \r
+ auto N = [] {\r
+ static_assert(is_same<decltype(this), const X*>); \r
+ };\r
+ };\r
+ auto M2 = [*this] () mutable { \r
+ static_assert(is_same<decltype(this), X*>); \r
+ auto N = [] {\r
+ static_assert(is_same<decltype(this), X*>); \r
+ };\r
+ };\r
+ };\r
+ \r
+ auto GL1 = [*this] (auto a) { \r
+ static_assert(is_same<decltype(this), const X*>);\r
+ auto M = [this] (auto b) mutable { \r
+ static_assert(is_same<decltype(this), const X*>); \r
+ auto N = [] (auto c) {\r
+ static_assert(is_same<decltype(this), const X*>); \r
+ };\r
+ return N;\r
+ };\r
+ \r
+ auto M2 = [*this] (auto a) mutable { \r
+ static_assert(is_same<decltype(this), X*>); \r
+ auto N = [] (auto b) {\r
+ static_assert(is_same<decltype(this), X*>); \r
+ };\r
+ return N;\r
+ };\r
+ return [=](auto a) mutable { M(a)(a); M2(a)(a); };\r
+ };\r
+ \r
+ GL1("abc")("abc");\r
+ \r
+ \r
+ auto L2 = [this] () mutable {\r
+ static_assert(is_same<decltype(this), const X*>); \r
+ ++d; //expected-error{{cannot assign}}\r
+ };\r
+ auto GL = [*this] (auto a) mutable {\r
+ static_assert(is_same<decltype(this), X*>);\r
+ ++d;\r
+ auto M = [this] (auto b) { \r
+ static_assert(is_same<decltype(this), X*>); \r
+ ++d;\r
+ auto N = [] (auto c) {\r
+ static_assert(is_same<decltype(this), X*>); \r
+ };\r
+ N(3.14);\r
+ };\r
+ M("abc");\r
+ };\r
+ GL(3.14);\r
+ \r
+ }\r
+ void foo() volatile const {\r
+ auto L = [this] () {\r
+ static_assert(is_same<decltype(this), const volatile X*>);\r
+ auto M = [*this] () mutable { \r
+ static_assert(is_same<decltype(this), X*>);\r
+ auto N = [this] {\r
+ static_assert(is_same<decltype(this), X*>);\r
+ auto M = [] {\r
+ static_assert(is_same<decltype(this), X*>);\r
+ };\r
+ };\r
+ auto N2 = [*this] {\r
+ static_assert(is_same<decltype(this), const X*>);\r
+ };\r
+ };\r
+ auto M2 = [*this] () {\r
+ static_assert(is_same<decltype(this), const X*>); \r
+ auto N = [this] {\r
+ static_assert(is_same<decltype(this), const X*>);\r
+ };\r
+ };\r
+ };\r
+ }\r
+ \r
+};\r
+\r
+} //end ns5\r
+namespace ns6 {\r
+struct X {\r
+ double d;\r
+ auto foo() const {\r
+ auto L = [*this] () mutable {\r
+ auto M = [=] (auto a) {\r
+ auto N = [this] {\r
+ ++d;\r
+ static_assert(is_same<decltype(this), X*>);\r
+ auto O = [*this] {\r
+ static_assert(is_same<decltype(this), const X*>);\r
+ };\r
+ };\r
+ N();\r
+ static_assert(is_same<decltype(this), X*>);\r
+ };\r
+ return M;\r
+ };\r
+ return L;\r
+ }\r
+}; \r
+\r
+int main() {\r
+ auto L = X{}.foo();\r
+ auto M = L();\r
+ M(3.14);\r
+}\r
+} // end ns6\r
+namespace ns7 {\r
+\r
+struct X {\r
+ double d;\r
+ X();\r
+ X(const X&); \r
+ X(X&) = delete;\r
+ auto foo() const {\r
+ //OK - the object used to initialize our capture is a const object and so prefers the non-deleted ctor.\r
+ const auto &&L = [*this] { };\r
+ }\r
+ \r
+}; \r
+int main() {\r
+ X x;\r
+ x.foo();\r
+}\r
+} // end ns7\r
+\r
} //end ns test_star_this\r
+\r