forked from OSchip/llvm-project
Refactor capture in blocks to use new-style capture hooks. Start adding a bit of the code for lambdas. The only visible changes are that we use the C++11 odr-used rules to figure out when a variable is captured, and type-checking in lambdas is slightly more accurate.
llvm-svn: 149663
This commit is contained in:
parent
d1956e46dd
commit
9bb33f572f
|
@ -1191,231 +1191,6 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks) {
|
|||
StringTokLocs.size()));
|
||||
}
|
||||
|
||||
enum CaptureResult {
|
||||
/// No capture is required.
|
||||
CR_NoCapture,
|
||||
|
||||
/// A capture is required.
|
||||
CR_Capture,
|
||||
|
||||
/// A by-ref capture is required.
|
||||
CR_CaptureByRef,
|
||||
|
||||
/// An error occurred when trying to capture the given variable.
|
||||
CR_Error
|
||||
};
|
||||
|
||||
/// Diagnose an uncapturable value reference.
|
||||
///
|
||||
/// \param var - the variable referenced
|
||||
/// \param DC - the context which we couldn't capture through
|
||||
static CaptureResult
|
||||
diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
|
||||
VarDecl *var, DeclContext *DC) {
|
||||
switch (S.ExprEvalContexts.back().Context) {
|
||||
case Sema::Unevaluated:
|
||||
case Sema::ConstantEvaluated:
|
||||
// The argument will never be evaluated at runtime, so don't complain.
|
||||
return CR_NoCapture;
|
||||
|
||||
case Sema::PotentiallyEvaluated:
|
||||
case Sema::PotentiallyEvaluatedIfUsed:
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't diagnose about capture if we're not actually in code right
|
||||
// now; in general, there are more appropriate places that will
|
||||
// diagnose this.
|
||||
if (!S.CurContext->isFunctionOrMethod()) return CR_NoCapture;
|
||||
|
||||
// Certain madnesses can happen with parameter declarations, which
|
||||
// we want to ignore.
|
||||
if (isa<ParmVarDecl>(var)) {
|
||||
// - If the parameter still belongs to the translation unit, then
|
||||
// we're actually just using one parameter in the declaration of
|
||||
// the next. This is useful in e.g. VLAs.
|
||||
if (isa<TranslationUnitDecl>(var->getDeclContext()))
|
||||
return CR_NoCapture;
|
||||
|
||||
// - This particular madness can happen in ill-formed default
|
||||
// arguments; claim it's okay and let downstream code handle it.
|
||||
if (S.CurContext == var->getDeclContext()->getParent())
|
||||
return CR_NoCapture;
|
||||
}
|
||||
|
||||
DeclarationName functionName;
|
||||
if (FunctionDecl *fn = dyn_cast<FunctionDecl>(var->getDeclContext()))
|
||||
functionName = fn->getDeclName();
|
||||
// FIXME: variable from enclosing block that we couldn't capture from!
|
||||
|
||||
S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_function)
|
||||
<< var->getIdentifier() << functionName;
|
||||
S.Diag(var->getLocation(), diag::note_local_variable_declared_here)
|
||||
<< var->getIdentifier();
|
||||
|
||||
return CR_Error;
|
||||
}
|
||||
|
||||
/// There is a well-formed capture at a particular scope level;
|
||||
/// propagate it through all the nested blocks.
|
||||
static CaptureResult propagateCapture(Sema &S, unsigned ValidScopeIndex,
|
||||
const CapturingScopeInfo::Capture &Cap) {
|
||||
// Update all the inner blocks with the capture information.
|
||||
for (unsigned i = ValidScopeIndex + 1, e = S.FunctionScopes.size();
|
||||
i != e; ++i) {
|
||||
BlockScopeInfo *innerBlock = cast<BlockScopeInfo>(S.FunctionScopes[i]);
|
||||
innerBlock->AddCapture(Cap.getVariable(), Cap.isReferenceCapture(),
|
||||
/*nested*/ true, Cap.getLocation(),
|
||||
Cap.getCopyExpr());
|
||||
}
|
||||
|
||||
return Cap.isReferenceCapture() ? CR_CaptureByRef : CR_Capture;
|
||||
}
|
||||
|
||||
/// shouldCaptureValueReference - Determine if a reference to the
|
||||
/// given value in the current context requires a variable capture.
|
||||
///
|
||||
/// This also keeps the captures set in the BlockScopeInfo records
|
||||
/// up-to-date.
|
||||
static CaptureResult shouldCaptureValueReference(Sema &S, SourceLocation loc,
|
||||
ValueDecl *Value) {
|
||||
// Only variables ever require capture.
|
||||
VarDecl *var = dyn_cast<VarDecl>(Value);
|
||||
if (!var) return CR_NoCapture;
|
||||
|
||||
// Fast path: variables from the current context never require capture.
|
||||
DeclContext *DC = S.CurContext;
|
||||
if (var->getDeclContext() == DC) return CR_NoCapture;
|
||||
|
||||
// Only variables with local storage require capture.
|
||||
// FIXME: What about 'const' variables in C++?
|
||||
if (!var->hasLocalStorage()) return CR_NoCapture;
|
||||
|
||||
// Otherwise, we need to capture.
|
||||
|
||||
unsigned functionScopesIndex = S.FunctionScopes.size() - 1;
|
||||
do {
|
||||
// Only blocks (and eventually C++0x closures) can capture; other
|
||||
// scopes don't work.
|
||||
if (!isa<BlockDecl>(DC))
|
||||
return diagnoseUncapturableValueReference(S, loc, var, DC);
|
||||
|
||||
BlockScopeInfo *blockScope =
|
||||
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
|
||||
assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC));
|
||||
|
||||
// Check whether we've already captured it in this block. If so,
|
||||
// we're done.
|
||||
if (unsigned indexPlus1 = blockScope->CaptureMap[var])
|
||||
return propagateCapture(S, functionScopesIndex,
|
||||
blockScope->Captures[indexPlus1 - 1]);
|
||||
|
||||
functionScopesIndex--;
|
||||
DC = cast<BlockDecl>(DC)->getDeclContext();
|
||||
} while (var->getDeclContext() != DC);
|
||||
|
||||
// Okay, we descended all the way to the block that defines the variable.
|
||||
// Actually try to capture it.
|
||||
QualType type = var->getType();
|
||||
|
||||
// Prohibit variably-modified types.
|
||||
if (type->isVariablyModifiedType()) {
|
||||
S.Diag(loc, diag::err_ref_vm_type);
|
||||
S.Diag(var->getLocation(), diag::note_declared_at);
|
||||
return CR_Error;
|
||||
}
|
||||
|
||||
// Prohibit arrays, even in __block variables, but not references to
|
||||
// them.
|
||||
if (type->isArrayType()) {
|
||||
S.Diag(loc, diag::err_ref_array_type);
|
||||
S.Diag(var->getLocation(), diag::note_declared_at);
|
||||
return CR_Error;
|
||||
}
|
||||
|
||||
// The BlocksAttr indicates the variable is bound by-reference.
|
||||
bool byRef = var->hasAttr<BlocksAttr>();
|
||||
|
||||
// Build a copy expression.
|
||||
Expr *copyExpr = 0;
|
||||
const RecordType *rtype;
|
||||
if (!byRef && S.getLangOptions().CPlusPlus && !type->isDependentType() &&
|
||||
(rtype = type->getAs<RecordType>())) {
|
||||
|
||||
// The capture logic needs the destructor, so make sure we mark it.
|
||||
// Usually this is unnecessary because most local variables have
|
||||
// their destructors marked at declaration time, but parameters are
|
||||
// an exception because it's technically only the call site that
|
||||
// actually requires the destructor.
|
||||
if (isa<ParmVarDecl>(var))
|
||||
S.FinalizeVarWithDestructor(var, rtype);
|
||||
|
||||
// According to the blocks spec, the capture of a variable from
|
||||
// the stack requires a const copy constructor. This is not true
|
||||
// of the copy/move done to move a __block variable to the heap.
|
||||
type.addConst();
|
||||
|
||||
Expr *declRef = new (S.Context) DeclRefExpr(var, type, VK_LValue, loc);
|
||||
ExprResult result =
|
||||
S.PerformCopyInitialization(
|
||||
InitializedEntity::InitializeBlock(var->getLocation(),
|
||||
type, false),
|
||||
loc, S.Owned(declRef));
|
||||
|
||||
// Build a full-expression copy expression if initialization
|
||||
// succeeded and used a non-trivial constructor. Recover from
|
||||
// errors by pretending that the copy isn't necessary.
|
||||
if (!result.isInvalid() &&
|
||||
!cast<CXXConstructExpr>(result.get())->getConstructor()->isTrivial()) {
|
||||
result = S.MaybeCreateExprWithCleanups(result);
|
||||
copyExpr = result.take();
|
||||
}
|
||||
}
|
||||
|
||||
// We're currently at the declarer; go back to the closure.
|
||||
functionScopesIndex++;
|
||||
BlockScopeInfo *blockScope =
|
||||
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
|
||||
|
||||
// Build a valid capture in this scope.
|
||||
blockScope->AddCapture(var, byRef, /*nested*/ false, loc, copyExpr);
|
||||
|
||||
// Propagate that to inner captures if necessary.
|
||||
return propagateCapture(S, functionScopesIndex,
|
||||
blockScope->Captures.back());
|
||||
}
|
||||
|
||||
static ExprResult BuildBlockDeclRefExpr(Sema &S, ValueDecl *VD,
|
||||
const DeclarationNameInfo &NameInfo,
|
||||
bool ByRef) {
|
||||
assert(isa<VarDecl>(VD) && "capturing non-variable");
|
||||
|
||||
VarDecl *var = cast<VarDecl>(VD);
|
||||
assert(var->hasLocalStorage() && "capturing non-local");
|
||||
assert(ByRef == var->hasAttr<BlocksAttr>() && "byref set wrong");
|
||||
|
||||
QualType exprType = var->getType().getNonReferenceType();
|
||||
|
||||
BlockDeclRefExpr *BDRE;
|
||||
if (!ByRef) {
|
||||
// The variable will be bound by copy; make it const within the
|
||||
// closure, but record that this was done in the expression.
|
||||
bool constAdded = !exprType.isConstQualified();
|
||||
exprType.addConst();
|
||||
|
||||
BDRE = new (S.Context) BlockDeclRefExpr(var, exprType, VK_LValue,
|
||||
NameInfo.getLoc(), false,
|
||||
constAdded);
|
||||
} else {
|
||||
BDRE = new (S.Context) BlockDeclRefExpr(var, exprType, VK_LValue,
|
||||
NameInfo.getLoc(), true);
|
||||
}
|
||||
|
||||
S.MarkBlockDeclRefReferenced(BDRE);
|
||||
|
||||
return S.Owned(BDRE);
|
||||
}
|
||||
|
||||
ExprResult
|
||||
Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK,
|
||||
SourceLocation Loc,
|
||||
|
@ -2323,6 +2098,68 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
|||
return Owned(ULE);
|
||||
}
|
||||
|
||||
static bool shouldBuildBlockDeclRef(ValueDecl *D, Sema &S) {
|
||||
// Check for a variable with local storage not from the current scope;
|
||||
// we need to create BlockDeclRefExprs for these.
|
||||
// FIXME: BlockDeclRefExpr shouldn't exist!
|
||||
VarDecl *var = dyn_cast<VarDecl>(D);
|
||||
if (!var)
|
||||
return false;
|
||||
if (var->getDeclContext() == S.CurContext)
|
||||
return false;
|
||||
if (!var->hasLocalStorage())
|
||||
return false;
|
||||
return S.getCurBlock() != 0;
|
||||
}
|
||||
|
||||
static bool shouldAddConstQualToVarRef(ValueDecl *D, Sema &S) {
|
||||
VarDecl *var = dyn_cast<VarDecl>(D);
|
||||
if (!var)
|
||||
return false;
|
||||
if (var->getDeclContext() == S.CurContext)
|
||||
return false;
|
||||
if (!var->hasLocalStorage())
|
||||
return false;
|
||||
|
||||
LambdaScopeInfo *LSI = S.getCurLambda();
|
||||
if (!LSI)
|
||||
return false;
|
||||
|
||||
// We don't actually allow capturing a __block variable in a lambda, but
|
||||
// this way gives better diagnostics.
|
||||
if (var->hasAttr<BlocksAttr>())
|
||||
return false;
|
||||
|
||||
// FIXME: Does the addition of const really only apply in
|
||||
// potentially-evaluated contexts? The text in the lambda spec
|
||||
// about decltype hints that it might apply in unevaluated contexts
|
||||
// as well... and there's precent in our blocks implementation.
|
||||
return !LSI->Mutable &&
|
||||
S.ExprEvalContexts.back().Context != Sema::Unevaluated;
|
||||
}
|
||||
|
||||
static ExprResult BuildBlockDeclRefExpr(Sema &S, ValueDecl *VD,
|
||||
const DeclarationNameInfo &NameInfo) {
|
||||
VarDecl *var = cast<VarDecl>(VD);
|
||||
QualType exprType = var->getType().getNonReferenceType();
|
||||
|
||||
bool HasBlockAttr = var->hasAttr<BlocksAttr>();
|
||||
bool ConstAdded = false;
|
||||
if (!HasBlockAttr) {
|
||||
ConstAdded = !exprType.isConstQualified();
|
||||
exprType.addConst();
|
||||
}
|
||||
|
||||
BlockDeclRefExpr *BDRE =
|
||||
new (S.Context) BlockDeclRefExpr(var, exprType, VK_LValue,
|
||||
NameInfo.getLoc(), HasBlockAttr,
|
||||
ConstAdded);
|
||||
|
||||
S.MarkBlockDeclRefReferenced(BDRE);
|
||||
|
||||
return S.Owned(BDRE);
|
||||
}
|
||||
|
||||
/// \brief Complete semantic analysis for a reference to the given declaration.
|
||||
ExprResult
|
||||
Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
||||
|
@ -2373,30 +2210,7 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
|||
return BuildAnonymousStructUnionMemberReference(SS, NameInfo.getLoc(),
|
||||
indirectField);
|
||||
|
||||
// If the identifier reference is inside a block, and it refers to a value
|
||||
// that is outside the block, create a BlockDeclRefExpr instead of a
|
||||
// DeclRefExpr. This ensures the value is treated as a copy-in snapshot when
|
||||
// the block is formed.
|
||||
//
|
||||
// We do not do this for things like enum constants, global variables, etc,
|
||||
// as they do not get snapshotted.
|
||||
//
|
||||
switch (shouldCaptureValueReference(*this, NameInfo.getLoc(), VD)) {
|
||||
case CR_Error:
|
||||
return ExprError();
|
||||
|
||||
case CR_Capture:
|
||||
assert(!SS.isSet() && "referenced local variable with scope specifier?");
|
||||
return BuildBlockDeclRefExpr(*this, VD, NameInfo, /*byref*/ false);
|
||||
|
||||
case CR_CaptureByRef:
|
||||
assert(!SS.isSet() && "referenced local variable with scope specifier?");
|
||||
return BuildBlockDeclRefExpr(*this, VD, NameInfo, /*byref*/ true);
|
||||
|
||||
case CR_NoCapture: {
|
||||
// If this reference is not in a block or if the referenced
|
||||
// variable is within the block, create a normal DeclRefExpr.
|
||||
|
||||
{
|
||||
QualType type = VD->getType();
|
||||
ExprValueKind valueKind = VK_RValue;
|
||||
|
||||
|
@ -2467,6 +2281,13 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
|||
// These are always l-values.
|
||||
valueKind = VK_LValue;
|
||||
type = type.getNonReferenceType();
|
||||
|
||||
if (shouldBuildBlockDeclRef(VD, *this))
|
||||
return BuildBlockDeclRefExpr(*this, VD, NameInfo);
|
||||
|
||||
if (shouldAddConstQualToVarRef(VD, *this))
|
||||
type.addConst();
|
||||
|
||||
break;
|
||||
|
||||
case Decl::Function: {
|
||||
|
@ -2529,10 +2350,6 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
|
|||
|
||||
return BuildDeclRefExpr(VD, type, valueKind, NameInfo, &SS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
llvm_unreachable("unknown capture result");
|
||||
}
|
||||
|
||||
ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) {
|
||||
|
@ -9613,6 +9430,129 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
|
|||
Func->setUsed(true);
|
||||
}
|
||||
|
||||
static void
|
||||
diagnoseUncapturableValueReference(Sema &S, SourceLocation loc,
|
||||
VarDecl *var, DeclContext *DC) {
|
||||
// If the parameter still belongs to the translation unit, then
|
||||
// we're actually just using one parameter in the declaration of
|
||||
// the next.
|
||||
if (isa<ParmVarDecl>(var) &&
|
||||
isa<TranslationUnitDecl>(var->getDeclContext()))
|
||||
return;
|
||||
|
||||
// Don't diagnose about capture if we're not actually in code right
|
||||
// now; in general, there are more appropriate places that will
|
||||
// diagnose this.
|
||||
// FIXME: We incorrectly skip diagnosing C++11 member initializers.
|
||||
if (!S.CurContext->isFunctionOrMethod())
|
||||
return;
|
||||
|
||||
DeclarationName functionName;
|
||||
if (FunctionDecl *fn = dyn_cast<FunctionDecl>(var->getDeclContext()))
|
||||
functionName = fn->getDeclName();
|
||||
// FIXME: variable from enclosing block that we couldn't capture from!
|
||||
|
||||
S.Diag(loc, diag::err_reference_to_local_var_in_enclosing_function)
|
||||
<< var->getIdentifier() << functionName;
|
||||
S.Diag(var->getLocation(), diag::note_local_variable_declared_here)
|
||||
<< var->getIdentifier();
|
||||
}
|
||||
|
||||
static void tryCaptureVar(Sema &S, VarDecl *var,
|
||||
SourceLocation loc) {
|
||||
// Check if the variable needs to be captured.
|
||||
DeclContext *DC = S.CurContext;
|
||||
if (var->getDeclContext() == DC) return;
|
||||
if (!var->hasLocalStorage()) return;
|
||||
|
||||
// Actually try to capture it.
|
||||
QualType type = var->getType();
|
||||
bool Nested = false;
|
||||
|
||||
unsigned functionScopesIndex = S.FunctionScopes.size() - 1;
|
||||
do {
|
||||
// Only blocks (and eventually C++0x closures) can capture; other
|
||||
// scopes don't work.
|
||||
// FIXME: Make this function support lambdas!
|
||||
if (!isa<BlockDecl>(DC))
|
||||
return diagnoseUncapturableValueReference(S, loc, var, DC);
|
||||
|
||||
BlockScopeInfo *blockScope =
|
||||
cast<BlockScopeInfo>(S.FunctionScopes[functionScopesIndex]);
|
||||
assert(blockScope->TheDecl == static_cast<BlockDecl*>(DC));
|
||||
|
||||
// Check whether we've already captured it in this block.
|
||||
if (blockScope->CaptureMap.count(var)) {
|
||||
Nested = true;
|
||||
break;
|
||||
}
|
||||
|
||||
functionScopesIndex--;
|
||||
DC = cast<BlockDecl>(DC)->getDeclContext();
|
||||
} while (var->getDeclContext() != DC);
|
||||
|
||||
bool byRef = var->hasAttr<BlocksAttr>();
|
||||
|
||||
for (unsigned i = functionScopesIndex + 1,
|
||||
e = S.FunctionScopes.size(); i != e; ++i) {
|
||||
// Prohibit variably-modified types.
|
||||
if (type->isVariablyModifiedType()) {
|
||||
S.Diag(loc, diag::err_ref_vm_type);
|
||||
S.Diag(var->getLocation(), diag::note_declared_at);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prohibit arrays, even in __block variables, but not references to
|
||||
// them.
|
||||
if (type->isArrayType()) {
|
||||
S.Diag(loc, diag::err_ref_array_type);
|
||||
S.Diag(var->getLocation(), diag::note_declared_at);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a copy expression.
|
||||
Expr *copyExpr = 0;
|
||||
const RecordType *rtype;
|
||||
if (!byRef && S.getLangOptions().CPlusPlus && !type->isDependentType() &&
|
||||
(rtype = type->getAs<RecordType>())) {
|
||||
|
||||
// The capture logic needs the destructor, so make sure we mark it.
|
||||
// Usually this is unnecessary because most local variables have
|
||||
// their destructors marked at declaration time, but parameters are
|
||||
// an exception because it's technically only the call site that
|
||||
// actually requires the destructor.
|
||||
if (isa<ParmVarDecl>(var))
|
||||
S.FinalizeVarWithDestructor(var, rtype);
|
||||
|
||||
// According to the blocks spec, the capture of a variable from
|
||||
// the stack requires a const copy constructor. This is not true
|
||||
// of the copy/move done to move a __block variable to the heap.
|
||||
type.addConst();
|
||||
|
||||
Expr *declRef = new (S.Context) DeclRefExpr(var, type, VK_LValue, loc);
|
||||
ExprResult result =
|
||||
S.PerformCopyInitialization(
|
||||
InitializedEntity::InitializeBlock(var->getLocation(),
|
||||
type, false),
|
||||
loc, S.Owned(declRef));
|
||||
|
||||
// Build a full-expression copy expression if initialization
|
||||
// succeeded and used a non-trivial constructor. Recover from
|
||||
// errors by pretending that the copy isn't necessary.
|
||||
if (!result.isInvalid() &&
|
||||
!cast<CXXConstructExpr>(result.get())->getConstructor()->isTrivial()) {
|
||||
result = S.MaybeCreateExprWithCleanups(result);
|
||||
copyExpr = result.take();
|
||||
}
|
||||
}
|
||||
|
||||
BlockScopeInfo *blockScope = cast<BlockScopeInfo>(S.FunctionScopes[i]);
|
||||
blockScope->AddCapture(var, byRef, Nested, loc, copyExpr);
|
||||
|
||||
Nested = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
|
||||
SourceLocation Loc) {
|
||||
// Keep track of used but undefined variables.
|
||||
|
@ -9622,6 +9562,8 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var,
|
|||
if (old.isInvalid()) old = Loc;
|
||||
}
|
||||
|
||||
tryCaptureVar(SemaRef, Var, Loc);
|
||||
|
||||
Var->setUsed(true);
|
||||
}
|
||||
|
||||
|
@ -9665,9 +9607,6 @@ static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
|
|||
VarDecl *Var, Expr *E) {
|
||||
Var->setReferenced();
|
||||
|
||||
if (Var->isUsed(false))
|
||||
return;
|
||||
|
||||
if (!IsPotentiallyEvaluatedContext(SemaRef))
|
||||
return;
|
||||
|
||||
|
|
Loading…
Reference in New Issue