Updated version after last review. Unfortunately, I don't think it is possible
to pass captured expression as a VariableLengthArray *. I have to capture expr
of VariableLengthArray * and then cast it to SizeExpr->getType() type to make
lambda capture this type, not VariableLengthArray *. I have to pass actual
value of SizeExpr to the Lambda in this field, because it is defined only in
calling function and in Lambda it can be received only in one of captured
fields.
http://reviews.llvm.org/D4368
Files:
include/clang/AST/Decl.h
include/clang/AST/LambdaCapture.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/Decl.cpp
lib/AST/Expr.cpp
lib/AST/ExprCXX.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/CodeGen/CGDebugInfo.cpp
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/TreeTransform.h
test/CodeGenCXX/instantiate-typeof-vla.cpp
test/SemaTemplate/instantiate-typeof.cpp
tools/libclang/IndexBody.cpp
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 {
@@ -3137,6 +3147,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<VarDecl>(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,15 @@
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();
@@ -3290,17 +3299,31 @@
}
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 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 {
+ return getDeclContext()->isRecord() && getParent()->isLambda() &&
+ getInClassInitStyle() == ICIS_NoInit &&
+ InitializerOrBitWidth.getPointer();
+}
+
+void FieldDecl::setCapturedVLABoundExpr(Expr *CapturedExpr) {
+ assert(getParent()->isLambda() && "capturing expression in non-lambda.");
+ assert(!InitializerOrBitWidth.getPointer() && !hasInClassInitializer() &&
+ "bit width, initializer or captured expr already set");
+ InitializerOrBitWidth.setPointer(CapturedExpr);
+}
+
//===----------------------------------------------------------------------===//
// TagDecl Implementation
//===----------------------------------------------------------------------===//
@@ -3521,6 +3544,13 @@
cast<RecordDecl>(getDeclContext())->getDeclName() == getDeclName();
}
+bool RecordDecl::isLambda() const {
+ if (auto RD = dyn_cast<CXXRecordDecl>(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
@@ -2989,7 +2989,7 @@
const LambdaExpr *LE = cast<LambdaExpr>(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,34 @@
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<VarDecl *> ArrayIndexes;
- if (CurField->getType()->isArrayType())
- ArrayIndexes = E->getCaptureInitIndexVars(i);
- EmitInitializerForField(*CurField, LV, *i, ArrayIndexes);
+ if (CurField->hasCapturedVLABoundExpr()) {
+ assert((*i)
+ ->IgnoreImpCasts()
+ ->getType()
+ ->getPointeeType()
+ ->isVariableArrayType() &&
+ "Captured expression is not a binary operator.");
+ auto QArrayType = cast<VariableArrayType>((*i)
+ ->IgnoreImpCasts()
+ ->getType()
+ ->getPointeeType()
+ ->getAsArrayTypeUnsafe());
+ EmitStoreThroughLValue(RValue::get(VLASizeMap[QArrayType->getSizeExpr()]),
+ LV);
+ } else {
+ ArrayRef<VarDecl *> 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,19 @@
CXXThisValue = EmitLoadOfLValue(ThisLValue,
SourceLocation()).getScalarVal();
}
+ for (auto *FD : MD->getParent()->fields()) {
+ if (FD->hasCapturedVLABoundExpr()) {
+ auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD),
+ SourceLocation()).getScalarVal();
+ auto QArrayType =
+ cast<VariableArrayType>(FD->getCapturedVLABoundExpr()
+ ->IgnoreImpCasts()
+ ->getType()
+ ->getPointeeType()
+ ->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,35 @@
// 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<LambdaScopeInfo>(CSI)) {
+ auto Loc = Size->getExprLoc();
+ auto QType = Size->getType();
+ auto QArrayType = Context.getPointerType(QualType(Vat, 0));
+ auto TInfo = Context.getTrivialTypeSourceInfo(QType, Loc);
+ ExprResult CapExpr =
+ new (Context) CXXNullPtrLiteralExpr(QArrayType, Loc);
+ auto CastKind = PrepareScalarCast(CapExpr, QType);
+ CapExpr = ImpCastExprToType(CapExpr.get(), QType, CastKind);
+ auto Lambda = LSI->Lambda;
+
+ // Build the non-static data member.
+ auto Field = FieldDecl::Create(Context, Lambda, Loc, Loc,
+ /*Id*/ nullptr, QType, TInfo,
+ CapExpr.get(), /*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, CapExpr.get());
+ } 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 <typename T> 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<typename T> 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<int>(1); // expected-note {{in instantiation}}
+ f<int>(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())
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits