aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaExpr.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2012-02-12 18:42:33 +0000
committerDouglas Gregor <dgregor@apple.com>2012-02-12 18:42:33 +0000
commitf8af98286022f72157d84951b48fde5fb369ab29 (patch)
tree9c6f89ee5d11a63c74f5dd8d9a41476992a4b070 /lib/Sema/SemaExpr.cpp
parent6dc00f6e98a00bd1c332927c3e04918d7e8b0d4f (diff)
Within the body of a lambda expression, decltype((x)) for an
id-expression 'x' will compute the type based on the assumption that 'x' will be captured, even if it isn't captured, per C++11 [expr.prim.lambda]p18. There are two related refactors that go into implementing this: 1) Split out the check that determines whether we should capture a particular variable reference, along with the computation of the type of the field, from the actual act of capturing the variable. 2) Always compute the result of decltype() within Sema, rather than AST, because the decltype() computation is now context-sensitive. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150347 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaExpr.cpp')
-rw-r--r--lib/Sema/SemaExpr.cpp202
1 files changed, 122 insertions, 80 deletions
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index ce5956ae2f..9bd1074116 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -9566,12 +9566,7 @@ static bool shouldAddConstForScope(CapturingScopeInfo *CSI, VarDecl *VD) {
return false;
}
-/// \brief Capture the given variable in the given lambda expression.
-static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
- VarDecl *Var, QualType Type,
- SourceLocation Loc, bool ByRef) {
- CXXRecordDecl *Lambda = LSI->Lambda;
- QualType FieldType;
+QualType Sema::getLambdaCaptureFieldType(QualType T, bool ByRef) {
if (ByRef) {
// C++11 [expr.prim.lambda]p15:
// An entity is captured by reference if it is implicitly or
@@ -9579,28 +9574,34 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
// unspecified whether additional unnamed non-static data
// members are declared in the closure type for entities
// captured by reference.
- FieldType = S.Context.getLValueReferenceType(Type.getNonReferenceType());
- } else {
- // C++11 [expr.prim.lambda]p14:
- // For each entity captured by copy, an unnamed non-static
- // data member is declared in the closure type. The
- // declaration order of these members is unspecified. The type
- // of such a data member is the type of the corresponding
- // captured entity if the entity is not a reference to an
- // object, or the referenced type otherwise. [Note: If the
- // captured entity is a reference to a function, the
- // corresponding data member is also a reference to a
- // function. - end note ]
- if (const ReferenceType *RefType = Type->getAs<ReferenceType>()) {
- if (!RefType->getPointeeType()->isFunctionType())
- FieldType = RefType->getPointeeType();
- else
- FieldType = Type;
- } else {
- FieldType = Type;
- }
+ return Context.getLValueReferenceType(T.getNonReferenceType());
+ }
+
+ // C++11 [expr.prim.lambda]p14:
+ // For each entity captured by copy, an unnamed non-static
+ // data member is declared in the closure type. The
+ // declaration order of these members is unspecified. The type
+ // of such a data member is the type of the corresponding
+ // captured entity if the entity is not a reference to an
+ // object, or the referenced type otherwise. [Note: If the
+ // captured entity is a reference to a function, the
+ // corresponding data member is also a reference to a
+ // function. - end note ]
+ if (const ReferenceType *RefType = T->getAs<ReferenceType>()) {
+ if (!RefType->getPointeeType()->isFunctionType())
+ return RefType->getPointeeType();
}
+ return T;
+}
+
+/// \brief Capture the given variable in the given lambda expression.
+static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
+ VarDecl *Var, QualType Type,
+ SourceLocation Loc, bool ByRef) {
+ CXXRecordDecl *Lambda = LSI->Lambda;
+ QualType FieldType = S.getLambdaCaptureFieldType(Type, ByRef);
+
// Build the non-static data member.
FieldDecl *Field
= FieldDecl::Create(S.Context, Lambda, Loc, Loc, 0, FieldType,
@@ -9715,20 +9716,20 @@ static ExprResult captureInLambda(Sema &S, LambdaScopeInfo *LSI,
return Result;
}
-// Check if the variable needs to be captured; if so, try to perform
-// the capture.
-// FIXME: Add support for explicit captures.
-void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc,
- TryCaptureKind Kind) {
+bool Sema::canCaptureVariable(VarDecl *Var, SourceLocation Loc, bool Explicit,
+ bool Diagnose, QualType &Type,
+ unsigned &FunctionScopesIndex, bool &Nested) {
+ Type = Var->getType();
+ FunctionScopesIndex = FunctionScopes.size() - 1;
+ Nested = false;
+
DeclContext *DC = CurContext;
- if (var->getDeclContext() == DC) return;
- if (!var->hasLocalStorage()) return;
+ if (Var->getDeclContext() == DC) return false;
+ if (!Var->hasLocalStorage()) return false;
- // Actually try to capture it.
- QualType type = var->getType();
- bool Nested = false;
+ bool HasBlocksAttr = Var->hasAttr<BlocksAttr>();
- unsigned functionScopesIndex = FunctionScopes.size() - 1;
+ // Figure out whether we can capture the variable.
do {
// Only block literals and lambda expressions can capture; other
// scopes don't work.
@@ -9736,84 +9737,125 @@ void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc,
if (isa<BlockDecl>(DC))
ParentDC = DC->getParent();
else if (isa<CXXMethodDecl>(DC) &&
+ cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
cast<CXXRecordDecl>(DC->getParent())->isLambda())
ParentDC = DC->getParent()->getParent();
- else
- return diagnoseUncapturableValueReference(*this, loc, var, DC);
+ else {
+ if (Diagnose)
+ diagnoseUncapturableValueReference(*this, Loc, Var, DC);
+ return false;
+ }
CapturingScopeInfo *CSI =
- cast<CapturingScopeInfo>(FunctionScopes[functionScopesIndex]);
+ cast<CapturingScopeInfo>(FunctionScopes[FunctionScopesIndex]);
// Check whether we've already captured it.
- if (CSI->CaptureMap.count(var)) {
+ if (CSI->CaptureMap.count(Var)) {
// If we found a capture, any subcaptures are nested
Nested = true;
- if (shouldAddConstForScope(CSI, var))
- type.addConst();
+ if (shouldAddConstForScope(CSI, Var))
+ Type.addConst();
break;
}
- functionScopesIndex--;
- DC = ParentDC;
- } while (var->getDeclContext() != DC);
-
- bool hasBlocksAttr = var->hasAttr<BlocksAttr>();
-
- for (unsigned i = functionScopesIndex + 1,
- e = FunctionScopes.size(); i != e; ++i) {
- CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[i]);
- bool isBlock = isa<BlockScopeInfo>(CSI);
- bool isLambda = isa<LambdaScopeInfo>(CSI);
+ bool IsBlock = isa<BlockScopeInfo>(CSI);
+ bool IsLambda = isa<LambdaScopeInfo>(CSI);
// Lambdas are not allowed to capture unnamed variables
// (e.g. anonymous unions).
// FIXME: The C++11 rule don't actually state this explicitly, but I'm
// assuming that's the intent.
- if (isLambda && !var->getDeclName()) {
- Diag(loc, diag::err_lambda_capture_anonymous_var);
- Diag(var->getLocation(), diag::note_declared_at);
- return;
+ if (IsLambda && !Var->getDeclName()) {
+ if (Diagnose) {
+ Diag(Loc, diag::err_lambda_capture_anonymous_var);
+ Diag(Var->getLocation(), diag::note_declared_at);
+ }
+ return false;
}
// Prohibit variably-modified types; they're difficult to deal with.
- if (type->isVariablyModifiedType()) {
- if (isBlock)
- Diag(loc, diag::err_ref_vm_type);
- else
- Diag(loc, diag::err_lambda_capture_vm_type) << var->getDeclName();
- Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName();
- return;
+ if (Type->isVariablyModifiedType()) {
+ if (Diagnose) {
+ if (IsBlock)
+ Diag(Loc, diag::err_ref_vm_type);
+ else
+ Diag(Loc, diag::err_lambda_capture_vm_type) << Var->getDeclName();
+ Diag(Var->getLocation(), diag::note_previous_decl)
+ << Var->getDeclName();
+ }
+ return false;
}
// Blocks are not allowed to capture arrays.
- if (isBlock && type->isArrayType()) {
- Diag(loc, diag::err_ref_array_type);
- Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName();
- return;
+ if (IsBlock && Type->isArrayType()) {
+ if (Diagnose) {
+ Diag(Loc, diag::err_ref_array_type);
+ Diag(Var->getLocation(), diag::note_previous_decl)
+ << Var->getDeclName();
+ }
+ return false;
}
// Lambdas are not allowed to capture __block variables; they don't
// support the expected semantics.
- if (isLambda && hasBlocksAttr) {
- Diag(loc, diag::err_lambda_capture_block) << var->getDeclName();
- Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName();
- return;
+ if (IsLambda && HasBlocksAttr) {
+ if (Diagnose) {
+ Diag(Loc, diag::err_lambda_capture_block)
+ << Var->getDeclName();
+ Diag(Var->getLocation(), diag::note_previous_decl)
+ << Var->getDeclName();
+ }
+ return false;
}
+ if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None && !Explicit) {
+ // No capture-default
+ if (Diagnose) {
+ Diag(Loc, diag::err_lambda_impcap) << Var->getDeclName();
+ Diag(Var->getLocation(), diag::note_previous_decl)
+ << Var->getDeclName();
+ Diag(cast<LambdaScopeInfo>(CSI)->Lambda->getLocStart(),
+ diag::note_lambda_decl);
+ }
+ return false;
+ }
+
+ FunctionScopesIndex--;
+ DC = ParentDC;
+ Explicit = false;
+ } while (!Var->getDeclContext()->Equals(DC));
+
+ ++FunctionScopesIndex;
+ return !Type->isVariablyModifiedType();
+}
+
+// Check if the variable needs to be captured; if so, try to perform
+// the capture.
+void Sema::TryCaptureVar(VarDecl *var, SourceLocation loc,
+ TryCaptureKind Kind) {
+ QualType type;
+ unsigned functionScopesIndex;
+ bool Nested;
+ // Determine whether we can capture this variable, and where to
+ // start capturing.
+ if (!canCaptureVariable(var, loc, /*Explicit=*/Kind != TryCapture_Implicit,
+ /*Diagnose=*/true, type, functionScopesIndex, Nested))
+ return;
+
+ bool hasBlocksAttr = var->hasAttr<BlocksAttr>();
+
+ for (unsigned i = functionScopesIndex,
+ e = FunctionScopes.size(); i != e; ++i) {
+ CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[i]);
+ bool isLambda = isa<LambdaScopeInfo>(CSI);
+
bool byRef;
bool isInnermostCapture = (i == e - 1);
if (isInnermostCapture && Kind == TryCapture_ExplicitByVal) {
byRef = false;
} else if (isInnermostCapture && Kind == TryCapture_ExplicitByRef) {
byRef = true;
- } else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_None) {
- // No capture-default
- Diag(loc, diag::err_lambda_impcap) << var->getDeclName();
- Diag(var->getLocation(), diag::note_previous_decl) << var->getDeclName();
- Diag(cast<LambdaScopeInfo>(CSI)->Lambda->getLocStart(),
- diag::note_lambda_decl);
- return;
} else if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval) {
// capture-default '='
byRef = false;