forked from OSchip/llvm-project
[C++11] Support for capturing of variable length arrays in lambda expression.
Differential Revision: http://reviews.llvm.org/D4368 llvm-svn: 216649
This commit is contained in:
parent
c5cafbb074
commit
39c81e2816
|
@ -2156,8 +2156,9 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
|
|||
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.
|
||||
|
@ -2165,7 +2166,7 @@ class FieldDecl : public DeclaratorDecl, public Mergeable<FieldDecl> {
|
|||
/// 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<Expr *, 2, unsigned> InitializerOrBitWidth;
|
||||
llvm::PointerIntPair<void *, 2, unsigned> InitializerOrBitWidth;
|
||||
protected:
|
||||
FieldDecl(Kind DK, DeclContext *DC, SourceLocation StartLoc,
|
||||
SourceLocation IdLoc, IdentifierInfo *Id,
|
||||
|
@ -2193,11 +2194,8 @@ public:
|
|||
/// 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(); }
|
||||
|
@ -2209,7 +2207,9 @@ public:
|
|||
bool isAnonymousStructOrUnion() const;
|
||||
|
||||
Expr *getBitWidth() const {
|
||||
return isBitField() ? InitializerOrBitWidth.getPointer() : nullptr;
|
||||
return isBitField()
|
||||
? static_cast<Expr *>(InitializerOrBitWidth.getPointer())
|
||||
: nullptr;
|
||||
}
|
||||
unsigned getBitWidthValue(const ASTContext &Ctx) const;
|
||||
|
||||
|
@ -2239,8 +2239,9 @@ public:
|
|||
/// 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<Expr *>(InitializerOrBitWidth.getPointer())
|
||||
: nullptr;
|
||||
}
|
||||
/// setInClassInitializer - Set the C++11 in-class initializer for this
|
||||
/// member.
|
||||
|
@ -2253,6 +2254,18 @@ public:
|
|||
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<const VariableArrayType *>(
|
||||
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 {
|
||||
|
@ -3144,6 +3157,10 @@ public:
|
|||
/// \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
|
||||
|
|
|
@ -68,13 +68,23 @@ public:
|
|||
|
||||
/// \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 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();
|
||||
|
|
|
@ -5357,9 +5357,6 @@ let CategoryName = "Lambda Issue" in {
|
|||
"'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">;
|
||||
|
|
|
@ -34,7 +34,8 @@ enum LambdaCaptureDefault {
|
|||
enum LambdaCaptureKind {
|
||||
LCK_This, ///< Capturing the \c this pointer
|
||||
LCK_ByCopy, ///< Capturing by copy (a.k.a., by value)
|
||||
LCK_ByRef ///< Capturing by reference
|
||||
LCK_ByRef, ///< Capturing by reference
|
||||
LCK_VLAType ///< Capturing variable-length array type
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -378,7 +378,7 @@ public:
|
|||
/// 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<Expr*, 2, CaptureKind> InitExprAndCaptureKind;
|
||||
llvm::PointerIntPair<void *, 2, CaptureKind> InitExprAndCaptureKind;
|
||||
|
||||
/// \brief The source location at which the first capture occurred.
|
||||
SourceLocation Loc;
|
||||
|
@ -410,10 +410,11 @@ public:
|
|||
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 @@ public:
|
|||
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 @@ public:
|
|||
QualType getCaptureType() const { return CaptureType; }
|
||||
|
||||
Expr *getInitExpr() const {
|
||||
return InitExprAndCaptureKind.getPointer();
|
||||
assert(!isVLATypeCapture() && "no init expression for type capture");
|
||||
return static_cast<Expr *>(InitExprAndCaptureKind.getPointer());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -475,6 +481,13 @@ public:
|
|||
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 @@ public:
|
|||
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) {
|
||||
|
|
|
@ -3261,9 +3261,18 @@ bool FieldDecl::isAnonymousStructOrUnion() const {
|
|||
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<Expr *>(InitializerOrBitWidth.getPointer());
|
||||
return BitWidth->EvaluateKnownConstInt(Ctx).getZExtValue();
|
||||
}
|
||||
|
||||
|
@ -3287,23 +3296,36 @@ unsigned FieldDecl::getFieldIndex() const {
|
|||
}
|
||||
|
||||
SourceRange FieldDecl::getSourceRange() const {
|
||||
if (const Expr *E = InitializerOrBitWidth.getPointer())
|
||||
if (const Expr *E =
|
||||
static_cast<const Expr *>(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<VariableArrayType *>(VLAType));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TagDecl Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -3524,6 +3546,12 @@ bool RecordDecl::isInjectedClassName() const {
|
|||
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();
|
||||
|
|
|
@ -909,16 +909,21 @@ LambdaCapture::LambdaCapture(SourceLocation Loc, bool Implicit,
|
|||
case LCK_ByRef:
|
||||
assert(Var && "capture must have a variable!");
|
||||
break;
|
||||
case LCK_VLAType:
|
||||
assert(!Var && "VLA type capture cannot have a variable!");
|
||||
Bits |= Capture_ByCopy;
|
||||
break;
|
||||
}
|
||||
DeclAndBits.setInt(Bits);
|
||||
}
|
||||
|
||||
LambdaCaptureKind LambdaCapture::getCaptureKind() const {
|
||||
Decl *D = DeclAndBits.getPointer();
|
||||
bool CapByCopy = DeclAndBits.getInt() & Capture_ByCopy;
|
||||
if (!D)
|
||||
return LCK_This;
|
||||
return CapByCopy ? LCK_VLAType : LCK_This;
|
||||
|
||||
return (DeclAndBits.getInt() & Capture_ByCopy) ? LCK_ByCopy : LCK_ByRef;
|
||||
return CapByCopy ? LCK_ByCopy : LCK_ByRef;
|
||||
}
|
||||
|
||||
LambdaExpr::LambdaExpr(QualType T,
|
||||
|
|
|
@ -1742,6 +1742,8 @@ void StmtPrinter::VisitLambdaExpr(LambdaExpr *Node) {
|
|||
case LCK_ByCopy:
|
||||
OS << C->getCapturedVar()->getName();
|
||||
break;
|
||||
case LCK_VLAType:
|
||||
llvm_unreachable("VLA type in explicit captures.");
|
||||
}
|
||||
|
||||
if (C->isInitCapture())
|
||||
|
|
|
@ -1040,6 +1040,8 @@ StmtProfiler::VisitLambdaExpr(const LambdaExpr *S) {
|
|||
VisitDecl(C->getCapturedVar());
|
||||
ID.AddBoolean(C->isPackExpansion());
|
||||
break;
|
||||
case LCK_VLAType:
|
||||
llvm_unreachable("VLA type in explicit captures.");
|
||||
}
|
||||
}
|
||||
// Note: If we actually needed to be able to match lambda
|
||||
|
|
|
@ -852,12 +852,11 @@ CollectRecordLambdaFields(const CXXRecordDecl *CXXDecl,
|
|||
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();
|
||||
|
|
|
@ -1805,19 +1805,23 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(llvm::Value *Value,
|
|||
|
||||
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->hasCapturedVLAType()) {
|
||||
auto VAT = CurField->getCapturedVLAType();
|
||||
EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV);
|
||||
} else {
|
||||
ArrayRef<VarDecl *> ArrayIndexes;
|
||||
if (CurField->getType()->isArrayType())
|
||||
ArrayIndexes = E->getCaptureInitIndexVars(i);
|
||||
EmitInitializerForField(*CurField, LV, *i, ArrayIndexes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -691,6 +691,14 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
|
|||
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
|
||||
|
|
|
@ -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 @@ FunctionScopeInfo::WeakObjectProfileTy::getBaseInfo(const Expr *E) {
|
|||
return BaseInfoTy(D, IsExact);
|
||||
}
|
||||
|
||||
bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const {
|
||||
if (auto *LSI = dyn_cast<LambdaScopeInfo>(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)) {
|
||||
|
|
|
@ -9959,6 +9959,7 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
|
|||
|
||||
// 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();
|
||||
|
@ -9975,7 +9976,10 @@ static void RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator,
|
|||
} else if (C.capturesThis()) {
|
||||
LSI->addThisCapture(/*Nested*/ false, C.getLocation(),
|
||||
S.getCurrentThisType(), /*Expr*/ nullptr);
|
||||
} else {
|
||||
LSI->addVLATypeCapture(C.getLocation(), I->getType());
|
||||
}
|
||||
++I;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11681,13 +11681,10 @@ static bool isVariableCapturable(CapturingScopeInfo *CSI, VarDecl *Var,
|
|||
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();
|
||||
}
|
||||
|
@ -12091,7 +12088,6 @@ static bool captureInLambda(LambdaScopeInfo *LSI,
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc,
|
||||
TryCaptureKind Kind, SourceLocation EllipsisLoc,
|
||||
bool BuildAndDiagnose,
|
||||
|
@ -12228,14 +12224,37 @@ bool Sema::tryCaptureVariable(VarDecl *Var, SourceLocation ExprLoc,
|
|||
break;
|
||||
case Type::VariableArray: {
|
||||
// Losing element qualification here is fine.
|
||||
const VariableArrayType *Vat = cast<VariableArrayType>(Ty);
|
||||
const VariableArrayType *VAT = cast<VariableArrayType>(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<LambdaScopeInfo>(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:
|
||||
|
|
|
@ -1414,6 +1414,12 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
|
|||
/*isImplicit=*/true));
|
||||
continue;
|
||||
}
|
||||
if (From.isVLATypeCapture()) {
|
||||
Captures.push_back(
|
||||
LambdaCapture(From.getLocation(), IsImplicit, LCK_VLAType));
|
||||
CaptureInits.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
VarDecl *Var = From.getVariable();
|
||||
LambdaCaptureKind Kind = From.isCopyCapture()? LCK_ByCopy : LCK_ByRef;
|
||||
|
|
|
@ -9002,6 +9002,10 @@ TreeTransform<Derived>::TransformLambdaScope(LambdaExpr *E,
|
|||
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()) {
|
||||
|
|
|
@ -976,7 +976,13 @@ void ASTDeclReader::VisitFieldDecl(FieldDecl *FD) {
|
|||
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<FieldDecl>(Record, Idx))
|
||||
|
@ -1301,6 +1307,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
|
|||
LambdaCaptureKind Kind = static_cast<LambdaCaptureKind>(Record[Idx++]);
|
||||
switch (Kind) {
|
||||
case LCK_This:
|
||||
case LCK_VLAType:
|
||||
*ToCapture++ = Capture(Loc, IsImplicit, Kind, nullptr,SourceLocation());
|
||||
break;
|
||||
case LCK_ByCopy:
|
||||
|
|
|
@ -5582,6 +5582,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
|
|||
Record.push_back(Capture.getCaptureKind());
|
||||
switch (Capture.getCaptureKind()) {
|
||||
case LCK_This:
|
||||
case LCK_VLAType:
|
||||
break;
|
||||
case LCK_ByCopy:
|
||||
case LCK_ByRef:
|
||||
|
|
|
@ -666,10 +666,16 @@ void ASTDeclWriter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
|
|||
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<Expr *>(D->InitializerOrBitWidth.getPointer()));
|
||||
} else if (D->hasCapturedVLAType()) {
|
||||
Record.push_back(D->InitializerOrBitWidth.getInt() + 1);
|
||||
Writer.AddTypeRef(
|
||||
QualType(static_cast<Type *>(D->InitializerOrBitWidth.getPointer()), 0),
|
||||
Record);
|
||||
} else {
|
||||
Record.push_back(0);
|
||||
}
|
||||
|
|
|
@ -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 <typename T>
|
||||
void f(T n, T m) {
|
||||
intptr_t buffer[n + m];
|
||||
[&buffer]() {
|
||||
__typeof(buffer) x;
|
||||
}();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
intptr_t getSize(T);
|
||||
|
||||
template <typename T>
|
||||
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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ public:
|
|||
}
|
||||
|
||||
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) {
|
||||
if (C->capturesThis())
|
||||
if (C->capturesThis() || C->capturesVLAType())
|
||||
return true;
|
||||
|
||||
if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols())
|
||||
|
|
Loading…
Reference in New Issue