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 VariableArrayType *getCapturedVLAType() const { + return hasCapturedVLAType() ? static_cast( + InitializerOrBitWidth.getPointer()) + : nullptr; + } + /// \brief Set the captured variable length array type for this field. + void setCapturedVLAType(const VariableArrayType *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 @@ -5354,9 +5354,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 @@ -378,7 +378,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; @@ -410,10 +410,11 @@ return InitExprAndCaptureKind.getInt() == Cap_This; } bool isVariableCapture() const { - return InitExprAndCaptureKind.getInt() != Cap_This; + return InitExprAndCaptureKind.getInt() != Cap_This && !isVLATypeCapture(); } bool isCopyCapture() const { - return InitExprAndCaptureKind.getInt() == Cap_ByCopy; + return InitExprAndCaptureKind.getInt() == Cap_ByCopy && + !isVLATypeCapture(); } bool isReferenceCapture() const { return InitExprAndCaptureKind.getInt() == Cap_ByRef; @@ -421,7 +422,11 @@ bool isBlockCapture() const { return InitExprAndCaptureKind.getInt() == Cap_Block; } - bool isNested() { return VarAndNested.getInt(); } + bool isVLATypeCapture() const { + return InitExprAndCaptureKind.getInt() == Cap_ByCopy && + getVariable() == nullptr; + } + bool isNested() const { return VarAndNested.getInt(); } VarDecl *getVariable() const { return VarAndNested.getPointer(); @@ -440,7 +445,8 @@ QualType getCaptureType() const { return CaptureType; } Expr *getInitExpr() const { - return InitExprAndCaptureKind.getPointer(); + assert(!isVLATypeCapture() && "no init expression for type capture"); + return static_cast(InitExprAndCaptureKind.getPointer()); } }; @@ -475,6 +481,13 @@ CaptureMap[Var] = Captures.size(); } + void addVLATypeCapture(SourceLocation Loc, QualType CaptureType) { + Captures.push_back(Capture(/*Var*/ nullptr, /*isBlock*/ false, + /*isByref*/ false, /*isNested*/ false, Loc, + /*EllipsisLoc*/ SourceLocation(), CaptureType, + /*Cpy*/ nullptr)); + } + void addThisCapture(bool isNested, SourceLocation Loc, QualType CaptureType, Expr *Cpy); @@ -491,7 +504,10 @@ bool isCaptured(VarDecl *Var) const { return CaptureMap.count(Var); } - + + /// \brief Determine whether the given variable-array type has been captured. + bool isVLATypeCaptured(const VariableArrayType *VAT) const; + /// \brief Retrieve the capture of the given variable, if it has been /// captured already. Capture &getCapture(VarDecl *Var) { Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -3261,9 +3261,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(); } @@ -3287,23 +3296,36 @@ } 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(!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() && + InitializerOrBitWidth.getPointer(); +} + +void FieldDecl::setCapturedVLAType(const VariableArrayType *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 //===----------------------------------------------------------------------===// @@ -3524,6 +3546,12 @@ 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 @@ -2991,7 +2991,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/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -852,12 +852,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 @@ -1807,19 +1807,23 @@ 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()) { + auto VAT = CurField->getCapturedVLAType(); + EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->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 @@ -691,6 +691,14 @@ CXXThisValue = EmitLoadOfLValue(ThisLValue, SourceLocation()).getScalarVal(); } + for (auto *FD : MD->getParent()->fields()) { + if (FD->hasCapturedVLAType()) { + auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD), + SourceLocation()).getScalarVal(); + auto VAT = FD->getCapturedVLAType(); + VLASizeMap[VAT->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/ScopeInfo.cpp =================================================================== --- lib/Sema/ScopeInfo.cpp +++ lib/Sema/ScopeInfo.cpp @@ -14,6 +14,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -93,6 +94,15 @@ return BaseInfoTy(D, IsExact); } +bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const { + if (auto *LSI = dyn_cast(this)) + for (auto *FD : LSI->Lambda->fields()) { + if (FD->hasCapturedVLAType() && FD->getCapturedVLAType() == VAT) + return true; + } + return false; +} + FunctionScopeInfo::WeakObjectProfileTy::WeakObjectProfileTy( const ObjCPropertyRefExpr *PropE) : Base(nullptr, true), Property(getBestPropertyDecl(PropE)) { Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -9909,6 +9909,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(); @@ -9925,7 +9926,10 @@ } else if (C.capturesThis()) { LSI->addThisCapture(/*Nested*/ false, C.getLocation(), S.getCurrentThisType(), /*Expr*/ nullptr); + } else { + LSI->addVLATypeCapture(C.getLocation(), I->getType()); } + ++I; } } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -11678,13 +11678,10 @@ return false; } - // Prohibit variably-modified types; they're difficult to deal with. - if (Var->getType()->isVariablyModifiedType() && (IsBlock || IsLambda)) { + // Prohibit variably-modified types in blocks; they're difficult to deal with. + 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(); } @@ -12088,7 +12085,6 @@ return true; } - bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc, TryCaptureKind Kind, SourceLocation EllipsisLoc, bool BuildAndDiagnose, @@ -12225,14 +12221,37 @@ 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)) { + if (!LSI->isVLATypeCaptured(VAT)) { + auto ExprLoc = Size->getExprLoc(); + auto SizeType = Context.getSizeType(); + auto Lambda = LSI->Lambda; + + // Build the non-static data member. + auto Field = FieldDecl::Create( + Context, Lambda, ExprLoc, ExprLoc, + /*Id*/ nullptr, SizeType, /*TInfo*/ nullptr, + /*BW*/ nullptr, /*Mutable*/ false, + /*InitStyle*/ ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + Field->setCapturedVLAType(VAT); + Lambda->addDecl(Field); + + LSI->addVLATypeCapture(ExprLoc, SizeType); + } + } 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.isVLATypeCapture()) { + 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 @@ -9002,6 +9002,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 @@ -955,7 +955,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 @@ -659,10 +659,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/vla-lambda-capturing.cpp =================================================================== --- test/CodeGenCXX/vla-lambda-capturing.cpp +++ test/CodeGenCXX/vla-lambda-capturing.cpp @@ -0,0 +1,172 @@ +// 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-DAG: [[CAP_TYPE3:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]], [[INTPTR_T]]*, [[INTPTR_T]]* } +// CHECK-DAG: [[CAP_TYPE4:%.+]] = type { [[INTPTR_T]]*, [[INTPTR_T]], [[INTPTR_T]]*, [[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; + }(); +} + +template +intptr_t getSize(T); + +template +void b(intptr_t n, T arg) { + typedef intptr_t ArrTy[getSize(arg)]; + ArrTy buffer2; + ArrTy buffer1[n + arg]; + intptr_t a; + [&]() { + n = sizeof(buffer1[n]); + [&](){ + n = sizeof(buffer2); + n = sizeof(buffer1); + }(); + }(); +} + +// 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: call void [[B_INT:@.+]]([[INTPTR_T]] 12, [[INTPTR_T]] 13) + b((intptr_t)12, (intptr_t)13); + // 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 [[B_INT]]([[INTPTR_T]] +// CHECK: [[N_ADDR:%.+]] = alloca [[INTPTR_T]] +// CHECK: [[SIZE1:%.+]] = call [[INTPTR_T]] +// CHECK: call i{{.+}}* @llvm.stacksave() +// CHECK: [[BUFFER2_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] [[SIZE1]] +// CHECK: [[SIZE2:%.+]] = add +// CHECK: [[BUFFER1_ADDR:%.+]] = alloca [[INTPTR_T]], [[INTPTR_T]] +// CHECK: [[CAP_N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG:%.+]], i{{.+}} 0, i{{.+}} 0 +// CHECK: store [[INTPTR_T]]* [[N_ADDR]], [[INTPTR_T]]** [[CAP_N_ADDR_REF]] +// CHECK: [[CAP_SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 1 +// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[CAP_SIZE2_REF]] +// CHECK: [[CAP_SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 2 +// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[CAP_SIZE1_REF]] +// CHECK: [[CAP_BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 3 +// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER1_ADDR_REF]] +// CHECK: [[CAP_BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[CAP_ARG]], i{{.+}} 0, i{{.+}} 4 +// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR]], [[INTPTR_T]]** [[CAP_BUFFER2_ADDR_REF]] +// CHECK: call void [[B_INT_LAMBDA:@.+]]([[CAP_TYPE3]]* [[CAP_ARG]]) +// CHECK: call void @llvm.stackrestore( +// CHECK: ret void + +// CHECK: define {{.*}} void [[B_INT_LAMBDA]]([[CAP_TYPE3]]* +// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1 +// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]] +// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2 +// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]] +// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]] +// CHECK: [[N:%.+]] = load [[INTPTR_T]]* [[N_ADDR]] +// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]] +// CHECK: [[ELEM_OFFSET:%.+]] = mul {{.*}} i{{[0-9]+}} [[N]], [[SIZE1]] +// CHECK: [[ELEM_ADDR:%.+]] = getelementptr inbounds [[INTPTR_T]]* [[BUFFER1_ADDR]], i{{[0-9]+}} [[ELEM_OFFSET]] +// CHECK: [[SIZEOF:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]] +// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[N_ADDR:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF]] +// CHECK: store [[INTPTR_T]] {{%.+}}, [[INTPTR_T]]* [[N_ADDR]] +// CHECK: [[N_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[N_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 0 +// CHECK: [[N_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[N_ADDR_REF_ORIG]] +// CHECK: store [[INTPTR_T]]* [[N_ADDR_ORIG]], [[INTPTR_T]]** [[N_ADDR_REF]] +// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 1 +// CHECK: store i{{[0-9]+}} [[SIZE1]], i{{[0-9]+}}* [[SIZE1_REF]] +// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 2 +// CHECK: [[BUFFER2_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4 +// CHECK: [[BUFFER2_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF_ORIG]] +// CHECK: store [[INTPTR_T]]* [[BUFFER2_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER2_ADDR_REF]] +// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: store i{{[0-9]+}} [[SIZE2]], i{{[0-9]+}}* [[SIZE2_REF]] +// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[CAP]], i{{[0-9]+}} 0, i{{[0-9]+}} 4 +// CHECK: [[BUFFER1_ADDR_REF_ORIG:%.+]] = getelementptr inbounds [[CAP_TYPE3]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: [[BUFFER1_ADDR_ORIG:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF_ORIG]] +// CHECK: store [[INTPTR_T]]* [[BUFFER1_ADDR_ORIG]], [[INTPTR_T]]** [[BUFFER1_ADDR_REF]] +// CHECK: call void [[B_INT_LAMBDA_LAMBDA:@.+]]([[CAP_TYPE4]]* [[CAP]]) +// CHECK: ret void + +// CHECK: define {{.*}} void [[B_INT_LAMBDA_LAMBDA]]([[CAP_TYPE4]]* +// CHECK: [[SIZE1_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS:%.+]], i{{[0-9]+}} 0, i{{[0-9]+}} 1 +// CHECK: [[SIZE1:%.+]] = load i{{[0-9]+}}* [[SIZE1_REF]] +// CHECK: [[SIZE2_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 3 +// CHECK: [[SIZE2:%.+]] = load i{{[0-9]+}}* [[SIZE2_REF]] +// CHECK: [[BUFFER2_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 2 +// CHECK: [[BUFFER2_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER2_ADDR_REF]] +// CHECK: [[SIZEOF_BUFFER2:%.+]] = mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[SIZE1]] +// CHECK: [[BUFFER1_ADDR_REF:%.+]] = getelementptr inbounds [[CAP_TYPE4]]* [[THIS]], i{{[0-9]+}} 0, i{{[0-9]+}} 4 +// CHECK: [[BUFFER1_ADDR:%.+]] = load [[INTPTR_T]]** [[BUFFER1_ADDR_REF]] +// CHECK: [[MUL:%.+]] = mul {{.*}} i{{[0-9]+}} [[SIZE2]], [[SIZE1]] +// CHECK: mul {{.*}} i{{[0-9]+}} {{[0-9]+}}, [[MUL]] +// 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 @@ -156,7 +156,7 @@ } bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { - if (C->capturesThis()) + if (C->capturesThis() || C->capturesVLAType()) return true; if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols())