Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2156,8 +2156,9 @@ mutable unsigned CachedFieldIndex : 31; /// \brief An InClassInitStyle value, and either a bit width expression (if - /// the InClassInitStyle value is ICIS_NoInit), or a pointer to the in-class - /// initializer for this field (otherwise). + /// the InClassInitStyle value is ICIS_NoInit) in struct/class, or a captured + /// variable length array bound in a lambda expression, or a pointer to the + /// in-class initializer for this field (otherwise). /// /// We can safely combine these two because in-class initializers are not /// permitted for bit-fields. @@ -2193,11 +2194,8 @@ /// isMutable - Determines whether this field is mutable (C++ only). bool isMutable() const { return Mutable; } - /// isBitfield - Determines whether this field is a bitfield. - bool isBitField() const { - return getInClassInitStyle() == ICIS_NoInit && - InitializerOrBitWidth.getPointer(); - } + /// \brief Determines whether this field is a bitfield. + bool isBitField() const; /// @brief Determines whether this is an unnamed bitfield. bool isUnnamedBitfield() const { return isBitField() && !getDeclName(); } @@ -2253,6 +2251,18 @@ InitializerOrBitWidth.setInt(ICIS_NoInit); } + /// \brief Determine whether this member captures the expression for variable + /// length arrays bounds. + bool hasCapturedVLABoundExpr() const; + /// \brief Get the captured variable length array bound expression. + Expr *getCapturedVLABoundExpr() const { + return hasCapturedVLABoundExpr() ? InitializerOrBitWidth.getPointer() + : nullptr; + } + /// \brief Set the captured variable length array bound expression for this + /// field. + void setCapturedVLABoundExpr(Expr *CapturedExpr); + /// getParent - Returns the parent of this field declaration, which /// is the struct in which this method is defined. const RecordDecl *getParent() const { Index: include/clang/AST/LambdaCapture.h =================================================================== --- include/clang/AST/LambdaCapture.h +++ include/clang/AST/LambdaCapture.h @@ -68,13 +68,23 @@ /// \brief Determine whether this capture handles the C++ \c this /// pointer. - bool capturesThis() const { return DeclAndBits.getPointer() == nullptr; } + bool capturesThis() const { + return (DeclAndBits.getPointer() == nullptr) && + !(DeclAndBits.getInt() & Capture_ByCopy); + } /// \brief Determine whether this capture handles a variable. bool capturesVariable() const { return dyn_cast_or_null(DeclAndBits.getPointer()); } + /// \brief Determine whether this captures a variable length array bound + /// expression. + bool capturesVLABoundExpression() const { + return (DeclAndBits.getPointer() == nullptr) && + (DeclAndBits.getInt() & Capture_ByCopy); + } + /// \brief Determine whether this is an init-capture. bool isInitCapture() const { return capturesVariable() && getCapturedVar()->isInitCapture(); Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5319,9 +5319,6 @@ "'this' cannot be %select{implicitly |}0captured in this context">; def err_lambda_capture_anonymous_var : Error< "unnamed variable cannot be implicitly captured in a lambda expression">; - def err_lambda_capture_vm_type : Error< - "variable %0 with variably modified type cannot be captured in " - "a lambda expression">; def err_lambda_capture_flexarray_type : Error< "variable %0 with flexible array member cannot be captured in " "a lambda expression">; Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3259,6 +3259,19 @@ return false; } +bool FieldDecl::isBitField() const { + if (getInClassInitStyle() == ICIS_NoInit && + InitializerOrBitWidth.getPointer()) { + if (getDeclContext() && getDeclContext()->isRecord()) { + if (auto *RD = dyn_cast(getParent())) { + return !RD->isLambda(); + } + } + return true; + } + return false; +} + unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { assert(isBitField() && "not a bitfield"); Expr *BitWidth = InitializerOrBitWidth.getPointer(); @@ -3291,16 +3304,35 @@ void FieldDecl::setBitWidth(Expr *Width) { assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && - "bit width or initializer already set"); + "bit width, initializer or captured expr already set"); InitializerOrBitWidth.setPointer(Width); } void FieldDecl::setInClassInitializer(Expr *Init) { assert(!InitializerOrBitWidth.getPointer() && hasInClassInitializer() && - "bit width or initializer already set"); + "bit width, initializer or captured expr already set"); InitializerOrBitWidth.setPointer(Init); } +bool FieldDecl::hasCapturedVLABoundExpr() const { + if (getDeclContext() && getDeclContext()->isRecord()) { + if (auto *RD = dyn_cast(getParent())) { + return RD->isLambda() && getInClassInitStyle() == ICIS_NoInit && + InitializerOrBitWidth.getPointer(); + } + } + return false; +} + +void FieldDecl::setCapturedVLABoundExpr(Expr *CapturedExpr) { + assert(isa(getParent()) && + cast(getParent())->isLambda() && + "capturing expression in non-lambda."); + assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && + "bit width, initializer or captured expr already set"); + InitializerOrBitWidth.setPointer(CapturedExpr); +} + //===----------------------------------------------------------------------===// // TagDecl Implementation //===----------------------------------------------------------------------===// Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2989,7 +2989,7 @@ const LambdaExpr *LE = cast(this); for (LambdaExpr::capture_iterator I = LE->capture_begin(), E = LE->capture_end(); I != E; ++I) - if (I->getCaptureKind() == LCK_ByCopy) + if (I->getCaptureKind() == LCK_ByCopy && I->capturesVariable()) // FIXME: Only has a side-effect if the variable is volatile or if // the copy would invoke a non-trivial copy constructor. return true; Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -901,7 +901,7 @@ case LCK_ByCopy: Bits |= Capture_ByCopy; - // Fall through + break; case LCK_ByRef: assert(Var && "capture must have a variable!"); break; @@ -911,7 +911,8 @@ LambdaCaptureKind LambdaCapture::getCaptureKind() const { Decl *D = DeclAndBits.getPointer(); - if (!D) + bool CapByCopy = DeclAndBits.getInt() & Capture_ByCopy; + if (!D && !CapByCopy) return LCK_This; return (DeclAndBits.getInt() & Capture_ByCopy) ? LCK_ByCopy : LCK_ByRef; Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1623,6 +1623,8 @@ CEnd = Node->explicit_capture_end(); C != CEnd; ++C) { + if (C->capturesVLABoundExpression()) + continue; if (NeedComma) OS << ", "; NeedComma = true; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -954,6 +954,8 @@ for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(), CEnd = S->explicit_capture_end(); C != CEnd; ++C) { + if (C->capturesVLABoundExpression()) + continue; ID.AddInteger(C->getCaptureKind()); switch (C->getCaptureKind()) { case LCK_This: Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -850,12 +850,11 @@ C.getLocation(), Field->getAccess(), layout.getFieldOffset(fieldno), VUnit, RecordTy); elements.push_back(fieldType); - } else { + } else if (C.capturesThis()) { // TODO: Need to handle 'this' in some way by probably renaming the // this of the lambda class and having a field member of 'this' or // by using AT_object_pointer for the function and having that be // used as 'this' for semantic references. - assert(C.capturesThis() && "Field that isn't captured and isn't this?"); FieldDecl *f = *Field; llvm::DIFile VUnit = getOrCreateFile(f->getLocation()); QualType type = f->getType(); Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -1768,19 +1768,29 @@ void CodeGenFunction::EmitLambdaExpr(const LambdaExpr *E, AggValueSlot Slot) { RunCleanupsScope Scope(*this); - LValue SlotLV = MakeAddrLValue(Slot.getAddr(), E->getType(), - Slot.getAlignment()); + LValue SlotLV = + MakeAddrLValue(Slot.getAddr(), E->getType(), Slot.getAlignment()); CXXRecordDecl::field_iterator CurField = E->getLambdaClass()->field_begin(); for (LambdaExpr::capture_init_iterator i = E->capture_init_begin(), e = E->capture_init_end(); i != e; ++i, ++CurField) { // Emit initialization - LValue LV = EmitLValueForFieldInitialization(SlotLV, *CurField); - ArrayRef ArrayIndexes; - if (CurField->getType()->isArrayType()) - ArrayIndexes = E->getCaptureInitIndexVars(i); - EmitInitializerForField(*CurField, LV, *i, ArrayIndexes); + if (CurField->hasCapturedVLABoundExpr()) { + assert(isa(*i) && + "Captured expression is not a binary operator."); + auto QArrayType = cast(cast(*i) + ->getLHS() + ->getType() + ->getAsArrayTypeUnsafe()); + EmitStoreThroughLValue(RValue::get(VLASizeMap[QArrayType->getSizeExpr()]), + LV); + } else { + ArrayRef ArrayIndexes; + if (CurField->getType()->isArrayType()) + ArrayIndexes = E->getCaptureInitIndexVars(i); + EmitInitializerForField(*CurField, LV, *i, ArrayIndexes); + } } } Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -668,6 +668,20 @@ CXXThisValue = EmitLoadOfLValue(ThisLValue, SourceLocation()).getScalarVal(); } + for (auto *FD : MD->getParent()->fields()) { + if (FD->hasCapturedVLABoundExpr()) { + auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD), + SourceLocation()).getScalarVal(); + assert(isa(FD->getCapturedVLABoundExpr()) && + "Captured expression is not a binary operator."); + auto QArrayType = cast( + cast(FD->getCapturedVLABoundExpr()) + ->getLHS() + ->getType() + ->getAsArrayTypeUnsafe()); + VLASizeMap[QArrayType->getSizeExpr()] = ExprArg; + } + } } else { // Not in a lambda; just use 'this' from the method. // FIXME: Should we generate a new load for each use of 'this'? The Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9749,6 +9749,7 @@ // Add the captures to the LSI so they can be noted as already // captured within tryCaptureVar. + auto I = LambdaClass->field_begin(); for (const auto &C : LambdaClass->captures()) { if (C.capturesVariable()) { VarDecl *VD = C.getCapturedVar(); @@ -9765,7 +9766,14 @@ } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), S.getCurrentThisType(), /*Expr*/ nullptr); + } else { + LSI->addCapture(nullptr, /*IsBlock*/ false, /*ByRef*/ false, + /*RefersToEnclosingLocal*/ false, C.getLocation(), + /*EllipsisLoc*/ C.isPackExpansion() ? C.getEllipsisLoc() + : SourceLocation(), + I->getType(), I->getCapturedVLABoundExpr()); } + ++I; } } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -11668,12 +11668,9 @@ } // Prohibit variably-modified types; they're difficult to deal with. - if (Var->getType()->isVariablyModifiedType() && (IsBlock || IsLambda)) { + if (Var->getType()->isVariablyModifiedType() && IsBlock) { if (Diagnose) { - if (IsBlock) - S.Diag(Loc, diag::err_ref_vm_type); - else - S.Diag(Loc, diag::err_lambda_capture_vm_type) << Var->getDeclName(); + S.Diag(Loc, diag::err_ref_vm_type); S.Diag(Var->getLocation(), diag::note_previous_decl) << Var->getDeclName(); } @@ -12218,8 +12215,43 @@ // Unknown size indication requires no size computation. // Otherwise, evaluate and record it. - if (Expr *Size = Vat->getSizeExpr()) { - MarkDeclarationsReferencedInExpr(Size); + if (auto Size = Vat->getSizeExpr()) { + if (auto LSI = dyn_cast(CSI)) { + auto Loc = Size->getExprLoc(); + auto QType = Size->getType(); + auto QArrayType = QualType(Vat, 0); + auto TInfo = Context.getTrivialTypeSourceInfo(QType, Loc); + auto QArrayTInfo = + Context.getTrivialTypeSourceInfo(QArrayType, Loc); + auto VD = VarDecl::Create(Context, getFunctionLevelDeclContext(), + SourceLocation(), SourceLocation(), + &Context.Idents.get(".captured_expr."), + QArrayType, QArrayTInfo, SC_Auto); + auto DRE = + new (Context) DeclRefExpr(VD, /*refersToEnclosingLocal*/ true, + QArrayType, VK_LValue, Loc); + auto BinExpr = new (Context) BinaryOperator( + DRE, Size, BO_Comma, QType, Size->getValueKind(), OK_Ordinary, + Loc, /*fpContractable*/ false); + auto Lambda = LSI->Lambda; + + // Build the non-static data member. + FieldDecl *Field = FieldDecl::Create(Context, Lambda, Loc, Loc, + /*Id*/ nullptr, QType, TInfo, + BinExpr, /*Mutable*/ false, + /*InitStyle*/ ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + Lambda->addDecl(Field); + + LSI->addCapture(/*Var=*/nullptr, /*IsBlock=*/false, + /*isByref=*/false, /*isNested=*/false, Loc, Loc, + QType, BinExpr); + } else { + // Immediately mark all referenced vars for CapturedStatements, + // they all are captured by reference. + MarkDeclarationsReferencedInExpr(Size); + } } QTy = Vat->getElementType(); break; Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -8760,6 +8760,10 @@ getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit()); continue; } + // Captured expression will be recaptured during captured variables + // rebuilding. + if (C->capturesVLABoundExpression()) + continue; // Rebuild init-captures, including the implied field declaration. if (C->isInitCapture()) { Index: test/CodeGenCXX/instantiate-typeof-vla.cpp =================================================================== --- test/CodeGenCXX/instantiate-typeof-vla.cpp +++ test/CodeGenCXX/instantiate-typeof-vla.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -std=c++11 -emit-pch -o %t +// RUN: %clang_cc1 %s -std=c++11 -include-pch %t -emit-llvm -o - | FileCheck %s + +#ifndef HEADER +#define HEADER + +typedef __INTPTR_TYPE__ intptr_t; + +// CHECK-DAG: [[CAP_TYPE1:%.+]] = type { [[INTPTR_T:i.+]], [[INTPTR_T]]*, [[INTPTR_T]]* } +// CHECK-DAG: [[CAP_TYPE2:%.+]] = type { [[INTPTR_T]], [[INTPTR_T]]* } + + + +// CHECK: define void [[G:@.+]]( +// CHECK: [[N_ADDR:%.+]] = alloca [[INTPTR_T]] +// CHECK: store [[INTPTR_T]] %{{.+}}, [[INTPTR_T]]* [[N_ADDR]] +// CHECK: [[N_VAL:%.+]] = load [[INTPTR_T]]* [[N_ADDR]] +// CHECK: [[CAP_EXPR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0 +// CHECK: store [[INTPTR_T]] [[N_VAL]], [[INTPTR_T]]* [[CAP_EXPR_REF]] +// CHECK: [[CAP_BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1 +// CHECK: store [[INTPTR_T]]* %{{.+}}, [[INTPTR_T]]** [[CAP_BUFFER_ADDR]] +// CHECK: [[CAP_N_REF:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 2 +// CHECK: store [[INTPTR_T]]* [[N_ADDR]], [[INTPTR_T]]** [[CAP_N_REF]] +// CHECK: call void [[G_LAMBDA:@.+]]([[CAP_TYPE1]]* [[CAP_ARG]]) +// CHECK: ret void +void g(intptr_t n) { + intptr_t buffer[n]; + [&buffer, &n]() { + __typeof(buffer) x; + }(); +} + +// CHECK: void [[G_LAMBDA]]([[CAP_TYPE1]]* +// CHECK: [[THIS:%.+]] = load [[CAP_TYPE1]]** +// CHECK: [[N_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 0 +// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]] +// CHECK: [[BUFFER_ADDR:%.+]] = getelementptr inbounds [[CAP_TYPE1]]* [[THIS]], i{{.+}} 0, i{{.+}} 1 +// CHECK: [[BUFFER:%.+]] = load [[INTPTR_T]]** [[BUFFER_ADDR]] +// CHECK: call i{{.+}}* @llvm.stacksave() +// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[N]] +// CHECK: call void @llvm.stackrestore( +// CHECK: ret void + +template void f(T n, T m) { + intptr_t buffer[n + m]; + [&buffer]() { + __typeof(buffer) x; + }(); +} + +// CHECK-LABEL: @main +int main() { +// CHECK: call void [[G]]([[INTPTR_T]] 1) + g((intptr_t)1); +// CHECK: call void [[F_INT:@.+]]([[INTPTR_T]] 1, [[INTPTR_T]] 2) + f((intptr_t)1, (intptr_t)2); +// CHECK: ret i32 0 + return 0; +} + +// CHECK: void [[F_INT]]([[INTPTR_T]] +// CHECK: [[SIZE:%.+]] = add +// CHECK: call i{{.+}}* @llvm.stacksave() +// CHECK: [[BUFFER_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]] +// CHECK: [[CAP_SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0 +// CHECK: store [[INTPTR_T]] [[SIZE]], [[INTPTR_T]]* [[CAP_SIZE_REF]] +// CHECK: [[CAP_BUFFER_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1 +// CHECK: store [[INTPTR_T]]* [[BUFFER_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER_ADDR_REF]] +// CHECK: call void [[F_INT_LAMBDA:@.+]]([[CAP_TYPE2]]* [[CAP_ARG]]) +// CHECK: call void @llvm.stackrestore( +// CHECK: ret void + +// CHECK: void [[F_INT_LAMBDA]]([[CAP_TYPE2]]* +// CHECK: [[THIS:%.+]] = load [[CAP_TYPE2]]** +// CHECK: [[SIZE_REF:%.+]] = getelementptr inbounds [[CAP_TYPE2]]* [[THIS]], i{{.+}} 0, i{{.+}} 0 +// CHECK: [[SIZE:%.+]] = load [[INTPTR_T]]* [[SIZE_REF]] +// CHECK: call i{{.+}}* @llvm.stacksave() +// CHECK: alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE]] +// CHECK: call void @llvm.stackrestore( +// CHECK: ret void +#endif Index: test/SemaTemplate/instantiate-typeof.cpp =================================================================== --- test/SemaTemplate/instantiate-typeof.cpp +++ test/SemaTemplate/instantiate-typeof.cpp @@ -1,10 +1,11 @@ // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// expected-no-diagnostics // Make sure we correctly treat __typeof as potentially-evaluated when appropriate template void f(T n) { - int buffer[n]; // expected-note {{declared here}} - [] { __typeof(buffer) x; }(); // expected-error {{variable 'buffer' with variably modified type cannot be captured in a lambda expression}} + int buffer[n]; + [&buffer] { __typeof(buffer) x; }(); } int main() { - f(1); // expected-note {{in instantiation}} + f(1); } Index: tools/libclang/IndexBody.cpp =================================================================== --- tools/libclang/IndexBody.cpp +++ tools/libclang/IndexBody.cpp @@ -150,7 +150,7 @@ } bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { - if (C->capturesThis()) + if (C->capturesThis() || C->capturesVLABoundExpression()) return true; if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols())