Objective-C fast enumeration loop variables are not retained in ARC, but
they should still be officially __strong for the purposes of errors,
block capture, etc. Make a new bit on variables, isARCPseudoStrong(),
and set this for 'self' and these enumeration-loop variables. Change
the code that was looking for the old patterns to look for this bit,
and change IR generation to find this bit and treat the resulting
variable as __unsafe_unretained for the purposes of init/destroy in
the two places it can come up.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133243 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp
index ab3bc1d..0c90962 100644
--- a/lib/ARCMigrate/Transforms.cpp
+++ b/lib/ARCMigrate/Transforms.cpp
@@ -421,10 +421,7 @@
if (IsLV != Expr::MLV_ConstQualified)
return true;
VarDecl *var = cast<VarDecl>(declRef->getDecl());
- if (var->getType().getLocalQualifiers().getObjCLifetime()
- == Qualifiers::OCL_ExplicitNone &&
- (var->getTypeSourceInfo() &&
- !var->getTypeSourceInfo()->getType().isConstQualified())) {
+ if (var->isARCPseudoStrong()) {
Transaction Trans(Pass.TA);
if (Pass.TA.clearDiagnostic(diag::err_typecheck_arr_assign_enumeration,
Exp->getOperatorLoc())) {
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index 99eb0d3..67f6531 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -474,19 +474,22 @@
} else // we have a factory method.
selfTy = Context.getObjCClassType();
+ bool selfIsPseudoStrong = false;
bool selfIsConsumed = false;
if (isInstanceMethod() && Context.getLangOptions().ObjCAutoRefCount) {
selfIsConsumed = hasAttr<NSConsumesSelfAttr>();
- // 'self' is always __strong, although as a special case we don't
- // actually retain it except in init methods.
+ // 'self' is always __strong. It's actually pseudo-strong except
+ // in init methods, though.
Qualifiers qs;
qs.setObjCLifetime(Qualifiers::OCL_Strong);
selfTy = Context.getQualifiedType(selfTy, qs);
// In addition, 'self' is const unless this is an init method.
- if (getMethodFamily() != OMF_init)
+ if (getMethodFamily() != OMF_init) {
selfTy = selfTy.withConst();
+ selfIsPseudoStrong = true;
+ }
}
ImplicitParamDecl *self
@@ -497,6 +500,9 @@
if (selfIsConsumed)
self->addAttr(new (Context) NSConsumedAttr(SourceLocation(), Context));
+ if (selfIsPseudoStrong)
+ self->setARCPseudoStrong(true);
+
setCmdDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(),
&Context.Idents.get("_cmd"),
Context.getObjCSelType()));
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index daa37ee..a79f031 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -562,6 +562,37 @@
EmitStoreOfScalar(value, lvalue);
}
+/// EmitScalarInit - Initialize the given lvalue with the given object.
+void CodeGenFunction::EmitScalarInit(llvm::Value *init, LValue lvalue) {
+ Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime();
+ if (!lifetime)
+ return EmitStoreThroughLValue(RValue::get(init), lvalue, lvalue.getType());
+
+ switch (lifetime) {
+ case Qualifiers::OCL_None:
+ llvm_unreachable("present but none");
+
+ case Qualifiers::OCL_ExplicitNone:
+ // nothing to do
+ break;
+
+ case Qualifiers::OCL_Strong:
+ init = EmitARCRetain(lvalue.getType(), init);
+ break;
+
+ case Qualifiers::OCL_Weak:
+ // Initialize and then skip the primitive store.
+ EmitARCInitWeak(lvalue.getAddress(), init);
+ return;
+
+ case Qualifiers::OCL_Autoreleasing:
+ init = EmitARCRetainAutorelease(lvalue.getType(), init);
+ break;
+ }
+
+ EmitStoreOfScalar(init, lvalue);
+}
+
/// canEmitInitWithFewStoresAfterMemset - Decide whether we can emit the
/// non-zero parts of the specified initializer with equal or fewer than
/// NumStores scalar stores.
@@ -995,8 +1026,10 @@
if (Qualifiers::ObjCLifetime lifetime
= D.getType().getQualifiers().getObjCLifetime()) {
- llvm::Value *loc = emission.getObjectAddress(*this);
- EmitAutoVarWithLifetime(*this, D, loc, lifetime);
+ if (!D.isARCPseudoStrong()) {
+ llvm::Value *loc = emission.getObjectAddress(*this);
+ EmitAutoVarWithLifetime(*this, D, loc, lifetime);
+ }
}
// Handle the cleanup attribute.
@@ -1081,10 +1114,11 @@
// 'self' is always formally __strong, but if this is not an
// init method then we don't want to retain it.
- if (lt == Qualifiers::OCL_Strong && qs.hasConst() &&
- isa<ImplicitParamDecl>(D)) {
+ if (D.isARCPseudoStrong()) {
const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CurCodeDecl);
assert(&D == method->getSelfDecl());
+ assert(lt == Qualifiers::OCL_Strong);
+ assert(qs.hasConst());
assert(method->getMethodFamily() != OMF_init);
(void) method;
lt = Qualifiers::OCL_ExplicitNone;
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index cdc2fff..ce9c262 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -1110,6 +1110,9 @@
elementLValue = EmitLValue(&tempDRE);
elementType = D->getType();
elementIsVariable = true;
+
+ if (D->isARCPseudoStrong())
+ elementLValue.getQuals().setObjCLifetime(Qualifiers::OCL_ExplicitNone);
} else {
elementLValue = LValue(); // suppress warning
elementType = cast<Expr>(S.getElement())->getType();
@@ -1136,10 +1139,12 @@
// Make sure we have an l-value. Yes, this gets evaluated every
// time through the loop.
- if (!elementIsVariable)
+ if (!elementIsVariable) {
elementLValue = EmitLValue(cast<Expr>(S.getElement()));
-
- EmitStoreThroughLValue(RValue::get(CurrentItem), elementLValue, elementType);
+ EmitStoreThroughLValue(RValue::get(CurrentItem), elementLValue, elementType);
+ } else {
+ EmitScalarInit(CurrentItem, elementLValue);
+ }
// If we do have an element variable, this assignment is the end of
// its initialization.
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index dec7736..f34a70c 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -1597,6 +1597,7 @@
void EmitScalarInit(const Expr *init, const ValueDecl *D,
LValue lvalue, bool capturedByInit);
+ void EmitScalarInit(llvm::Value *init, LValue lvalue);
typedef void SpecialInitFn(CodeGenFunction &Init, const VarDecl &D,
llvm::Value *Address);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 2d8ed17..e3fde7c 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -8161,25 +8161,28 @@
case Expr::MLV_ConstQualified:
Diag = diag::err_typecheck_assign_const;
- // In ARC, use some specialized diagnostics for the times when we
- // infer const.
+ // In ARC, use some specialized diagnostics for occasions where we
+ // infer 'const'. These are always pseudo-strong variables.
if (S.getLangOptions().ObjCAutoRefCount) {
DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts());
if (declRef && isa<VarDecl>(declRef->getDecl())) {
VarDecl *var = cast<VarDecl>(declRef->getDecl());
- // If the variable wasn't written with 'const', there are some
- // cases where we infer const anyway:
- // - self
- // - fast enumeration variables
- if (!var->getTypeSourceInfo() ||
- !var->getTypeSourceInfo()->getType().isConstQualified()) {
+ // Use the normal diagnostic if it's pseudo-__strong but the
+ // user actually wrote 'const'.
+ if (var->isARCPseudoStrong() &&
+ (!var->getTypeSourceInfo() ||
+ !var->getTypeSourceInfo()->getType().isConstQualified())) {
+ // There are two pseudo-strong cases:
+ // - self
ObjCMethodDecl *method = S.getCurMethodDecl();
if (method && var == method->getSelfDecl())
Diag = diag::err_typecheck_arr_assign_self;
- else if (var->getType().getObjCLifetime()
- == Qualifiers::OCL_ExplicitNone)
+
+ // - fast enumeration variables
+ else
Diag = diag::err_typecheck_arr_assign_enumeration;
+
SourceRange Assign;
if (Loc != OrigLoc)
Assign = SourceRange(OrigLoc, OrigLoc);
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index a5ad0a5..9af9c8d 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -71,22 +71,23 @@
// suppress any potential 'unused variable' warning.
var->setUsed();
- // In ARC, we don't want to lifetime for the iteration
- // variable of a fast enumeration loop. Rather than actually
- // trying to catch that during declaration processing, we
- // remove the consequences here.
- if (getLangOptions().ObjCAutoRefCount) {
- SplitQualType split = var->getType().split();
+ // foreach variables are never actually initialized in the way that
+ // the parser came up with.
+ var->setInit(0);
- // Inferred lifetime will show up as a local qualifier because
- // explicit lifetime would have shown up as an AttributedType
- // instead.
- if (split.second.hasObjCLifetime()) {
- // Change the qualification to 'const __unsafe_unretained'.
- split.second.setObjCLifetime(Qualifiers::OCL_ExplicitNone);
- split.second.addConst();
- var->setType(Context.getQualifiedType(split.first, split.second));
- var->setInit(0);
+ // In ARC, we don't need to retain the iteration variable of a fast
+ // enumeration loop. Rather than actually trying to catch that
+ // during declaration processing, we remove the consequences here.
+ if (getLangOptions().ObjCAutoRefCount) {
+ QualType type = var->getType();
+
+ // Only do this if we inferred the lifetime. Inferred lifetime
+ // will show up as a local qualifier because explicit lifetime
+ // should have shown up as an AttributedType instead.
+ if (type.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong) {
+ // Add 'const' and mark the variable as pseudo-strong.
+ var->setType(type.withConst());
+ var->setARCPseudoStrong(true);
}
}
}
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index fab2069..24ab544 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -710,6 +710,7 @@
VD->VarDeclBits.ExceptionVar = Record[Idx++];
VD->VarDeclBits.NRVOVariable = Record[Idx++];
VD->VarDeclBits.CXXForRangeDecl = Record[Idx++];
+ VD->VarDeclBits.ARCPseudoStrong = Record[Idx++];
if (Record[Idx++])
VD->setInit(Reader.ReadExpr(F));
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index 7c24088..651e207 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -645,6 +645,7 @@
Record.push_back(D->isExceptionVariable());
Record.push_back(D->isNRVOVariable());
Record.push_back(D->isCXXForRangeDecl());
+ Record.push_back(D->isARCPseudoStrong());
Record.push_back(D->getInit() ? 1 : 0);
if (D->getInit())
Writer.AddStmt(D->getInit());
@@ -670,7 +671,7 @@
D->RedeclLink.getNext() == D &&
!D->hasCXXDirectInitializer() &&
D->getInit() == 0 &&
- !ParmVarDecl::classofKind(D->getKind()) &&
+ !isa<ParmVarDecl>(D) &&
!SpecInfo)
AbbrevToUse = Writer.getDeclVarAbbrev();
@@ -695,6 +696,8 @@
Writer.AddStmt(D->getUninstantiatedDefaultArg());
Code = serialization::DECL_PARM_VAR;
+ assert(!D->isARCPseudoStrong()); // can be true of ImplicitParamDecl
+
// If the assumptions about the DECL_PARM_VAR abbrev are true, use it. Here
// we dynamically check for the properties that we optimize for, but don't
// know are true of all PARM_VAR_DECLs.
@@ -1426,6 +1429,7 @@
Abv->Add(BitCodeAbbrevOp(0)); // isExceptionVariable
Abv->Add(BitCodeAbbrevOp(0)); // isNRVOVariable
Abv->Add(BitCodeAbbrevOp(0)); // isCXXForRangeDecl
+ Abv->Add(BitCodeAbbrevOp(0)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(0)); // HasInit
Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo
// ParmVarDecl
@@ -1498,6 +1502,7 @@
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl
+ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasInit
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasMemberSpecInfo
// Type Source Info