Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -2155,8 +2155,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. @@ -2164,7 +2165,7 @@ /// If the InClassInitStyle is not ICIS_NoInit and the initializer is null, /// then this field has an in-class initializer which has not yet been parsed /// and attached. - llvm::PointerIntPair InitializerOrBitWidth; + llvm::PointerIntPair InitializerOrBitWidth; protected: FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id, @@ -2192,11 +2193,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(); } @@ -2208,7 +2206,9 @@ bool isAnonymousStructOrUnion() const; Expr *getBitWidth() const { - return isBitField() ? InitializerOrBitWidth.getPointer() : nullptr; + return isBitField() + ? static_cast(InitializerOrBitWidth.getPointer()) + : nullptr; } unsigned getBitWidthValue(const ASTContext &Ctx) const; @@ -2238,8 +2238,9 @@ /// in-class initializer, but this returns null, then we have not parsed and /// attached it yet. Expr *getInClassInitializer() const { - return hasInClassInitializer() ? InitializerOrBitWidth.getPointer() - : nullptr; + return hasInClassInitializer() + ? static_cast(InitializerOrBitWidth.getPointer()) + : nullptr; } /// setInClassInitializer - Set the C++11 in-class initializer for this /// member. @@ -2252,6 +2253,18 @@ InitializerOrBitWidth.setInt(ICIS_NoInit); } + /// \brief Determine whether this member captures the variable length array + /// type. + bool hasCapturedVLAType() const; + /// \brief Get the captured variable length array type. + const Type *getCapturedVLAType() const { + return hasCapturedVLAType() + ? static_cast(InitializerOrBitWidth.getPointer()) + : nullptr; + } + /// \brief Set the captured variable length array type for this field. + void setCapturedVLAType(const Type *VLAType); + /// getParent - Returns the parent of this field declaration, which /// is the struct in which this method is defined. const RecordDecl *getParent() const { @@ -3136,6 +3149,10 @@ /// \endcode bool isInjectedClassName() const; + /// \brief Determine whether this record is a class describing a lambda + /// function object. + bool isLambda() const; + /// getDefinition - Returns the RecordDecl that actually defines /// this struct/union/class. When determining whether or not a /// struct/union/class is completely defined, one should use this 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 capturesVLAType() 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 @@ -5320,9 +5320,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: include/clang/Sema/ScopeInfo.h =================================================================== --- include/clang/Sema/ScopeInfo.h +++ include/clang/Sema/ScopeInfo.h @@ -381,7 +381,7 @@ /// capture (if this is a capture and not an init-capture). The expression /// is only required if we are capturing ByVal and the variable's type has /// a non-trivial copy constructor. - llvm::PointerIntPair InitExprAndCaptureKind; + llvm::PointerIntPair InitExprAndCaptureKind; /// \brief The source location at which the first capture occurred. SourceLocation Loc; @@ -402,6 +402,12 @@ ByRef ? Cap_ByRef : Cap_ByCopy), Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {} + Capture(SourceLocation Loc, SourceLocation EllipsisLoc, + QualType CaptureType, const Type *Tpy) + : VarAndNested(nullptr, /*isNested*/ false), + InitExprAndCaptureKind(const_cast(Tpy), Cap_ByCopy), Loc(Loc), + EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {} + enum IsThisCapture { ThisCapture }; Capture(IsThisCapture, bool IsNested, SourceLocation Loc, QualType CaptureType, Expr *Cpy) @@ -413,10 +419,10 @@ return InitExprAndCaptureKind.getInt() == Cap_This; } bool isVariableCapture() const { - return InitExprAndCaptureKind.getInt() != Cap_This; + return InitExprAndCaptureKind.getInt() != Cap_This && !isTypeCapture(); } bool isCopyCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_ByCopy; + return InitExprAndCaptureKind.getInt() == Cap_ByCopy && !isTypeCapture(); } bool isReferenceCapture() const { return InitExprAndCaptureKind.getInt() == Cap_ByRef; @@ -424,7 +430,11 @@ bool isBlockCapture() const { return InitExprAndCaptureKind.getInt() == Cap_Block; } - bool isNested() { return VarAndNested.getInt(); } + bool isTypeCapture() const { + return InitExprAndCaptureKind.getInt() == Cap_ByCopy && + getVariable() == nullptr; + } + bool isNested() const { return VarAndNested.getInt(); } VarDecl *getVariable() const { return VarAndNested.getPointer(); @@ -443,7 +453,12 @@ QualType getCaptureType() const { return CaptureType; } Expr *getInitExpr() const { - return InitExprAndCaptureKind.getPointer(); + assert(!isTypeCapture() && "no init expression for type capture"); + return static_cast(InitExprAndCaptureKind.getPointer()); + } + const Type *getCapturedType() const { + assert(isTypeCapture() && "no type for var or this capture"); + return static_cast(InitExprAndCaptureKind.getPointer()); } }; @@ -478,6 +493,11 @@ CaptureMap[Var] = Captures.size(); } + void addVLATypeCapture(SourceLocation Loc, SourceLocation EllipsisLoc, + QualType CaptureType, const Type *Tpy) { + Captures.push_back(Capture(Loc, EllipsisLoc, CaptureType, Tpy)); + } + void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType, Expr *Cpy); Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3259,9 +3259,18 @@ return false; } +bool FieldDecl::isBitField() const { + if (getInClassInitStyle() == ICIS_NoInit && + InitializerOrBitWidth.getPointer()) { + assert(getDeclContext() && "No parent context for FieldDecl"); + return !getDeclContext()->isRecord() || !getParent()->isLambda(); + } + return false; +} + unsigned FieldDecl::getBitWidthValue(const ASTContext &Ctx) const { assert(isBitField() && "not a bitfield"); - Expr *BitWidth = InitializerOrBitWidth.getPointer(); + Expr *BitWidth = static_cast(InitializerOrBitWidth.getPointer()); return BitWidth->EvaluateKnownConstInt(Ctx).getZExtValue(); } @@ -3284,23 +3293,38 @@ } SourceRange FieldDecl::getSourceRange() const { - if (const Expr *E = InitializerOrBitWidth.getPointer()) + if (const Expr *E = + static_cast(InitializerOrBitWidth.getPointer())) return SourceRange(getInnerLocStart(), E->getLocEnd()); return DeclaratorDecl::getSourceRange(); } void FieldDecl::setBitWidth(Expr *Width) { + assert(isBitField() && "not a bitfield"); assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && - "bit width or initializer already set"); + "bit width, initializer or captured type 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::hasCapturedVLAType() const { + return getDeclContext()->isRecord() && getParent()->isLambda() && + getInClassInitStyle() == ICIS_NoInit && + InitializerOrBitWidth.getPointer(); +} + +void FieldDecl::setCapturedVLAType(const Type *VLAType) { + assert(getParent()->isLambda() && "capturing type in non-lambda."); + assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() && + "bit width, initializer or captured type already set"); + InitializerOrBitWidth.setPointer(const_cast(VLAType)); +} + //===----------------------------------------------------------------------===// // TagDecl Implementation //===----------------------------------------------------------------------===// @@ -3521,6 +3545,13 @@ cast(getDeclContext())->getDeclName() == getDeclName(); } +bool RecordDecl::isLambda() const { + if (auto RD = dyn_cast(this)) { + return RD->isLambda(); + } + return false; +} + RecordDecl::field_iterator RecordDecl::field_begin() const { if (hasExternalLexicalStorage() && !LoadedFieldsFromExternalStorage) LoadFieldsFromExternalStorage(); Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2992,7 +2992,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 @@ -905,7 +905,7 @@ case LCK_ByCopy: Bits |= Capture_ByCopy; - // Fall through + break; case LCK_ByRef: assert(Var && "capture must have a variable!"); break; @@ -915,7 +915,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 @@ -1708,6 +1708,8 @@ CEnd = Node->explicit_capture_end(); C != CEnd; ++C) { + if (C->capturesVLAType()) + continue; if (NeedComma) OS << ", "; NeedComma = true; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1017,6 +1017,8 @@ for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(), CEnd = S->explicit_capture_end(); C != CEnd; ++C) { + if (C->capturesVLAType()) + 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 @@ -1800,19 +1800,27 @@ 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->hasCapturedVLAType()) { + assert(CurField->getCapturedVLAType()->isVariableArrayType() && + "Captured type is not a variable length array type."); + auto QArrayType = cast( + CurField->getCapturedVLAType()->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 @@ -663,6 +663,17 @@ CXXThisValue = EmitLoadOfLValue(ThisLValue, SourceLocation()).getScalarVal(); } + for (auto *FD : MD->getParent()->fields()) { + if (FD->hasCapturedVLAType()) { + assert(FD->getCapturedVLAType()->isVariableArrayType() && + "Captured type is not a variable length array type."); + auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD), + SourceLocation()).getScalarVal(); + auto QArrayType = cast( + FD->getCapturedVLAType()->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 @@ -9833,6 +9833,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(); @@ -9849,7 +9850,14 @@ } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), S.getCurrentThisType(), /*Expr*/ nullptr); + } else { + LSI->addVLATypeCapture(C.getLocation(), + /*EllipsisLoc*/ C.isPackExpansion() + ? C.getEllipsisLoc() + : SourceLocation(), + I->getType(), I->getCapturedVLAType()); } + ++I; } } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -11666,12 +11666,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(); } @@ -12212,14 +12209,35 @@ break; case Type::VariableArray: { // Losing element qualification here is fine. - const VariableArrayType *Vat = cast(Ty); + const VariableArrayType *VAT = cast(Ty); // 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 Lambda = LSI->Lambda; + + // Build the non-static data member. + auto Field = + FieldDecl::Create(Context, Lambda, Loc, Loc, + /*Id*/ nullptr, QType, /*TInfo*/ nullptr, + /*BW*/ nullptr, /*Mutable*/ false, + /*InitStyle*/ ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + Field->setCapturedVLAType(VAT); + Lambda->addDecl(Field); + + LSI->addVLATypeCapture(Loc, Loc, QType, VAT); + } else { + // Immediately mark all referenced vars for CapturedStatements, + // they all are captured by reference. + MarkDeclarationsReferencedInExpr(Size); + } } - QTy = Vat->getElementType(); + QTy = VAT->getElementType(); break; } case Type::FunctionProto: Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1414,6 +1414,13 @@ /*isImplicit=*/true)); continue; } + if (From.isTypeCapture()) { + Captures.push_back( + LambdaCapture(From.getLocation(), IsImplicit, LCK_ByCopy, + /*Var*/ nullptr, From.getEllipsisLoc())); + CaptureInits.push_back(nullptr); + continue; + } VarDecl *Var = From.getVariable(); LambdaCaptureKind Kind = From.isCopyCapture()? LCK_ByCopy : LCK_ByRef; Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -8976,6 +8976,10 @@ getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit()); continue; } + // Captured expression will be recaptured during captured variables + // rebuilding. + if (C->capturesVLAType()) + continue; // Rebuild init-captures, including the implied field declaration. if (C->isInitCapture()) { Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -954,7 +954,13 @@ FD->Mutable = Record[Idx++]; if (int BitWidthOrInitializer = Record[Idx++]) { FD->InitializerOrBitWidth.setInt(BitWidthOrInitializer - 1); - FD->InitializerOrBitWidth.setPointer(Reader.ReadExpr(F)); + if (FD->getDeclContext()->isRecord() && FD->getParent()->isLambda()) { + // Read captured variable length array. + FD->InitializerOrBitWidth.setPointer( + Reader.readType(F, Record, Idx).getAsOpaquePtr()); + } else { + FD->InitializerOrBitWidth.setPointer(Reader.ReadExpr(F)); + } } if (!FD->getDeclName()) { if (FieldDecl *Tmpl = ReadDeclAs(Record, Idx)) Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -663,10 +663,16 @@ void ASTDeclWriter::VisitFieldDecl(FieldDecl *D) { VisitDeclaratorDecl(D); Record.push_back(D->isMutable()); - if (D->InitializerOrBitWidth.getInt() != ICIS_NoInit || - D->InitializerOrBitWidth.getPointer()) { + if ((D->InitializerOrBitWidth.getInt() != ICIS_NoInit || + D->InitializerOrBitWidth.getPointer()) && + !D->hasCapturedVLAType()) { Record.push_back(D->InitializerOrBitWidth.getInt() + 1); - Writer.AddStmt(D->InitializerOrBitWidth.getPointer()); + Writer.AddStmt(static_cast(D->InitializerOrBitWidth.getPointer())); + } else if (D->hasCapturedVLAType()) { + Record.push_back(D->InitializerOrBitWidth.getInt() + 1); + Writer.AddTypeRef( + QualType(static_cast(D->InitializerOrBitWidth.getPointer()), 0), + Record); } else { Record.push_back(0); } 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->capturesVLAType()) return true; if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols())