Automatic Reference Counting.
Language-design credit goes to a lot of people, but I particularly want
to single out Blaine Garst and Patrick Beard for their contributions.
Compiler implementation credit goes to Argyrios, Doug, Fariborz, and myself,
in no particular order.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133103 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp
index 619a5b9..fda4e0c 100644
--- a/lib/Sema/AttributeList.cpp
+++ b/lib/Sema/AttributeList.cpp
@@ -177,6 +177,11 @@
.Case("cf_consumed", AT_cf_consumed)
.Case("cf_returns_not_retained", AT_cf_returns_not_retained)
.Case("cf_returns_retained", AT_cf_returns_retained)
+ .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased)
+ .Case("ns_consumes_self", AT_ns_consumes_self)
+ .Case("ns_consumed", AT_ns_consumed)
+ .Case("objc_lifetime", AT_objc_lifetime)
+ .Case("objc_precise_lifetime", AT_objc_precise_lifetime)
.Case("ownership_returns", AT_ownership_returns)
.Case("ownership_holds", AT_ownership_holds)
.Case("ownership_takes", AT_ownership_takes)
diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp
index af548fe..c6744ed 100644
--- a/lib/Sema/DelayedDiagnostic.cpp
+++ b/lib/Sema/DelayedDiagnostic.cpp
@@ -47,5 +47,8 @@
case Deprecation:
delete [] DeprecationData.Message;
break;
+
+ case ForbiddenType:
+ break;
}
}
diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp
index 679f4fe..007d755 100644
--- a/lib/Sema/JumpDiagnostics.cpp
+++ b/lib/Sema/JumpDiagnostics.cpp
@@ -111,90 +111,110 @@
return A;
}
+typedef std::pair<unsigned,unsigned> ScopePair;
+
/// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a
/// diagnostic that should be emitted if control goes over it. If not, return 0.
-static std::pair<unsigned,unsigned>
- GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) {
+static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
unsigned InDiag = 0, OutDiag = 0;
if (VD->getType()->isVariablyModifiedType())
InDiag = diag::note_protected_by_vla;
- if (VD->hasAttr<BlocksAttr>()) {
- InDiag = diag::note_protected_by___block;
- OutDiag = diag::note_exits___block;
- } else if (VD->hasAttr<CleanupAttr>()) {
- InDiag = diag::note_protected_by_cleanup;
- OutDiag = diag::note_exits_cleanup;
- } else if (isCPlusPlus) {
- if (!VD->hasLocalStorage())
- return std::make_pair(InDiag, OutDiag);
-
- ASTContext &Context = D->getASTContext();
- QualType T = Context.getBaseElementType(VD->getType());
- if (!T->isDependentType()) {
- // C++0x [stmt.dcl]p3:
- // A program that jumps from a point where a variable with automatic
- // storage duration is not in scope to a point where it is in scope
- // is ill-formed unless the variable has scalar type, class type with
- // a trivial default constructor and a trivial destructor, a
- // cv-qualified version of one of these types, or an array of one of
- // the preceding types and is declared without an initializer (8.5).
- // Check whether this is a C++ class.
- CXXRecordDecl *Record = T->getAsCXXRecordDecl();
-
- if (const Expr *Init = VD->getInit()) {
- bool CallsTrivialConstructor = false;
- if (Record) {
- // FIXME: With generalized initializer lists, this may
- // classify "X x{};" as having no initializer.
- if (const CXXConstructExpr *Construct
- = dyn_cast<CXXConstructExpr>(Init))
- if (const CXXConstructorDecl *Constructor
- = Construct->getConstructor())
- if ((Context.getLangOptions().CPlusPlus0x
- ? Record->hasTrivialDefaultConstructor()
- : Record->isPOD()) &&
- Constructor->isDefaultConstructor())
- CallsTrivialConstructor = true;
-
- if (CallsTrivialConstructor && !Record->hasTrivialDestructor())
- InDiag = diag::note_protected_by_variable_nontriv_destructor;
+ if (VD->hasAttr<BlocksAttr>())
+ return ScopePair(diag::note_protected_by___block,
+ diag::note_exits___block);
+
+ if (VD->hasAttr<CleanupAttr>())
+ return ScopePair(diag::note_protected_by_cleanup,
+ diag::note_exits_cleanup);
+
+ if (Context.getLangOptions().ObjCAutoRefCount && VD->hasLocalStorage()) {
+ switch (VD->getType().getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Autoreleasing:
+ break;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ return ScopePair(diag::note_protected_by_objc_lifetime,
+ diag::note_exits_objc_lifetime);
+ }
+ }
+
+ if (Context.getLangOptions().CPlusPlus && VD->hasLocalStorage()) {
+ // C++0x [stmt.dcl]p3:
+ // A program that jumps from a point where a variable with automatic
+ // storage duration is not in scope to a point where it is in scope
+ // is ill-formed unless the variable has scalar type, class type with
+ // a trivial default constructor and a trivial destructor, a
+ // cv-qualified version of one of these types, or an array of one of
+ // the preceding types and is declared without an initializer.
+
+ // C++03 [stmt.dcl.p3:
+ // A program that jumps from a point where a local variable
+ // with automatic storage duration is not in scope to a point
+ // where it is in scope is ill-formed unless the variable has
+ // POD type and is declared without an initializer.
+
+ if (const Expr *init = VD->getInit()) {
+ // We actually give variables of record type (or array thereof)
+ // an initializer even if that initializer only calls a trivial
+ // ctor. Detect that case.
+ // FIXME: With generalized initializer lists, this may
+ // classify "X x{};" as having no initializer.
+ unsigned inDiagToUse = diag::note_protected_by_variable_init;
+
+ const CXXRecordDecl *record = 0;
+
+ if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) {
+ const CXXConstructorDecl *ctor = cce->getConstructor();
+ record = ctor->getParent();
+
+ if (ctor->isTrivial() && ctor->isDefaultConstructor()) {
+ if (Context.getLangOptions().CPlusPlus0x) {
+ inDiagToUse = (record->hasTrivialDestructor() ? 0 :
+ diag::note_protected_by_variable_nontriv_destructor);
+ } else {
+ if (record->isPOD())
+ inDiagToUse = 0;
+ }
}
-
- if (!CallsTrivialConstructor)
- InDiag = diag::note_protected_by_variable_init;
+ } else if (VD->getType()->isArrayType()) {
+ record = VD->getType()->getBaseElementTypeUnsafe()
+ ->getAsCXXRecordDecl();
}
-
- // Note whether we have a class with a non-trivial destructor.
- if (Record && !Record->hasTrivialDestructor())
+
+ if (inDiagToUse)
+ InDiag = inDiagToUse;
+
+ // Also object to indirect jumps which leave scopes with dtors.
+ if (record && !record->hasTrivialDestructor())
OutDiag = diag::note_exits_dtor;
}
}
- return std::make_pair(InDiag, OutDiag);
+ return ScopePair(InDiag, OutDiag);
}
if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
- return std::make_pair((unsigned) diag::note_protected_by_vla_typedef, 0);
+ return ScopePair(diag::note_protected_by_vla_typedef, 0);
}
if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) {
if (TD->getUnderlyingType()->isVariablyModifiedType())
- return std::make_pair((unsigned) diag::note_protected_by_vla_type_alias, 0);
+ return ScopePair(diag::note_protected_by_vla_type_alias, 0);
}
- return std::make_pair(0U, 0U);
+ return ScopePair(0U, 0U);
}
/// \brief Build scope information for a declaration that is part of a DeclStmt.
void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
- bool isCPlusPlus = this->S.getLangOptions().CPlusPlus;
-
// If this decl causes a new scope, push and switch to it.
- std::pair<unsigned,unsigned> Diags
- = GetDiagForGotoScopeDecl(D, isCPlusPlus);
+ std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D);
if (Diags.first || Diags.second) {
Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second,
D->getLocation()));
@@ -369,6 +389,18 @@
continue;
}
+ // Disallow jumps into the protected statement of an @autoreleasepool.
+ if (ObjCAutoreleasePoolStmt *AS = dyn_cast<ObjCAutoreleasePoolStmt>(SubStmt)){
+ // Recursively walk the AST for the @autoreleasepool part, protected by a new
+ // scope.
+ Scopes.push_back(GotoScope(ParentScope,
+ diag::note_protected_by_objc_autoreleasepool,
+ diag::note_exits_objc_autoreleasepool,
+ AS->getAtLoc()));
+ BuildScopeInformation(AS->getSubStmt(), Scopes.size()-1);
+ continue;
+ }
+
// Recursively walk the AST.
BuildScopeInformation(SubStmt, ParentScope);
}
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 8297b31..7d22031 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -143,7 +143,7 @@
Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()),
ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0),
PackContext(0), MSStructPragmaOn(false), VisContext(0),
- LateTemplateParser(0), OpaqueParser(0),
+ ExprNeedsCleanups(0), LateTemplateParser(0), OpaqueParser(0),
IdResolver(pp.getLangOptions()), CXXTypeInfoDecl(0), MSVCGuidDecl(0),
GlobalNewDeleteDeclared(false),
CompleteTranslationUnit(CompleteTranslationUnit),
@@ -162,7 +162,7 @@
&Context);
ExprEvalContexts.push_back(
- ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0));
+ ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false));
FunctionScopes.push_back(new FunctionScopeInfo(Diags));
}
@@ -202,6 +202,32 @@
ExternalSema->ForgetSema();
}
+
+/// makeUnavailableInSystemHeader - There is an error in the current
+/// context. If we're still in a system header, and we can plausibly
+/// make the relevant declaration unavailable instead of erroring, do
+/// so and return true.
+bool Sema::makeUnavailableInSystemHeader(SourceLocation loc,
+ llvm::StringRef msg) {
+ // If we're not in a function, it's an error.
+ FunctionDecl *fn = dyn_cast<FunctionDecl>(CurContext);
+ if (!fn) return false;
+
+ // If we're in template instantiation, it's an error.
+ if (!ActiveTemplateInstantiations.empty())
+ return false;
+
+ // If that function's not in a system header, it's an error.
+ if (!Context.getSourceManager().isInSystemHeader(loc))
+ return false;
+
+ // If the function is already unavailable, it's not an error.
+ if (fn->hasAttr<UnavailableAttr>()) return true;
+
+ fn->addAttr(new (Context) UnavailableAttr(loc, Context, msg));
+ return true;
+}
+
ASTMutationListener *Sema::getASTMutationListener() const {
return getASTConsumer().GetASTMutationListener();
}
@@ -211,13 +237,17 @@
/// The result is of the given category.
ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
CastKind Kind, ExprValueKind VK,
- const CXXCastPath *BasePath) {
+ const CXXCastPath *BasePath,
+ CheckedConversionKind CCK) {
QualType ExprTy = Context.getCanonicalType(E->getType());
QualType TypeTy = Context.getCanonicalType(Ty);
if (ExprTy == TypeTy)
return Owned(E);
+ if (getLangOptions().ObjCAutoRefCount)
+ CheckObjCARCConversion(SourceRange(), Ty, E, CCK);
+
// If this is a derived-to-base cast to a through a virtual base, we
// need a vtable.
if (Kind == CK_DerivedToBase &&
@@ -729,8 +759,8 @@
/// \brief Determine whether any errors occurred within this function/method/
/// block.
-bool Sema::hasAnyErrorsInThisFunction() const {
- return getCurFunction()->ErrorTrap.hasErrorOccurred();
+bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const {
+ return getCurFunction()->ErrorTrap.hasUnrecoverableErrorOccurred();
}
BlockScopeInfo *Sema::getCurBlock() {
diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp
index e46ad5b..0b3083a 100644
--- a/lib/Sema/SemaCXXCast.cpp
+++ b/lib/Sema/SemaCXXCast.cpp
@@ -63,7 +63,8 @@
CastKind &Kind,
CXXCastPath &BasePath);
-static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType);
+static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType,
+ bool CheckCVR, bool CheckObjCLifetime);
// The Try functions attempt a specific way of casting. If they succeed, they
// return TC_Success. If their way of casting is not appropriate for the given
@@ -109,12 +110,14 @@
CXXCastPath &BasePath);
static TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr,
- QualType DestType, bool CStyle,
+ QualType DestType,
+ Sema::CheckedConversionKind CCK,
const SourceRange &OpRange,
unsigned &msg,
CastKind &Kind);
static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
- QualType DestType, bool CStyle,
+ QualType DestType,
+ Sema::CheckedConversionKind CCK,
const SourceRange &OpRange,
unsigned &msg,
CastKind &Kind,
@@ -248,8 +251,10 @@
InitializedEntity entity = InitializedEntity::InitializeTemporary(destType);
InitializationKind initKind
- = InitializationKind::CreateCast(/*type range?*/ range,
- (CT == CT_CStyle || CT == CT_Functional));
+ = (CT == CT_CStyle)? InitializationKind::CreateCStyleCast(range.getBegin(),
+ range)
+ : (CT == CT_Functional)? InitializationKind::CreateFunctionalCast(range)
+ : InitializationKind::CreateCast(/*type range?*/ range);
InitializationSequence sequence(S, entity, initKind, &src, 1);
assert(sequence.Failed() && "initialization succeeded on second try?");
@@ -373,8 +378,19 @@
/// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by
/// the cast checkers. Both arguments must denote pointer (possibly to member)
/// types.
+///
+/// \param CheckCVR Whether to check for const/volatile/restrict qualifiers.
+///
+/// \param CheckObjCLifetime Whether to check Objective-C lifetime qualifiers.
static bool
-CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) {
+CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType,
+ bool CheckCVR, bool CheckObjCLifetime) {
+ // If the only checking we care about is for Objective-C lifetime qualifiers,
+ // and we're not in ARC mode, there's nothing to check.
+ if (!CheckCVR && CheckObjCLifetime &&
+ !Self.Context.getLangOptions().ObjCAutoRefCount)
+ return false;
+
// Casting away constness is defined in C++ 5.2.11p8 with reference to
// C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since
// the rules are non-trivial. So first we construct Tcv *...cv* as described
@@ -394,13 +410,23 @@
// purpose of this check, because other qualifiers (address spaces,
// Objective-C GC, etc.) are part of the type's identity.
while (UnwrapDissimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) {
- Qualifiers SrcQuals;
+ // Determine the relevant qualifiers at this level.
+ Qualifiers SrcQuals, DestQuals;
Self.Context.getUnqualifiedArrayType(UnwrappedSrcType, SrcQuals);
- cv1.push_back(Qualifiers::fromCVRMask(SrcQuals.getCVRQualifiers()));
-
- Qualifiers DestQuals;
Self.Context.getUnqualifiedArrayType(UnwrappedDestType, DestQuals);
- cv2.push_back(Qualifiers::fromCVRMask(DestQuals.getCVRQualifiers()));
+
+ Qualifiers RetainedSrcQuals, RetainedDestQuals;
+ if (CheckCVR) {
+ RetainedSrcQuals.setCVRQualifiers(SrcQuals.getCVRQualifiers());
+ RetainedDestQuals.setCVRQualifiers(DestQuals.getCVRQualifiers());
+ }
+
+ if (CheckObjCLifetime &&
+ !DestQuals.compatiblyIncludesObjCLifetime(SrcQuals))
+ return true;
+
+ cv1.push_back(RetainedSrcQuals);
+ cv2.push_back(RetainedDestQuals);
}
if (cv1.empty())
return false;
@@ -420,8 +446,10 @@
}
// Test if they're compatible.
+ bool ObjCLifetimeConversion;
return SrcConstruct != DestConstruct &&
- !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false);
+ !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false,
+ ObjCLifetimeConversion);
}
/// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid.
@@ -595,9 +623,10 @@
}
unsigned msg = diag::err_bad_cxx_cast_generic;
- if (TryReinterpretCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange,
- msg, Kind)
- != TC_Success && msg != 0)
+ TryCastResult tcr =
+ TryReinterpretCast(Self, SrcExpr, DestType,
+ /*CStyle*/false, OpRange, msg, Kind);
+ if (tcr != TC_Success && msg != 0)
{
if (SrcExpr.isInvalid()) // if conversion failed, don't report another error
return;
@@ -611,7 +640,10 @@
} else {
diagnoseBadCast(Self, msg, CT_Reinterpret, OpRange, SrcExpr.get(), DestType);
}
- }
+ } else if (tcr == TC_Success && Self.getLangOptions().ObjCAutoRefCount) {
+ Self.CheckObjCARCConversion(OpRange, DestType,
+ SrcExpr.get(), Sema::CCK_OtherCast);
+ }
}
@@ -654,8 +686,10 @@
}
unsigned msg = diag::err_bad_cxx_cast_generic;
- if (TryStaticCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, msg,
- Kind, BasePath) != TC_Success && msg != 0) {
+ TryCastResult tcr
+ = TryStaticCast(Self, SrcExpr, DestType, Sema::CCK_OtherCast, OpRange, msg,
+ Kind, BasePath);
+ if (tcr != TC_Success && msg != 0) {
if (SrcExpr.isInvalid())
return;
if (SrcExpr.get()->getType() == Self.Context.OverloadTy) {
@@ -667,6 +701,12 @@
} else {
diagnoseBadCast(Self, msg, CT_Static, OpRange, SrcExpr.get(), DestType);
}
+ } else if (tcr == TC_Success) {
+ if (Kind == CK_BitCast)
+ Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
+ if (Self.getLangOptions().ObjCAutoRefCount)
+ Self.CheckObjCARCConversion(OpRange, DestType,
+ SrcExpr.get(), Sema::CCK_OtherCast);
}
else if (Kind == CK_BitCast)
Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
@@ -676,10 +716,15 @@
/// possible. If @p CStyle, ignore access restrictions on hierarchy casting
/// and casting away constness.
static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
- QualType DestType, bool CStyle,
+ QualType DestType,
+ Sema::CheckedConversionKind CCK,
const SourceRange &OpRange, unsigned &msg,
CastKind &Kind,
CXXCastPath &BasePath) {
+ // Determine whether we have the semantics of a C-style cast.
+ bool CStyle
+ = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast);
+
// The order the tests is not entirely arbitrary. There is one conversion
// that can be handled in two different ways. Given:
// struct A {};
@@ -715,7 +760,7 @@
// C++ 5.2.9p2: An expression e can be explicitly converted to a type T
// [...] if the declaration "T t(e);" is well-formed, [...].
- tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CStyle, OpRange, msg,
+ tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CCK, OpRange, msg,
Kind);
if (SrcExpr.isInvalid())
return TC_Failed;
@@ -792,10 +837,20 @@
QualType DestPointee = DestPointer->getPointeeType();
if (DestPointee->isIncompleteOrObjectType()) {
// This is definitely the intended conversion, but it might fail due
- // to a const violation.
- if (!CStyle && !DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) {
- msg = diag::err_bad_cxx_cast_qualifiers_away;
- return TC_Failed;
+ // to a qualifier violation. Note that we permit Objective-C lifetime
+ // and GC qualifier mismatches here.
+ if (!CStyle) {
+ Qualifiers DestPointeeQuals = DestPointee.getQualifiers();
+ Qualifiers SrcPointeeQuals = SrcPointee.getQualifiers();
+ DestPointeeQuals.removeObjCGCAttr();
+ DestPointeeQuals.removeObjCLifetime();
+ SrcPointeeQuals.removeObjCGCAttr();
+ SrcPointeeQuals.removeObjCLifetime();
+ if (DestPointeeQuals != SrcPointeeQuals &&
+ !DestPointeeQuals.compatiblyIncludes(SrcPointeeQuals)) {
+ msg = diag::err_bad_cxx_cast_qualifiers_away;
+ return TC_Failed;
+ }
}
Kind = CK_BitCast;
return TC_Success;
@@ -845,6 +900,7 @@
// FIXME: Should allow casting away constness if CStyle.
bool DerivedToBase;
bool ObjCConversion;
+ bool ObjCLifetimeConversion;
QualType FromType = SrcExpr->getType();
QualType ToType = R->getPointeeType();
if (CStyle) {
@@ -854,8 +910,9 @@
if (Self.CompareReferenceRelationship(SrcExpr->getLocStart(),
ToType, FromType,
- DerivedToBase, ObjCConversion) <
- Sema::Ref_Compatible_With_Added_Qualification) {
+ DerivedToBase, ObjCConversion,
+ ObjCLifetimeConversion)
+ < Sema::Ref_Compatible_With_Added_Qualification) {
msg = diag::err_bad_lvalue_to_rvalue_cast;
return TC_Failed;
}
@@ -1172,7 +1229,8 @@
/// @c static_cast if the declaration "T t(e);" is well-formed [...].
TryCastResult
TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType,
- bool CStyle, const SourceRange &OpRange, unsigned &msg,
+ Sema::CheckedConversionKind CCK,
+ const SourceRange &OpRange, unsigned &msg,
CastKind &Kind) {
if (DestType->isRecordType()) {
if (Self.RequireCompleteType(OpRange.getBegin(), DestType,
@@ -1184,7 +1242,11 @@
InitializedEntity Entity = InitializedEntity::InitializeTemporary(DestType);
InitializationKind InitKind
- = InitializationKind::CreateCast(/*FIXME:*/OpRange, CStyle);
+ = (CCK == Sema::CCK_CStyleCast)
+ ? InitializationKind::CreateCStyleCast(OpRange.getBegin(), OpRange)
+ : (CCK == Sema::CCK_FunctionalCast)
+ ? InitializationKind::CreateFunctionalCast(OpRange)
+ : InitializationKind::CreateCast(OpRange);
Expr *SrcExprRaw = SrcExpr.get();
InitializationSequence InitSeq(Self, Entity, InitKind, &SrcExprRaw, 1);
@@ -1193,7 +1255,8 @@
// There is no other way that works.
// On the other hand, if we're checking a C-style cast, we've still got
// the reinterpret_cast way.
-
+ bool CStyle
+ = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast);
if (InitSeq.Failed() && (CStyle || !DestType->isReferenceType()))
return TC_NotApplicable;
@@ -1428,7 +1491,8 @@
// constness.
// A reinterpret_cast followed by a const_cast can, though, so in C-style,
// we accept it.
- if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) {
+ if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
+ /*CheckObjCLifetime=*/CStyle)) {
msg = diag::err_bad_cxx_cast_qualifiers_away;
return TC_Failed;
}
@@ -1543,7 +1607,8 @@
// C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness.
// The C-style cast operator can.
- if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) {
+ if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle,
+ /*CheckObjCLifetime=*/CStyle)) {
msg = diag::err_bad_cxx_cast_qualifiers_away;
return TC_Failed;
}
@@ -1675,11 +1740,14 @@
if (tcr == TC_Success)
Kind = CK_NoOp;
+ Sema::CheckedConversionKind CCK
+ = FunctionalStyle? Sema::CCK_FunctionalCast
+ : Sema::CCK_CStyleCast;
if (tcr == TC_NotApplicable) {
// ... or if that is not possible, a static_cast, ignoring const, ...
ExprResult CastExprRes = Owned(CastExpr);
- tcr = TryStaticCast(*this, CastExprRes, CastTy, /*CStyle*/true, R, msg,
- Kind, BasePath);
+ tcr = TryStaticCast(*this, CastExprRes, CastTy, CCK, R, msg, Kind,
+ BasePath);
if (CastExprRes.isInvalid())
return ExprError();
CastExpr = CastExprRes.take();
@@ -1694,6 +1762,9 @@
}
}
+ if (getLangOptions().ObjCAutoRefCount && tcr == TC_Success)
+ CheckObjCARCConversion(R, CastTy, CastExpr, CCK);
+
if (tcr != TC_Success && msg != 0) {
if (CastExpr->getType() == Context.OverloadTy) {
DeclAccessPair Found;
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 389dd91..d45ebf9 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
@@ -382,14 +383,14 @@
// casts here.
// FIXME: We don't allow floating point scalars as input.
Expr *FirstArg = TheCall->getArg(0);
- if (!FirstArg->getType()->isPointerType()) {
+ const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>();
+ if (!pointerType) {
Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer)
<< FirstArg->getType() << FirstArg->getSourceRange();
return ExprError();
}
- QualType ValType =
- FirstArg->getType()->getAs<PointerType>()->getPointeeType();
+ QualType ValType = pointerType->getPointeeType();
if (!ValType->isIntegerType() && !ValType->isAnyPointerType() &&
!ValType->isBlockPointerType()) {
Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer_intptr)
@@ -397,6 +398,20 @@
return ExprError();
}
+ switch (ValType.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ // okay
+ break;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Autoreleasing:
+ Diag(DRE->getLocStart(), diag::err_arc_atomic_lifetime)
+ << ValType << FirstArg->getSourceRange();
+ return ExprError();
+ }
+
// The majority of builtins return a value, but a few have special return
// types, so allow them to override appropriately below.
QualType ResultType = ValType;
@@ -518,7 +533,8 @@
CastKind Kind = CK_Invalid;
ExprValueKind VK = VK_RValue;
CXXCastPath BasePath;
- Arg = CheckCastTypes(Arg.get()->getSourceRange(), ValType, Arg.take(), Kind, VK, BasePath);
+ Arg = CheckCastTypes(Arg.get()->getLocStart(), Arg.get()->getSourceRange(),
+ ValType, Arg.take(), Kind, VK, BasePath);
if (Arg.isInvalid())
return ExprError();
@@ -1849,6 +1865,7 @@
QualType DestTy = Dest->getType();
if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) {
QualType PointeeTy = DestPtrTy->getPointeeType();
+
if (PointeeTy->isVoidType())
continue;
@@ -1863,16 +1880,22 @@
break;
}
+ unsigned DiagID;
+
// Always complain about dynamic classes.
- if (isDynamicClassType(PointeeTy)) {
- DiagRuntimeBehavior(
- Dest->getExprLoc(), Dest,
- PDiag(diag::warn_dyn_class_memaccess)
- << ArgIdx << FnName << PointeeTy
- << Call->getCallee()->getSourceRange());
- } else {
+ if (isDynamicClassType(PointeeTy))
+ DiagID = diag::warn_dyn_class_memaccess;
+ else if (PointeeTy.hasNonTrivialObjCLifetime() &&
+ !FnName->isStr("memset"))
+ DiagID = diag::warn_arc_object_memaccess;
+ else
continue;
- }
+
+ DiagRuntimeBehavior(
+ Dest->getExprLoc(), Dest,
+ PDiag(DiagID)
+ << ArgIdx << FnName << PointeeTy
+ << Call->getCallee()->getSourceRange());
DiagRuntimeBehavior(
Dest->getExprLoc(), Dest,
@@ -1899,7 +1922,8 @@
// Perform checking for returned stack addresses, local blocks,
// label addresses or references to temporaries.
- if (lhsType->isPointerType() || lhsType->isBlockPointerType()) {
+ if (lhsType->isPointerType() ||
+ (!getLangOptions().ObjCAutoRefCount && lhsType->isBlockPointerType())) {
stackE = EvalAddr(RetValExp, refVars);
} else if (lhsType->isReferenceType()) {
stackE = EvalVal(RetValExp, refVars);
@@ -2070,7 +2094,8 @@
// pointer values, and pointer-to-pointer conversions.
case Stmt::ImplicitCastExprClass:
case Stmt::CStyleCastExprClass:
- case Stmt::CXXFunctionalCastExprClass: {
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::ObjCBridgedCastExprClass: {
Expr* SubExpr = cast<CastExpr>(E)->getSubExpr();
QualType T = SubExpr->getType();
@@ -3385,3 +3410,232 @@
}
}
}
+
+//===--- CHECK: Objective-C retain cycles ----------------------------------//
+
+namespace {
+ struct RetainCycleOwner {
+ RetainCycleOwner() : Variable(0), Indirect(false) {}
+ VarDecl *Variable;
+ SourceRange Range;
+ SourceLocation Loc;
+ bool Indirect;
+
+ void setLocsFrom(Expr *e) {
+ Loc = e->getExprLoc();
+ Range = e->getSourceRange();
+ }
+ };
+}
+
+/// Consider whether capturing the given variable can possibly lead to
+/// a retain cycle.
+static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) {
+ // In ARC, it's captured strongly iff the variable has __strong
+ // lifetime. In MRR, it's captured strongly if the variable is
+ // __block and has an appropriate type.
+ if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
+ return false;
+
+ owner.Variable = var;
+ owner.setLocsFrom(ref);
+ return true;
+}
+
+static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) {
+ while (true) {
+ e = e->IgnoreParens();
+ if (CastExpr *cast = dyn_cast<CastExpr>(e)) {
+ switch (cast->getCastKind()) {
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_LValueToRValue:
+ e = cast->getSubExpr();
+ continue;
+
+ case CK_GetObjCProperty: {
+ // Bail out if this isn't a strong explicit property.
+ const ObjCPropertyRefExpr *pre = cast->getSubExpr()->getObjCProperty();
+ if (pre->isImplicitProperty()) return false;
+ ObjCPropertyDecl *property = pre->getExplicitProperty();
+ if (!(property->getPropertyAttributes() &
+ (ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_copy |
+ ObjCPropertyDecl::OBJC_PR_strong)) &&
+ !(property->getPropertyIvarDecl() &&
+ property->getPropertyIvarDecl()->getType()
+ .getObjCLifetime() == Qualifiers::OCL_Strong))
+ return false;
+
+ owner.Indirect = true;
+ e = const_cast<Expr*>(pre->getBase());
+ continue;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) {
+ ObjCIvarDecl *ivar = ref->getDecl();
+ if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong)
+ return false;
+
+ // Try to find a retain cycle in the base.
+ if (!findRetainCycleOwner(ref->getBase(), owner))
+ return false;
+
+ if (ref->isFreeIvar()) owner.setLocsFrom(ref);
+ owner.Indirect = true;
+ return true;
+ }
+
+ if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) {
+ VarDecl *var = dyn_cast<VarDecl>(ref->getDecl());
+ if (!var) return false;
+ return considerVariable(var, ref, owner);
+ }
+
+ if (BlockDeclRefExpr *ref = dyn_cast<BlockDeclRefExpr>(e)) {
+ owner.Variable = ref->getDecl();
+ owner.setLocsFrom(ref);
+ return true;
+ }
+
+ if (MemberExpr *member = dyn_cast<MemberExpr>(e)) {
+ if (member->isArrow()) return false;
+
+ // Don't count this as an indirect ownership.
+ e = member->getBase();
+ continue;
+ }
+
+ // Array ivars?
+
+ return false;
+ }
+}
+
+namespace {
+ struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> {
+ FindCaptureVisitor(ASTContext &Context, VarDecl *variable)
+ : EvaluatedExprVisitor<FindCaptureVisitor>(Context),
+ Variable(variable), Capturer(0) {}
+
+ VarDecl *Variable;
+ Expr *Capturer;
+
+ void VisitDeclRefExpr(DeclRefExpr *ref) {
+ if (ref->getDecl() == Variable && !Capturer)
+ Capturer = ref;
+ }
+
+ void VisitBlockDeclRefExpr(BlockDeclRefExpr *ref) {
+ if (ref->getDecl() == Variable && !Capturer)
+ Capturer = ref;
+ }
+
+ void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) {
+ if (Capturer) return;
+ Visit(ref->getBase());
+ if (Capturer && ref->isFreeIvar())
+ Capturer = ref;
+ }
+
+ void VisitBlockExpr(BlockExpr *block) {
+ // Look inside nested blocks
+ if (block->getBlockDecl()->capturesVariable(Variable))
+ Visit(block->getBlockDecl()->getBody());
+ }
+ };
+}
+
+/// Check whether the given argument is a block which captures a
+/// variable.
+static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) {
+ assert(owner.Variable && owner.Loc.isValid());
+
+ e = e->IgnoreParenCasts();
+ BlockExpr *block = dyn_cast<BlockExpr>(e);
+ if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable))
+ return 0;
+
+ FindCaptureVisitor visitor(S.Context, owner.Variable);
+ visitor.Visit(block->getBlockDecl()->getBody());
+ return visitor.Capturer;
+}
+
+static void diagnoseRetainCycle(Sema &S, Expr *capturer,
+ RetainCycleOwner &owner) {
+ assert(capturer);
+ assert(owner.Variable && owner.Loc.isValid());
+
+ S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle)
+ << owner.Variable << capturer->getSourceRange();
+ S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner)
+ << owner.Indirect << owner.Range;
+}
+
+/// Check for a keyword selector that starts with the word 'add' or
+/// 'set'.
+static bool isSetterLikeSelector(Selector sel) {
+ if (sel.isUnarySelector()) return false;
+
+ llvm::StringRef str = sel.getNameForSlot(0);
+ while (!str.empty() && str.front() == '_') str = str.substr(1);
+ if (str.startswith("set") || str.startswith("add"))
+ str = str.substr(3);
+ else
+ return false;
+
+ if (str.empty()) return true;
+ return !islower(str.front());
+}
+
+/// Check a message send to see if it's likely to cause a retain cycle.
+void Sema::checkRetainCycles(ObjCMessageExpr *msg) {
+ // Only check instance methods whose selector looks like a setter.
+ if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector()))
+ return;
+
+ // Try to find a variable that the receiver is strongly owned by.
+ RetainCycleOwner owner;
+ if (msg->getReceiverKind() == ObjCMessageExpr::Instance) {
+ if (!findRetainCycleOwner(msg->getInstanceReceiver(), owner))
+ return;
+ } else {
+ assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
+ owner.Variable = getCurMethodDecl()->getSelfDecl();
+ owner.Loc = msg->getSuperLoc();
+ owner.Range = msg->getSuperLoc();
+ }
+
+ // Check whether the receiver is captured by any of the arguments.
+ for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i)
+ if (Expr *capturer = findCapturingExpr(*this, msg->getArg(i), owner))
+ return diagnoseRetainCycle(*this, capturer, owner);
+}
+
+/// Check a property assign to see if it's likely to cause a retain cycle.
+void Sema::checkRetainCycles(Expr *receiver, Expr *argument) {
+ RetainCycleOwner owner;
+ if (!findRetainCycleOwner(receiver, owner))
+ return;
+
+ if (Expr *capturer = findCapturingExpr(*this, argument, owner))
+ diagnoseRetainCycle(*this, capturer, owner);
+}
+
+void Sema::checkUnsafeAssigns(SourceLocation Loc,
+ QualType LHS, Expr *RHS) {
+ Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime();
+ if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone)
+ return;
+ if (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS))
+ if (cast->getCastKind() == CK_ObjCConsumeObject)
+ Diag(Loc, diag::warn_arc_retained_assign)
+ << (LT == Qualifiers::OCL_ExplicitNone)
+ << RHS->getSourceRange();
+}
+
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index e328eeb..69b3859 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -1676,6 +1676,34 @@
// Fall through: conditions and statements can have expressions.
case Sema::PCC_ParenthesizedExpression:
+ if (SemaRef.getLangOptions().ObjCAutoRefCount &&
+ CCC == Sema::PCC_ParenthesizedExpression) {
+ // (__bridge <type>)<expression>
+ Builder.AddTypedTextChunk("__bridge");
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddPlaceholderChunk("type");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Builder.AddPlaceholderChunk("expression");
+ Results.AddResult(Result(Builder.TakeString()));
+
+ // (__bridge_transfer <Objective-C type>)<expression>
+ Builder.AddTypedTextChunk("__bridge_transfer");
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddPlaceholderChunk("Objective-C type");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Builder.AddPlaceholderChunk("expression");
+ Results.AddResult(Result(Builder.TakeString()));
+
+ // (__bridge_retained <CF type>)<expression>
+ Builder.AddTypedTextChunk("__bridge_retained");
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddPlaceholderChunk("CF type");
+ Builder.AddChunk(CodeCompletionString::CK_RightParen);
+ Builder.AddPlaceholderChunk("expression");
+ Results.AddResult(Result(Builder.TakeString()));
+ }
+ // Fall through
+
case Sema::PCC_Expression: {
if (SemaRef.getLangOptions().CPlusPlus) {
// 'this', if we're in a non-static member function.
@@ -1828,7 +1856,8 @@
CodeCompletionAllocator &Allocator) {
PrintingPolicy Policy(Context.PrintingPolicy);
Policy.AnonymousTagLocations = false;
-
+ Policy.SuppressStrongLifetime = true;
+
if (!T.getLocalQualifiers()) {
// Built-in type names are constant strings.
if (const BuiltinType *BT = dyn_cast<BuiltinType>(T))
@@ -1878,9 +1907,9 @@
T = Context.getTypeDeclType(cast<TypeDecl>(Enumerator->getDeclContext()));
else if (isa<UnresolvedUsingValueDecl>(ND)) {
/* Do nothing: ignore unresolved using declarations*/
- } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND))
+ } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) {
T = Value->getType();
- else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND))
+ } else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND))
T = Property->getType();
if (T.isNull() || Context.hasSameType(T, Context.DependentTy))
@@ -1907,6 +1936,10 @@
static std::string FormatFunctionParameter(ASTContext &Context,
ParmVarDecl *Param,
bool SuppressName = false) {
+ PrintingPolicy Policy(Context.PrintingPolicy);
+ Policy.AnonymousTagLocations = false;
+ Policy.SuppressStrongLifetime = true;
+
bool ObjCMethodParam = isa<ObjCMethodDecl>(Param->getDeclContext());
if (Param->getType()->isDependentType() ||
!Param->getType()->isBlockPointerType()) {
@@ -1917,8 +1950,7 @@
if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName)
Result = Param->getIdentifier()->getName();
- Param->getType().getAsStringInternal(Result,
- Context.PrintingPolicy);
+ Param->getType().getAsStringInternal(Result, Policy);
if (ObjCMethodParam) {
Result = "(" + Result;
@@ -1968,8 +2000,7 @@
// We were unable to find a FunctionProtoTypeLoc with parameter names
// for the block; just use the parameter type as a placeholder.
std::string Result;
- Param->getType().getUnqualifiedType().
- getAsStringInternal(Result, Context.PrintingPolicy);
+ Param->getType().getUnqualifiedType().getAsStringInternal(Result, Policy);
if (ObjCMethodParam) {
Result = "(" + Result;
@@ -1986,7 +2017,7 @@
std::string Result;
QualType ResultType = Block->getTypePtr()->getResultType();
if (!ResultType->isVoidType())
- ResultType.getAsStringInternal(Result, Context.PrintingPolicy);
+ ResultType.getAsStringInternal(Result, Policy);
Result = '^' + Result;
if (!BlockProto || Block->getNumArgs() == 0) {
@@ -2071,6 +2102,9 @@
unsigned MaxParameters = 0,
unsigned Start = 0,
bool InDefaultArg = false) {
+ PrintingPolicy Policy(Context.PrintingPolicy);
+ Policy.AnonymousTagLocations = false;
+
typedef CodeCompletionString::Chunk Chunk;
bool FirstParameter = true;
@@ -2098,8 +2132,7 @@
= dyn_cast<NonTypeTemplateParmDecl>(*P)) {
if (NTTP->getIdentifier())
PlaceholderStr = NTTP->getIdentifier()->getName();
- NTTP->getType().getAsStringInternal(PlaceholderStr,
- Context.PrintingPolicy);
+ NTTP->getType().getAsStringInternal(PlaceholderStr, Policy);
HasDefaultArg = NTTP->hasDefaultArgument();
} else {
assert(isa<TemplateTemplateParmDecl>(*P));
@@ -2286,6 +2319,10 @@
typedef CodeCompletionString::Chunk Chunk;
CodeCompletionBuilder Result(Allocator, Priority, Availability);
+ PrintingPolicy Policy(S.Context.PrintingPolicy);
+ Policy.AnonymousTagLocations = false;
+ Policy.SuppressStrongLifetime = true;
+
if (Kind == RK_Pattern) {
Pattern->Priority = Priority;
Pattern->Availability = Availability;
@@ -2470,7 +2507,7 @@
if ((*P)->getType()->isBlockPointerType() && !DeclaringEntity)
Arg = FormatFunctionParameter(S.Context, *P, true);
else {
- (*P)->getType().getAsStringInternal(Arg, S.Context.PrintingPolicy);
+ (*P)->getType().getAsStringInternal(Arg, Policy);
Arg = "(" + Arg + ")";
if (IdentifierInfo *II = (*P)->getIdentifier())
if (DeclaringEntity || AllParametersAreInformative)
@@ -2519,7 +2556,10 @@
Sema &S,
CodeCompletionAllocator &Allocator) const {
typedef CodeCompletionString::Chunk Chunk;
-
+ PrintingPolicy Policy(S.Context.PrintingPolicy);
+ Policy.AnonymousTagLocations = false;
+ Policy.SuppressStrongLifetime = true;
+
// FIXME: Set priority, availability appropriately.
CodeCompletionBuilder Result(Allocator, 1, CXAvailability_Available);
FunctionDecl *FDecl = getFunction();
@@ -2545,7 +2585,7 @@
else
Result.AddTextChunk(
Result.getAllocator().CopyString(
- Proto->getResultType().getAsString(S.Context.PrintingPolicy)));
+ Proto->getResultType().getAsString(Policy)));
Result.AddChunk(Chunk(CodeCompletionString::CK_LeftParen));
unsigned NumParams = FDecl? FDecl->getNumParams() : Proto->getNumArgs();
@@ -2563,7 +2603,7 @@
ArgType = Proto->getArgType(I);
}
- ArgType.getAsStringInternal(ArgString, S.Context.PrintingPolicy);
+ ArgType.getAsStringInternal(ArgString, Policy);
if (I == CurrentArg)
Result.AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter,
@@ -3786,6 +3826,10 @@
void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD,
CXXCtorInitializer** Initializers,
unsigned NumInitializers) {
+ PrintingPolicy Policy(Context.PrintingPolicy);
+ Policy.AnonymousTagLocations = false;
+ Policy.SuppressStrongLifetime = true;
+
CXXConstructorDecl *Constructor
= static_cast<CXXConstructorDecl *>(ConstructorD);
if (!Constructor)
@@ -3825,7 +3869,7 @@
Builder.AddTypedTextChunk(
Results.getAllocator().CopyString(
- Base->getType().getAsString(Context.PrintingPolicy)));
+ Base->getType().getAsString(Policy)));
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("args");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
@@ -3850,7 +3894,7 @@
Builder.AddTypedTextChunk(
Builder.getAllocator().CopyString(
- Base->getType().getAsString(Context.PrintingPolicy)));
+ Base->getType().getAsString(Policy)));
Builder.AddChunk(CodeCompletionString::CK_LeftParen);
Builder.AddPlaceholderChunk("args");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
@@ -4126,18 +4170,24 @@
if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
(Attributes & (ObjCDeclSpec::DQ_PR_readwrite |
ObjCDeclSpec::DQ_PR_assign |
+ ObjCDeclSpec::DQ_PR_unsafe_unretained |
ObjCDeclSpec::DQ_PR_copy |
- ObjCDeclSpec::DQ_PR_retain)))
+ ObjCDeclSpec::DQ_PR_retain |
+ ObjCDeclSpec::DQ_PR_strong)))
return true;
- // Check for more than one of { assign, copy, retain }.
+ // Check for more than one of { assign, copy, retain, strong }.
unsigned AssignCopyRetMask = Attributes & (ObjCDeclSpec::DQ_PR_assign |
+ ObjCDeclSpec::DQ_PR_unsafe_unretained |
ObjCDeclSpec::DQ_PR_copy |
- ObjCDeclSpec::DQ_PR_retain);
+ ObjCDeclSpec::DQ_PR_retain|
+ ObjCDeclSpec::DQ_PR_strong);
if (AssignCopyRetMask &&
AssignCopyRetMask != ObjCDeclSpec::DQ_PR_assign &&
+ AssignCopyRetMask != ObjCDeclSpec::DQ_PR_unsafe_unretained &&
AssignCopyRetMask != ObjCDeclSpec::DQ_PR_copy &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain)
+ AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain &&
+ AssignCopyRetMask != ObjCDeclSpec::DQ_PR_strong)
return true;
return false;
@@ -4157,10 +4207,15 @@
Results.AddResult(CodeCompletionResult("readonly"));
if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_assign))
Results.AddResult(CodeCompletionResult("assign"));
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCDeclSpec::DQ_PR_unsafe_unretained))
+ Results.AddResult(CodeCompletionResult("unsafe_unretained"));
if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_readwrite))
Results.AddResult(CodeCompletionResult("readwrite"));
if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_retain))
Results.AddResult(CodeCompletionResult("retain"));
+ if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_strong))
+ Results.AddResult(CodeCompletionResult("strong"));
if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_copy))
Results.AddResult(CodeCompletionResult("copy"));
if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nonatomic))
@@ -4522,6 +4577,7 @@
if (Method->isInstanceMethod())
return llvm::StringSwitch<ObjCInterfaceDecl *>(Id->getName())
.Case("retain", IFace)
+ .Case("strong", IFace)
.Case("autorelease", IFace)
.Case("copy", IFace)
.Case("copyWithZone", IFace)
@@ -6273,6 +6329,7 @@
Results.EnterNewScope();
PrintingPolicy Policy(Context.PrintingPolicy);
Policy.AnonymousTagLocations = false;
+ Policy.SuppressStrongLifetime = true;
for (KnownMethodsMap::iterator M = KnownMethods.begin(),
MEnd = KnownMethods.end();
M != MEnd; ++M) {
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c7545cf..4476211 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1928,6 +1928,7 @@
return false;
}
+
void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
const ObjCMethodDecl *oldMethod) {
// Merge the attributes.
@@ -3473,6 +3474,50 @@
DD->setQualifierInfo(SS.getWithLocInContext(DD->getASTContext()));
}
+bool Sema::inferObjCARCLifetime(ValueDecl *decl) {
+ QualType type = decl->getType();
+ Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime();
+ if (lifetime == Qualifiers::OCL_Autoreleasing) {
+ // Various kinds of declaration aren't allowed to be __autoreleasing.
+ unsigned kind = -1U;
+ if (VarDecl *var = dyn_cast<VarDecl>(decl)) {
+ if (var->hasAttr<BlocksAttr>())
+ kind = 0; // __block
+ else if (!var->hasLocalStorage())
+ kind = 1; // global
+ } else if (isa<ObjCIvarDecl>(decl)) {
+ kind = 3; // ivar
+ } else if (isa<FieldDecl>(decl)) {
+ kind = 2; // field
+ }
+
+ if (kind != -1U) {
+ Diag(decl->getLocation(), diag::err_arc_autoreleasing_var)
+ << kind;
+ }
+ } else if (lifetime == Qualifiers::OCL_None) {
+ // Try to infer lifetime.
+ if (!type->isObjCLifetimeType())
+ return false;
+
+ lifetime = type->getObjCARCImplicitLifetime();
+ type = Context.getLifetimeQualifiedType(type, lifetime);
+ decl->setType(type);
+ }
+
+ if (VarDecl *var = dyn_cast<VarDecl>(decl)) {
+ // Thread-local variables cannot have lifetime.
+ if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone &&
+ var->isThreadSpecified()) {
+ Diag(var->getLocation(), diag::err_arc_thread_lifetime)
+ << var->getType();
+ return true;
+ }
+ }
+
+ return false;
+}
+
NamedDecl*
Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
QualType R, TypeSourceInfo *TInfo,
@@ -3631,6 +3676,11 @@
// Handle attributes prior to checking for duplicates in MergeVarDecl
ProcessDeclAttributes(S, NewVD, D);
+ // In auto-retain/release, infer strong retension for variables of
+ // retainable type.
+ if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewVD))
+ NewVD->setInvalidDecl();
+
// Handle GNU asm-label extension (encoded as an attribute).
if (Expr *E = (Expr*)D.getAsmLabel()) {
// The parser guarantees this is a string.
@@ -5229,6 +5279,10 @@
VDecl->setTypeSourceInfo(DeducedType);
VDecl->setType(DeducedType->getType());
+ // In ARC, infer lifetime.
+ if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))
+ VDecl->setInvalidDecl();
+
// If this is a redeclaration, check that the type we just deduced matches
// the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDeclaration())
@@ -5471,7 +5525,10 @@
// Check any implicit conversions within the expression.
CheckImplicitConversions(Init, VDecl->getLocation());
-
+
+ if (!VDecl->isInvalidDecl())
+ checkUnsafeAssigns(VDecl->getLocation(), VDecl->getType(), Init);
+
Init = MaybeCreateExprWithCleanups(Init);
// Attach the initializer to the decl.
VDecl->setInit(Init);
@@ -5740,6 +5797,23 @@
void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
if (var->isInvalidDecl()) return;
+ // In ARC, don't allow jumps past the implicit initialization of a
+ // local retaining variable.
+ if (getLangOptions().ObjCAutoRefCount &&
+ var->hasLocalStorage()) {
+ switch (var->getType().getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Autoreleasing:
+ break;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ getCurFunction()->setHasBranchProtectedScope();
+ break;
+ }
+ }
+
// All the following checks are C++ only.
if (!getLangOptions().CPlusPlus) return;
@@ -6008,7 +6082,7 @@
// Warn if the return value is pass-by-value and larger than the specified
// threshold.
- if (ReturnTy->isPODType()) {
+ if (ReturnTy.isPODType(Context)) {
unsigned Size = Context.getTypeSizeInChars(ReturnTy).getQuantity();
if (Size > LangOpts.NumLargeByValueCopy)
Diag(D->getLocation(), diag::warn_return_value_size)
@@ -6019,7 +6093,7 @@
// threshold.
for (; Param != ParamEnd; ++Param) {
QualType T = (*Param)->getType();
- if (!T->isPODType())
+ if (!T.isPODType(Context))
continue;
unsigned Size = Context.getTypeSizeInChars(T).getQuantity();
if (Size > LangOpts.NumLargeByValueCopy)
@@ -6033,6 +6107,28 @@
QualType T, TypeSourceInfo *TSInfo,
VarDecl::StorageClass StorageClass,
VarDecl::StorageClass StorageClassAsWritten) {
+ // In ARC, infer a lifetime qualifier for appropriate parameter types.
+ if (getLangOptions().ObjCAutoRefCount &&
+ T.getObjCLifetime() == Qualifiers::OCL_None &&
+ T->isObjCLifetimeType()) {
+
+ Qualifiers::ObjCLifetime lifetime;
+
+ // Special cases for arrays:
+ // - if it's const, use __unsafe_unretained
+ // - otherwise, it's an error
+ if (T->isArrayType()) {
+ if (!T.isConstQualified()) {
+ Diag(NameLoc, diag::err_arc_array_param_no_lifetime)
+ << TSInfo->getTypeLoc().getSourceRange();
+ }
+ lifetime = Qualifiers::OCL_ExplicitNone;
+ } else {
+ lifetime = T->getObjCARCImplicitLifetime();
+ }
+ T = Context.getLifetimeQualifiedType(T, lifetime);
+ }
+
ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
adjustParameterType(T), TSInfo,
StorageClass, StorageClassAsWritten,
@@ -6369,7 +6465,7 @@
// Verify that that gotos and switch cases don't jump into scopes illegally.
if (getCurFunction()->NeedsScopeChecking() &&
!dcl->isInvalidDecl() &&
- !hasAnyErrorsInThisFunction())
+ !hasAnyUnrecoverableErrorsInThisFunction())
DiagnoseInvalidJumps(Body);
if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(dcl)) {
@@ -6384,15 +6480,17 @@
// been leftover. This ensures that these temporaries won't be picked up for
// deletion in some later function.
if (PP.getDiagnostics().hasErrorOccurred() ||
- PP.getDiagnostics().getSuppressAllDiagnostics())
+ PP.getDiagnostics().getSuppressAllDiagnostics()) {
ExprTemporaries.clear();
- else if (!isa<FunctionTemplateDecl>(dcl)) {
+ ExprNeedsCleanups = false;
+ } else if (!isa<FunctionTemplateDecl>(dcl)) {
// Since the body is valid, issue any analysis-based warnings that are
// enabled.
ActivePolicy = &WP;
}
assert(ExprTemporaries.empty() && "Leftover temporaries in function");
+ assert(!ExprNeedsCleanups && "Unaccounted cleanups in function");
}
if (!IsInstantiation)
@@ -6403,8 +6501,10 @@
// If any errors have occurred, clear out any temporaries that may have
// been leftover. This ensures that these temporaries won't be picked up for
// deletion in some later function.
- if (getDiagnostics().hasErrorOccurred())
+ if (getDiagnostics().hasErrorOccurred()) {
ExprTemporaries.clear();
+ ExprNeedsCleanups = false;
+ }
return dcl;
}
@@ -7736,6 +7836,11 @@
// FIXME: What to pass instead of TUScope?
ProcessDeclAttributes(TUScope, NewFD, *D);
+ // In auto-retain/release, infer strong retension for fields of
+ // retainable type.
+ if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewFD))
+ NewFD->setInvalidDecl();
+
if (T.isObjCGCWeak())
Diag(Loc, diag::warn_attribute_weak_on_field);
@@ -7769,6 +7874,21 @@
member = CXXDestructor;
if (member != CXXInvalid) {
+ if (getLangOptions().ObjCAutoRefCount && RDecl->hasObjectMember()) {
+ // Objective-C++ ARC: it is an error to have a non-trivial field of
+ // a union. However, system headers in Objective-C programs
+ // occasionally have Objective-C lifetime objects within unions,
+ // and rather than cause the program to fail, we make those
+ // members unavailable.
+ SourceLocation Loc = FD->getLocation();
+ if (getSourceManager().isInSystemHeader(Loc)) {
+ if (!FD->hasAttr<UnavailableAttr>())
+ FD->addAttr(new (Context) UnavailableAttr(Loc, Context,
+ "this system field has retaining lifetime"));
+ return false;
+ }
+ }
+
Diag(FD->getLocation(), diag::err_illegal_union_or_anon_struct_member)
<< (int)FD->getParent()->isUnion() << FD->getDeclName() << member;
DiagnoseNontrivial(RT, member);
@@ -7921,6 +8041,21 @@
return;
}
}
+
+ if (EltTy->isObjCLifetimeType()) {
+ switch (EltTy.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ break;
+
+ case Qualifiers::OCL_Autoreleasing:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ Diag((*fi)->getLocation(), diag::note_nontrivial_objc_lifetime)
+ << QT << EltTy.getObjCLifetime();
+ return;
+ }
+ }
}
assert(0 && "found no explanation for non-trivial member");
@@ -8030,6 +8165,10 @@
if (D.isInvalidType())
NewID->setInvalidDecl();
+ // In ARC, infer 'retaining' for ivars of retainable type.
+ if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewID))
+ NewID->setInvalidDecl();
+
if (II) {
// FIXME: When interfaces are DeclContexts, we'll need to add
// these to the interface.
@@ -8101,6 +8240,7 @@
llvm::SmallVector<FieldDecl*, 32> RecFields;
RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
+ bool ARCErrReported = false;
for (unsigned i = 0; i != NumFields; ++i) {
FieldDecl *FD = cast<FieldDecl>(Fields[i]);
@@ -8166,7 +8306,7 @@
continue;
}
if (!FD->getType()->isDependentType() &&
- !Context.getBaseElementType(FD->getType())->isPODType()) {
+ !Context.getBaseElementType(FD->getType()).isPODType(Context)) {
Diag(FD->getLocation(), diag::err_flexible_array_has_nonpod_type)
<< FD->getDeclName() << FD->getType();
FD->setInvalidDecl();
@@ -8213,17 +8353,45 @@
FD->setInvalidDecl();
EnclosingDecl->setInvalidDecl();
continue;
- } else if (getLangOptions().ObjC1 &&
+ }
+ else if (!getLangOptions().CPlusPlus) {
+ if (getLangOptions().ObjCAutoRefCount && Record && !ARCErrReported) {
+ // It's an error in ARC if a field has lifetime.
+ // We don't want to report this in a system header, though,
+ // so we just make the field unavailable.
+ // FIXME: that's really not sufficient; we need to make the type
+ // itself invalid to, say, initialize or copy.
+ QualType T = FD->getType();
+ Qualifiers::ObjCLifetime lifetime = T.getObjCLifetime();
+ if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone) {
+ SourceLocation loc = FD->getLocation();
+ if (getSourceManager().isInSystemHeader(loc)) {
+ if (!FD->hasAttr<UnavailableAttr>()) {
+ FD->addAttr(new (Context) UnavailableAttr(loc, Context,
+ "this system field has retaining lifetime"));
+ }
+ } else {
+ Diag(FD->getLocation(), diag::err_arc_objc_object_in_struct);
+ }
+ ARCErrReported = true;
+ }
+ }
+ else if (getLangOptions().ObjC1 &&
getLangOptions().getGCMode() != LangOptions::NonGC &&
- Record &&
- (FD->getType()->isObjCObjectPointerType() ||
- FD->getType().isObjCGCStrong()))
- Record->setHasObjectMember(true);
- else if (Context.getAsArrayType(FD->getType())) {
- QualType BaseType = Context.getBaseElementType(FD->getType());
- if (Record && BaseType->isRecordType() &&
- BaseType->getAs<RecordType>()->getDecl()->hasObjectMember())
- Record->setHasObjectMember(true);
+ Record && !Record->hasObjectMember()) {
+ if (FD->getType()->isObjCObjectPointerType() ||
+ FD->getType().isObjCGCStrong())
+ Record->setHasObjectMember(true);
+ else if (Context.getAsArrayType(FD->getType())) {
+ QualType BaseType = Context.getBaseElementType(FD->getType());
+ if (BaseType->isRecordType() &&
+ BaseType->getAs<RecordType>()->getDecl()->hasObjectMember())
+ Record->setHasObjectMember(true);
+ else if (BaseType->isObjCObjectPointerType() ||
+ BaseType.isObjCGCStrong())
+ Record->setHasObjectMember(true);
+ }
+ }
}
// Keep track of the number of named members.
if (FD->getIdentifier())
@@ -8242,6 +8410,42 @@
Convs->setAccess(I, (*I)->getAccess());
if (!CXXRecord->isDependentType()) {
+ // Objective-C Automatic Reference Counting:
+ // If a class has a non-static data member of Objective-C pointer
+ // type (or array thereof), it is a non-POD type and its
+ // default constructor (if any), copy constructor, copy assignment
+ // operator, and destructor are non-trivial.
+ //
+ // This rule is also handled by CXXRecordDecl::completeDefinition().
+ // However, here we check whether this particular class is only
+ // non-POD because of the presence of an Objective-C pointer member.
+ // If so, objects of this type cannot be shared between code compiled
+ // with instant objects and code compiled with manual retain/release.
+ if (getLangOptions().ObjCAutoRefCount &&
+ CXXRecord->hasObjectMember() &&
+ CXXRecord->getLinkage() == ExternalLinkage) {
+ if (CXXRecord->isPOD()) {
+ Diag(CXXRecord->getLocation(),
+ diag::warn_arc_non_pod_class_with_object_member)
+ << CXXRecord;
+ } else {
+ // FIXME: Fix-Its would be nice here, but finding a good location
+ // for them is going to be tricky.
+ if (CXXRecord->hasTrivialCopyConstructor())
+ Diag(CXXRecord->getLocation(),
+ diag::warn_arc_trivial_member_function_with_object_member)
+ << CXXRecord << 0;
+ if (CXXRecord->hasTrivialCopyAssignment())
+ Diag(CXXRecord->getLocation(),
+ diag::warn_arc_trivial_member_function_with_object_member)
+ << CXXRecord << 1;
+ if (CXXRecord->hasTrivialDestructor())
+ Diag(CXXRecord->getLocation(),
+ diag::warn_arc_trivial_member_function_with_object_member)
+ << CXXRecord << 2;
+ }
+ }
+
// Adjust user-defined destructor exception spec.
if (getLangOptions().CPlusPlus0x &&
CXXRecord->hasUserDeclaredDestructor())
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 430b4f9..4769b09 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/DelayedDiagnostic.h"
@@ -101,8 +102,9 @@
/// Return true if the given decl has a declarator that should have
/// been processed by Sema::GetTypeForDeclarator.
static bool hasDeclarator(const Decl *d) {
- // In some sense, TypedefNameDecl really *ought* to be a DeclaratorDecl.
- return isa<DeclaratorDecl>(d) || isa<BlockDecl>(d) || isa<TypedefNameDecl>(d);
+ // In some sense, TypedefDecl really *ought* to be a DeclaratorDecl.
+ return isa<DeclaratorDecl>(d) || isa<BlockDecl>(d) || isa<TypedefNameDecl>(d) ||
+ isa<ObjCPropertyDecl>(d);
}
/// hasFunctionProto - Return true if the given decl has a argument
@@ -1216,8 +1218,16 @@
return;
}
- decl->addAttr(new (S.Context) ObjCMethodFamilyAttr(attr.getLoc(),
- S.Context, family));
+ if (family == ObjCMethodFamilyAttr::OMF_init &&
+ !method->getResultType()->isObjCObjectPointerType()) {
+ S.Diag(method->getLocation(), diag::err_init_method_bad_return_type)
+ << method->getResultType();
+ // Ignore the attribute.
+ return;
+ }
+
+ method->addAttr(new (S.Context) ObjCMethodFamilyAttr(attr.getLoc(),
+ S.Context, family));
}
static void HandleObjCExceptionAttr(Decl *D, const AttributeList &Attr,
@@ -2706,6 +2716,9 @@
if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(d))
returnType = MD->getResultType();
+ else if (S.getLangOptions().ObjCAutoRefCount && hasDeclarator(d) &&
+ (attr.getKind() == AttributeList::AT_ns_returns_retained))
+ return; // ignore: was handled as a type attribute
else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(d))
returnType = FD->getResultType();
else {
@@ -2767,6 +2780,62 @@
};
}
+static void HandleObjCLifetimeAttr(Decl *d, const AttributeList &attr,
+ Sema &S) {
+ if (hasDeclarator(d)) return;
+
+ SourceLocation L = attr.getLoc();
+ S.Diag(d->getLocStart(), diag::err_attribute_wrong_decl_type)
+ << SourceRange(L, L) << attr.getName() << 12 /* variable */;
+}
+
+static void HandleObjCPreciseLifetimeAttr(Decl *d, const AttributeList &attr,
+ Sema &S) {
+ if (!isa<VarDecl>(d) && !isa<FieldDecl>(d)) {
+ SourceLocation L = attr.getLoc();
+ S.Diag(d->getLocStart(), diag::err_attribute_wrong_decl_type)
+ << SourceRange(L, L) << attr.getName() << 12 /* variable */;
+ return;
+ }
+
+ ValueDecl *vd = cast<ValueDecl>(d);
+ QualType type = vd->getType();
+
+ if (!type->isDependentType() &&
+ !type->isObjCLifetimeType()) {
+ S.Diag(attr.getLoc(), diag::err_objc_precise_lifetime_bad_type)
+ << type;
+ return;
+ }
+
+ Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime();
+
+ // If we have no lifetime yet, check the lifetime we're presumably
+ // going to infer.
+ if (lifetime == Qualifiers::OCL_None && !type->isDependentType())
+ lifetime = type->getObjCARCImplicitLifetime();
+
+ switch (lifetime) {
+ case Qualifiers::OCL_None:
+ assert(type->isDependentType() &&
+ "didn't infer lifetime for non-dependent type?");
+ break;
+
+ case Qualifiers::OCL_Weak: // meaningful
+ case Qualifiers::OCL_Strong: // meaningful
+ break;
+
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Autoreleasing:
+ S.Diag(attr.getLoc(), diag::warn_objc_precise_lifetime_meaningless)
+ << (lifetime == Qualifiers::OCL_Autoreleasing);
+ break;
+ }
+
+ d->addAttr(::new (S.Context)
+ ObjCPreciseLifetimeAttr(attr.getLoc(), S.Context));
+}
+
static bool isKnownDeclSpecAttr(const AttributeList &Attr) {
return Attr.getKind() == AttributeList::AT_dllimport ||
Attr.getKind() == AttributeList::AT_dllexport ||
@@ -2909,6 +2978,11 @@
case AttributeList::AT_shared: HandleSharedAttr (D, Attr, S); break;
case AttributeList::AT_vecreturn: HandleVecReturnAttr (D, Attr, S); break;
+ case AttributeList::AT_objc_lifetime:
+ HandleObjCLifetimeAttr(D, Attr, S); break;
+ case AttributeList::AT_objc_precise_lifetime:
+ HandleObjCPreciseLifetimeAttr(D, Attr, S); break;
+
// Checker-specific.
case AttributeList::AT_cf_consumed:
case AttributeList::AT_ns_consumed: HandleNSConsumedAttr (D, Attr, S); break;
@@ -3117,6 +3191,32 @@
ProcessDeclAttributeList(S, D, Attrs, NonInheritable, Inheritable);
}
+/// Is the given declaration allowed to use a forbidden type?
+static bool isForbiddenTypeAllowed(Sema &S, Decl *decl) {
+ // Private ivars are always okay. Unfortunately, people don't
+ // always properly make their ivars private, even in system headers.
+ // Plus we need to make fields okay, too.
+ if (!isa<FieldDecl>(decl) && !isa<ObjCPropertyDecl>(decl))
+ return false;
+
+ // Require it to be declared in a system header.
+ return S.Context.getSourceManager().isInSystemHeader(decl->getLocation());
+}
+
+/// Handle a delayed forbidden-type diagnostic.
+static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag,
+ Decl *decl) {
+ if (decl && isForbiddenTypeAllowed(S, decl)) {
+ decl->addAttr(new (S.Context) UnavailableAttr(diag.Loc, S.Context,
+ "this system declaration uses an unsupported type"));
+ return;
+ }
+
+ S.Diag(diag.Loc, diag.getForbiddenTypeDiagnostic())
+ << diag.getForbiddenTypeOperand() << diag.getForbiddenTypeArgument();
+ diag.Triggered = true;
+}
+
// This duplicates a vector push_back but hides the need to know the
// size of the type.
void Sema::DelayedDiagnostics::add(const DelayedDiagnostic &diag) {
@@ -3179,6 +3279,10 @@
case DelayedDiagnostic::Access:
S.HandleDelayedAccessCheck(diag, decl);
break;
+
+ case DelayedDiagnostic::ForbiddenType:
+ handleDelayedForbiddenType(S, diag, decl);
+ break;
}
}
}
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index ce99efb..09597d2 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -762,13 +762,6 @@
QualType NewBaseType
= Context.getCanonicalType(Bases[idx]->getType());
NewBaseType = NewBaseType.getLocalUnqualifiedType();
- if (!Class->hasObjectMember()) {
- if (const RecordType *FDTTy =
- NewBaseType.getTypePtr()->getAs<RecordType>())
- if (FDTTy->getDecl()->hasObjectMember())
- Class->setHasObjectMember(true);
- }
-
if (KnownBaseTypes[NewBaseType]) {
// C++ [class.mi]p3:
// A class shall not be specified as a direct base class of a
@@ -1619,12 +1612,7 @@
Init = new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs,
RParenLoc);
- // Erase any temporaries within this evaluation context; we're not
- // going to track them in the AST, since we'll be rebuilding the
- // ASTs during template instantiation.
- ExprTemporaries.erase(
- ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries,
- ExprTemporaries.end());
+ DiscardCleanupsInEvaluationContext();
} else {
// Initialize the member.
InitializedEntity MemberEntity =
@@ -1817,12 +1805,7 @@
= Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs,
RParenLoc));
- // Erase any temporaries within this evaluation context; we're not
- // going to track them in the AST, since we'll be rebuilding the
- // ASTs during template instantiation.
- ExprTemporaries.erase(
- ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries,
- ExprTemporaries.end());
+ DiscardCleanupsInEvaluationContext();
return new (Context) CXXCtorInitializer(Context, BaseTInfo,
/*IsVirtual=*/false,
@@ -2134,6 +2117,20 @@
}
}
+ if (SemaRef.getLangOptions().ObjCAutoRefCount &&
+ FieldBaseElementType->isObjCRetainableType() &&
+ FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None &&
+ FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) {
+ // Instant objects:
+ // Default-initialize Objective-C pointers to NULL.
+ CXXMemberInit
+ = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field,
+ Loc, Loc,
+ new (SemaRef.Context) ImplicitValueInitExpr(Field->getType()),
+ Loc);
+ return false;
+ }
+
// Nothing to initialize.
CXXMemberInit = 0;
return false;
@@ -7041,10 +7038,8 @@
// explicit assignments, do so. This optimization only applies for arrays
// of scalars and arrays of class type with trivial copy-assignment
// operators.
- if (FieldType->isArrayType() &&
- (!BaseType->isRecordType() ||
- cast<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl())
- ->hasTrivialCopyAssignment())) {
+ if (FieldType->isArrayType() &&
+ BaseType.hasTrivialCopyAssignment(Context)) {
// Compute the size of the memory buffer to be copied.
QualType SizeType = Context.getSizeType();
llvm::APInt Size(Context.getTypeSize(SizeType),
@@ -7523,6 +7518,10 @@
VDecl->setTypeSourceInfo(DeducedType);
VDecl->setType(DeducedType->getType());
+ // In ARC, infer lifetime.
+ if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))
+ VDecl->setInvalidDecl();
+
// If this is a redeclaration, check that the type we just deduced matches
// the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDeclaration())
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index bfa2ef4..563de6f 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -16,14 +16,96 @@
#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/AST/ASTConsumer.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Sema/DeclSpec.h"
#include "llvm/ADT/DenseSet.h"
using namespace clang;
+/// Check whether the given method, which must be in the 'init'
+/// family, is a valid member of that family.
+///
+/// \param receiverTypeIfCall - if null, check this as if declaring it;
+/// if non-null, check this as if making a call to it with the given
+/// receiver type
+///
+/// \return true to indicate that there was an error and appropriate
+/// actions were taken
+bool Sema::checkInitMethod(ObjCMethodDecl *method,
+ QualType receiverTypeIfCall) {
+ if (method->isInvalidDecl()) return true;
+
+ // This castAs is safe: methods that don't return an object
+ // pointer won't be inferred as inits and will reject an explicit
+ // objc_method_family(init).
+
+ // We ignore protocols here. Should we? What about Class?
+
+ const ObjCObjectType *result = method->getResultType()
+ ->castAs<ObjCObjectPointerType>()->getObjectType();
+
+ if (result->isObjCId()) {
+ return false;
+ } else if (result->isObjCClass()) {
+ // fall through: always an error
+ } else {
+ ObjCInterfaceDecl *resultClass = result->getInterface();
+ assert(resultClass && "unexpected object type!");
+
+ // It's okay for the result type to still be a forward declaration
+ // if we're checking an interface declaration.
+ if (resultClass->isForwardDecl()) {
+ if (receiverTypeIfCall.isNull() &&
+ !isa<ObjCImplementationDecl>(method->getDeclContext()))
+ return false;
+
+ // Otherwise, we try to compare class types.
+ } else {
+ // If this method was declared in a protocol, we can't check
+ // anything unless we have a receiver type that's an interface.
+ const ObjCInterfaceDecl *receiverClass = 0;
+ if (isa<ObjCProtocolDecl>(method->getDeclContext())) {
+ if (receiverTypeIfCall.isNull())
+ return false;
+
+ receiverClass = receiverTypeIfCall->castAs<ObjCObjectPointerType>()
+ ->getInterfaceDecl();
+
+ // This can be null for calls to e.g. id<Foo>.
+ if (!receiverClass) return false;
+ } else {
+ receiverClass = method->getClassInterface();
+ assert(receiverClass && "method not associated with a class!");
+ }
+
+ // If either class is a subclass of the other, it's fine.
+ if (receiverClass->isSuperClassOf(resultClass) ||
+ resultClass->isSuperClassOf(receiverClass))
+ return false;
+ }
+ }
+
+ SourceLocation loc = method->getLocation();
+
+ // If we're in a system header, and this is not a call, just make
+ // the method unusable.
+ if (receiverTypeIfCall.isNull() && getSourceManager().isInSystemHeader(loc)) {
+ method->addAttr(new (Context) UnavailableAttr(loc, Context,
+ "init method returns a type unrelated to its receiver type"));
+ return true;
+ }
+
+ // Otherwise, it's an error.
+ Diag(loc, diag::err_arc_init_method_unrelated_result_type);
+ method->setInvalidDecl();
+ return true;
+}
+
bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod,
const ObjCMethodDecl *Overridden,
bool IsImplementation) {
@@ -36,7 +118,7 @@
QualType ResultType = NewMethod->getResultType();
SourceRange ResultTypeRange;
if (const TypeSourceInfo *ResultTypeInfo
- = NewMethod->getResultTypeSourceInfo())
+ = NewMethod->getResultTypeSourceInfo())
ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange();
// Figure out which class this method is part of, if any.
@@ -107,10 +189,10 @@
if (CheckObjCMethodOverrides(S, NewMethod, Category, false))
return true;
}
-
+
// Look through protocols.
for (ObjCList<ObjCProtocolDecl>::iterator I = Class->protocol_begin(),
- IEnd = Class->protocol_end();
+ IEnd = Class->protocol_end();
I != IEnd; ++I)
if (CheckObjCMethodOverrides(S, NewMethod, *I, false))
return true;
@@ -123,7 +205,7 @@
if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) {
// Look through protocols.
for (ObjCList<ObjCProtocolDecl>::iterator I = Category->protocol_begin(),
- IEnd = Category->protocol_end();
+ IEnd = Category->protocol_end();
I != IEnd; ++I)
if (CheckObjCMethodOverrides(S, NewMethod, *I, false))
return true;
@@ -134,7 +216,7 @@
if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) {
// Look through protocols.
for (ObjCList<ObjCProtocolDecl>::iterator I = Protocol->protocol_begin(),
- IEnd = Protocol->protocol_end();
+ IEnd = Protocol->protocol_end();
I != IEnd; ++I)
if (CheckObjCMethodOverrides(S, NewMethod, *I, false))
return true;
@@ -149,13 +231,13 @@
DeclContext *DC) {
if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(DC))
return ::CheckObjCMethodOverrides(*this, NewMethod, Class);
-
+
if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC))
return ::CheckObjCMethodOverrides(*this, NewMethod, Category);
-
+
if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC))
return ::CheckObjCMethodOverrides(*this, NewMethod, Protocol);
-
+
if (ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(DC))
return ::CheckObjCMethodOverrides(*this, NewMethod,
Impl->getClassInterface());
@@ -167,6 +249,50 @@
return ::CheckObjCMethodOverrides(*this, NewMethod, CurContext);
}
+/// \brief Check a method declaration for compatibility with the Objective-C
+/// ARC conventions.
+static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) {
+ ObjCMethodFamily family = method->getMethodFamily();
+ switch (family) {
+ case OMF_None:
+ case OMF_dealloc:
+ case OMF_retain:
+ case OMF_release:
+ case OMF_autorelease:
+ case OMF_retainCount:
+ case OMF_self:
+ return false;
+
+ case OMF_init:
+ // If the method doesn't obey the init rules, don't bother annotating it.
+ if (S.checkInitMethod(method, QualType()))
+ return true;
+
+ method->addAttr(new (S.Context) NSConsumesSelfAttr(SourceLocation(),
+ S.Context));
+
+ // Don't add a second copy of this attribute, but otherwise don't
+ // let it be suppressed.
+ if (method->hasAttr<NSReturnsRetainedAttr>())
+ return false;
+ break;
+
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ case OMF_new:
+ if (method->hasAttr<NSReturnsRetainedAttr>() ||
+ method->hasAttr<NSReturnsNotRetainedAttr>() ||
+ method->hasAttr<NSReturnsAutoreleasedAttr>())
+ return false;
+ break;
+ }
+
+ method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(),
+ S.Context));
+ return false;
+}
+
static void DiagnoseObjCImplementedDeprecations(Sema &S,
NamedDecl *ND,
SourceLocation ImplLoc,
@@ -220,6 +346,30 @@
if ((*PI)->getIdentifier())
PushOnScopeChains(*PI, FnBodyScope);
}
+
+ // In ARC, disallow definition of retain/release/autorelease/retainCount
+ if (getLangOptions().ObjCAutoRefCount) {
+ switch (MDecl->getMethodFamily()) {
+ case OMF_retain:
+ case OMF_retainCount:
+ case OMF_release:
+ case OMF_autorelease:
+ Diag(MDecl->getLocation(), diag::err_arc_illegal_method_def)
+ << MDecl->getSelector();
+ break;
+
+ case OMF_None:
+ case OMF_dealloc:
+ case OMF_alloc:
+ case OMF_init:
+ case OMF_mutableCopy:
+ case OMF_copy:
+ case OMF_new:
+ case OMF_self:
+ break;
+ }
+ }
+
// Warn on implementating deprecated methods under
// -Wdeprecated-implementations flag.
if (ObjCInterfaceDecl *IC = MDecl->getClassInterface())
@@ -1099,11 +1249,83 @@
S.Diag(IfaceVar->getLocation(), diag::note_previous_definition)
<< getTypeRange(IfaceVar->getTypeSourceInfo());
}
-
+
+/// In ARC, check whether the conventional meanings of the two methods
+/// match. If they don't, it's a hard error.
+static bool checkMethodFamilyMismatch(Sema &S, ObjCMethodDecl *impl,
+ ObjCMethodDecl *decl) {
+ ObjCMethodFamily implFamily = impl->getMethodFamily();
+ ObjCMethodFamily declFamily = decl->getMethodFamily();
+ if (implFamily == declFamily) return false;
+
+ // Since conventions are sorted by selector, the only possibility is
+ // that the types differ enough to cause one selector or the other
+ // to fall out of the family.
+ assert(implFamily == OMF_None || declFamily == OMF_None);
+
+ // No further diagnostics required on invalid declarations.
+ if (impl->isInvalidDecl() || decl->isInvalidDecl()) return true;
+
+ const ObjCMethodDecl *unmatched = impl;
+ ObjCMethodFamily family = declFamily;
+ unsigned errorID = diag::err_arc_lost_method_convention;
+ unsigned noteID = diag::note_arc_lost_method_convention;
+ if (declFamily == OMF_None) {
+ unmatched = decl;
+ family = implFamily;
+ errorID = diag::err_arc_gained_method_convention;
+ noteID = diag::note_arc_gained_method_convention;
+ }
+
+ // Indexes into a %select clause in the diagnostic.
+ enum FamilySelector {
+ F_alloc, F_copy, F_mutableCopy = F_copy, F_init, F_new
+ };
+ FamilySelector familySelector = FamilySelector();
+
+ switch (family) {
+ case OMF_None: llvm_unreachable("logic error, no method convention");
+ case OMF_retain:
+ case OMF_release:
+ case OMF_autorelease:
+ case OMF_dealloc:
+ case OMF_retainCount:
+ case OMF_self:
+ // Mismatches for these methods don't change ownership
+ // conventions, so we don't care.
+ return false;
+
+ case OMF_init: familySelector = F_init; break;
+ case OMF_alloc: familySelector = F_alloc; break;
+ case OMF_copy: familySelector = F_copy; break;
+ case OMF_mutableCopy: familySelector = F_mutableCopy; break;
+ case OMF_new: familySelector = F_new; break;
+ }
+
+ enum ReasonSelector { R_NonObjectReturn, R_UnrelatedReturn };
+ ReasonSelector reasonSelector;
+
+ // The only reason these methods don't fall within their families is
+ // due to unusual result types.
+ if (unmatched->getResultType()->isObjCObjectPointerType()) {
+ reasonSelector = R_UnrelatedReturn;
+ } else {
+ reasonSelector = R_NonObjectReturn;
+ }
+
+ S.Diag(impl->getLocation(), errorID) << familySelector << reasonSelector;
+ S.Diag(decl->getLocation(), noteID) << familySelector << reasonSelector;
+
+ return true;
+}
void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl,
ObjCMethodDecl *MethodDecl,
bool IsProtocolMethodDecl) {
+ if (getLangOptions().ObjCAutoRefCount &&
+ checkMethodFamilyMismatch(*this, ImpMethodDecl, MethodDecl))
+ return;
+
CheckMethodOverrideReturn(*this, ImpMethodDecl, MethodDecl,
IsProtocolMethodDecl);
@@ -1430,48 +1652,82 @@
return CDecl;
}
+static bool matchTypes(ASTContext &Context, Sema::MethodMatchStrategy strategy,
+ QualType leftQT, QualType rightQT) {
+ const Type *left =
+ Context.getCanonicalType(leftQT).getUnqualifiedType().getTypePtr();
+ const Type *right =
+ Context.getCanonicalType(rightQT).getUnqualifiedType().getTypePtr();
+
+ if (left == right) return true;
+
+ // If we're doing a strict match, the types have to match exactly.
+ if (strategy == Sema::MMS_strict) return false;
+
+ if (left->isIncompleteType() || right->isIncompleteType()) return false;
+
+ // Otherwise, use this absurdly complicated algorithm to try to
+ // validate the basic, low-level compatibility of the two types.
+
+ // As a minimum, require the sizes and alignments to match.
+ if (Context.getTypeInfo(left) != Context.getTypeInfo(right))
+ return false;
+
+ // Consider all the kinds of non-dependent canonical types:
+ // - functions and arrays aren't possible as return and parameter types
+
+ // - vector types of equal size can be arbitrarily mixed
+ if (isa<VectorType>(left)) return isa<VectorType>(right);
+ if (isa<VectorType>(right)) return false;
+
+ // - references should only match references of identical type
+ // - structs, unions, and Objective-C objects must match exactly
+ // - everything else should be a scalar
+ if (!left->isScalarType() || !right->isScalarType())
+ return false;
+
+ // Make scalars agree in kind, except count bools as chars.
+ Type::ScalarTypeKind leftSK = left->getScalarTypeKind();
+ Type::ScalarTypeKind rightSK = right->getScalarTypeKind();
+ if (leftSK == Type::STK_Bool) leftSK = Type::STK_Integral;
+ if (rightSK == Type::STK_Bool) rightSK = Type::STK_Integral;
+
+ // Note that data member pointers and function member pointers don't
+ // intermix because of the size differences.
+
+ return (leftSK == rightSK);
+}
/// MatchTwoMethodDeclarations - Checks that two methods have matching type and
/// returns true, or false, accordingly.
/// TODO: Handle protocol list; such as id<p1,p2> in type comparisons
-bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method,
- const ObjCMethodDecl *PrevMethod,
- bool matchBasedOnSizeAndAlignment,
- bool matchBasedOnStrictEqulity) {
- QualType T1 = Context.getCanonicalType(Method->getResultType());
- QualType T2 = Context.getCanonicalType(PrevMethod->getResultType());
+bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left,
+ const ObjCMethodDecl *right,
+ MethodMatchStrategy strategy) {
+ if (!matchTypes(Context, strategy,
+ left->getResultType(), right->getResultType()))
+ return false;
- if (T1 != T2) {
- // The result types are different.
- if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity)
- return false;
- // Incomplete types don't have a size and alignment.
- if (T1->isIncompleteType() || T2->isIncompleteType())
- return false;
- // Check is based on size and alignment.
- if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2))
- return false;
- }
+ if (getLangOptions().ObjCAutoRefCount &&
+ (left->hasAttr<NSReturnsRetainedAttr>()
+ != right->hasAttr<NSReturnsRetainedAttr>() ||
+ left->hasAttr<NSConsumesSelfAttr>()
+ != right->hasAttr<NSConsumesSelfAttr>()))
+ return false;
- ObjCMethodDecl::param_iterator ParamI = Method->param_begin(),
- E = Method->param_end();
- ObjCMethodDecl::param_iterator PrevI = PrevMethod->param_begin();
+ ObjCMethodDecl::param_iterator
+ li = left->param_begin(), le = left->param_end(), ri = right->param_begin();
- for (; ParamI != E; ++ParamI, ++PrevI) {
- assert(PrevI != PrevMethod->param_end() && "Param mismatch");
- T1 = Context.getCanonicalType((*ParamI)->getType());
- T2 = Context.getCanonicalType((*PrevI)->getType());
- if (T1 != T2) {
- // The result types are different.
- if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity)
- return false;
- // Incomplete types don't have a size and alignment.
- if (T1->isIncompleteType() || T2->isIncompleteType())
- return false;
- // Check is based on size and alignment.
- if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2))
- return false;
- }
+ for (; li != le; ++li, ++ri) {
+ assert(ri != right->param_end() && "Param mismatch");
+ ParmVarDecl *lparm = *li, *rparm = *ri;
+
+ if (!matchTypes(Context, strategy, lparm->getType(), rparm->getType()))
+ return false;
+
+ if (getLangOptions().ObjCAutoRefCount &&
+ lparm->hasAttr<NSConsumedAttr>() != rparm->hasAttr<NSConsumedAttr>())
+ return false;
}
return true;
}
@@ -1513,8 +1769,10 @@
// We've seen a method with this name, see if we have already seen this type
// signature.
- for (ObjCMethodList *List = &Entry; List; List = List->Next)
- if (MatchTwoMethodDeclarations(Method, List->Method)) {
+ for (ObjCMethodList *List = &Entry; List; List = List->Next) {
+ bool match = MatchTwoMethodDeclarations(Method, List->Method);
+
+ if (match) {
ObjCMethodDecl *PrevObjCMethod = List->Method;
PrevObjCMethod->setDefined(impl);
// If a method is deprecated, push it in the global pool.
@@ -1531,6 +1789,7 @@
}
return;
}
+ }
// We have a new signature for an existing method - add it.
// This is extremely rare. Only 1% of Cocoa selectors are "overloaded".
@@ -1538,6 +1797,25 @@
Entry.Next = new (Mem) ObjCMethodList(Method, Entry.Next);
}
+/// Determines if this is an "acceptable" loose mismatch in the global
+/// method pool. This exists mostly as a hack to get around certain
+/// global mismatches which we can't afford to make warnings / errors.
+/// Really, what we want is a way to take a method out of the global
+/// method pool.
+static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen,
+ ObjCMethodDecl *other) {
+ if (!chosen->isInstanceMethod())
+ return false;
+
+ Selector sel = chosen->getSelector();
+ if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length")
+ return false;
+
+ // Don't complain about mismatches for -length if the method we
+ // chose has an integral result type.
+ return (chosen->getResultType()->isIntegerType());
+}
+
ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R,
bool receiverIdOrClass,
bool warn, bool instance) {
@@ -1551,32 +1829,52 @@
ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second;
- bool strictSelectorMatch = receiverIdOrClass && warn &&
- (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl,
- R.getBegin()) !=
- Diagnostic::Ignored);
if (warn && MethList.Method && MethList.Next) {
- bool issueWarning = false;
+ bool issueDiagnostic = false, issueError = false;
+
+ // We support a warning which complains about *any* difference in
+ // method signature.
+ bool strictSelectorMatch =
+ (receiverIdOrClass && warn &&
+ (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl,
+ R.getBegin()) !=
+ Diagnostic::Ignored));
if (strictSelectorMatch)
for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) {
- // This checks if the methods differ in type mismatch.
- if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, false, true))
- issueWarning = true;
+ if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method,
+ MMS_strict)) {
+ issueDiagnostic = true;
+ break;
+ }
}
- if (!issueWarning)
+ // If we didn't see any strict differences, we won't see any loose
+ // differences. In ARC, however, we also need to check for loose
+ // mismatches, because most of them are errors.
+ if (!strictSelectorMatch ||
+ (issueDiagnostic && getLangOptions().ObjCAutoRefCount))
for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) {
- // This checks if the methods differ by size & alignment.
- if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true))
- issueWarning = true;
+ // This checks if the methods differ in type mismatch.
+ if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method,
+ MMS_loose) &&
+ !isAcceptableMethodMismatch(MethList.Method, Next->Method)) {
+ issueDiagnostic = true;
+ if (getLangOptions().ObjCAutoRefCount)
+ issueError = true;
+ break;
+ }
}
- if (issueWarning) {
- if (strictSelectorMatch)
+ if (issueDiagnostic) {
+ if (issueError)
+ Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R;
+ else if (strictSelectorMatch)
Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R;
else
Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R;
- Diag(MethList.Method->getLocStart(), diag::note_using)
+
+ Diag(MethList.Method->getLocStart(),
+ issueError ? diag::note_possibility : diag::note_using)
<< MethList.Method->getSourceRange();
for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next)
Diag(Next->Method->getLocStart(), diag::note_also_found)
@@ -1804,6 +2102,7 @@
DefaultSynthesizeProperties(S, IC, IDecl);
ImplMethodsVsClassMethods(S, IC, IDecl);
AtomicPropertySetterGetterRules(IC, IDecl);
+ DiagnoseOwningPropertyGetterSynthesis(IC);
if (LangOpts.ObjCNonFragileABI2)
while (IDecl->getSuperClass()) {
@@ -2112,7 +2411,11 @@
mergeObjCMethodDecls(ObjCMethod, InterfaceMD);
}
- if (!ObjCMethod->hasRelatedResultType() &&
+ bool ARCError = false;
+ if (getLangOptions().ObjCAutoRefCount)
+ ARCError = CheckARCMethodDecl(*this, ObjCMethod);
+
+ if (!ObjCMethod->hasRelatedResultType() && !ARCError &&
getLangOptions().ObjCInferRelatedResultType) {
bool InferRelatedResultType = false;
switch (ObjCMethod->getMethodFamily()) {
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index a2ad161..f0ecb58 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -87,7 +87,7 @@
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isDeleted()) {
Diag(Loc, diag::err_deleted_function_use);
- Diag(D->getLocation(), diag::note_unavailable_here) << true;
+ Diag(D->getLocation(), diag::note_unavailable_here) << 1 << true;
return true;
}
}
@@ -114,7 +114,8 @@
else
Diag(Loc, diag::err_unavailable_message)
<< D->getDeclName() << Message;
- Diag(D->getLocation(), diag::note_unavailable_here) << 0;
+ Diag(D->getLocation(), diag::note_unavailable_here)
+ << isa<FunctionDecl>(D) << false;
break;
}
@@ -437,7 +438,7 @@
/// will warn if the resulting type is not a POD type, and rejects ObjC
/// interfaces passed by value.
ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
- FunctionDecl *FDecl) {
+ FunctionDecl *FDecl) {
ExprResult ExprRes = DefaultArgumentPromotion(E);
if (ExprRes.isInvalid())
return ExprError();
@@ -456,7 +457,7 @@
<< E->getType() << CT))
return ExprError();
- if (!E->getType()->isPODType()) {
+ if (!E->getType().isPODType(Context)) {
// C++0x [expr.call]p7:
// Passing a potentially-evaluated argument of class type (Clause 9)
// having a non-trivial copy constructor, a non-trivial move constructor,
@@ -471,6 +472,11 @@
TrivialEnough = true;
}
}
+
+ if (!TrivialEnough &&
+ getLangOptions().ObjCAutoRefCount &&
+ E->getType()->isObjCLifetimeType())
+ TrivialEnough = true;
if (TrivialEnough) {
// Nothing to diagnose. This is okay.
@@ -1545,9 +1551,11 @@
// An id-expression that denotes a non-static data member or non-static
// member function of a class can only be used:
// (...)
- // - if that id-expression denotes a non-static data member and it appears in an unevaluated operand.
- const Sema::ExpressionEvaluationContextRecord& record = SemaRef.ExprEvalContexts.back();
- bool isUnevaluatedExpression = record.Context == Sema::Unevaluated;
+ // - if that id-expression denotes a non-static data member and it
+ // appears in an unevaluated operand.
+ const Sema::ExpressionEvaluationContextRecord& record
+ = SemaRef.ExprEvalContexts.back();
+ bool isUnevaluatedExpression = (record.Context == Sema::Unevaluated);
if (isUnevaluatedExpression)
return IMA_Mixed_StaticContext;
}
@@ -4241,6 +4249,9 @@
// - an interface
ObjCInterfaceDecl *IDecl = OTy->getInterface();
if (!IDecl) {
+ if (getLangOptions().ObjCAutoRefCount &&
+ (OTy->isObjCId() || OTy->isObjCClass()))
+ goto fail;
// There's an implicit 'isa' ivar on all objects.
// But we only actually find it this way on objects of type 'id',
// apparently.
@@ -4730,6 +4741,7 @@
MarkDeclarationReferenced(Param->getDefaultArg()->getLocStart(),
const_cast<CXXDestructorDecl*>(Temporary->getDestructor()));
ExprTemporaries.push_back(Temporary);
+ ExprNeedsCleanups = true;
}
// We already type-checked the argument, so we know it works.
@@ -4848,7 +4860,8 @@
InitializedEntity Entity =
Param? InitializedEntity::InitializeParameter(Context, Param)
- : InitializedEntity::InitializeParameter(Context, ProtoArgType);
+ : InitializedEntity::InitializeParameter(Context, ProtoArgType,
+ Proto->isArgConsumed(i));
ExprResult ArgE = PerformCopyInitialization(Entity,
SourceLocation(),
Owned(Arg));
@@ -5171,7 +5184,8 @@
if (Proto && i < Proto->getNumArgs()) {
InitializedEntity Entity
= InitializedEntity::InitializeParameter(Context,
- Proto->getArgType(i));
+ Proto->getArgType(i),
+ Proto->isArgConsumed(i));
ExprResult ArgE = PerformCopyInitialization(Entity,
SourceLocation(),
Owned(Arg));
@@ -5262,8 +5276,8 @@
InitializedEntity Entity
= InitializedEntity::InitializeTemporary(literalType);
InitializationKind Kind
- = InitializationKind::CreateCast(SourceRange(LParenLoc, RParenLoc),
- /*IsCStyleCast=*/true);
+ = InitializationKind::CreateCStyleCast(LParenLoc,
+ SourceRange(LParenLoc, RParenLoc));
InitializationSequence InitSeq(*this, Entity, Kind, &literalExpr, 1);
ExprResult Result = InitSeq.Perform(*this, Entity, Kind,
MultiExprArg(*this, &literalExpr, 1),
@@ -5440,14 +5454,15 @@
}
/// CheckCastTypes - Check type constraints for casting between types.
-ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType,
- Expr *castExpr, CastKind& Kind, ExprValueKind &VK,
+ExprResult Sema::CheckCastTypes(SourceLocation CastStartLoc, SourceRange TyR,
+ QualType castType, Expr *castExpr,
+ CastKind& Kind, ExprValueKind &VK,
CXXCastPath &BasePath, bool FunctionalStyle) {
if (castExpr->getType() == Context.UnknownAnyTy)
return checkUnknownAnyCast(TyR, castType, castExpr, Kind, VK, BasePath);
if (getLangOptions().CPlusPlus)
- return CXXCheckCStyleCast(SourceRange(TyR.getBegin(),
+ return CXXCheckCStyleCast(SourceRange(CastStartLoc,
castExpr->getLocEnd()),
castType, VK, castExpr, Kind, BasePath,
FunctionalStyle);
@@ -5565,8 +5580,8 @@
// If either type is a pointer, the other type has to be either an
// integer or a pointer.
+ QualType castExprType = castExpr->getType();
if (!castType->isArithmeticType()) {
- QualType castExprType = castExpr->getType();
if (!castExprType->isIntegralType(Context) &&
castExprType->isArithmeticType()) {
Diag(castExpr->getLocStart(),
@@ -5582,6 +5597,29 @@
}
}
+ if (getLangOptions().ObjCAutoRefCount) {
+ // Diagnose problems with Objective-C casts involving lifetime qualifiers.
+ CheckObjCARCConversion(SourceRange(CastStartLoc, castExpr->getLocEnd()),
+ castType, castExpr, CCK_CStyleCast);
+
+ if (const PointerType *CastPtr = castType->getAs<PointerType>()) {
+ if (const PointerType *ExprPtr = castExprType->getAs<PointerType>()) {
+ Qualifiers CastQuals = CastPtr->getPointeeType().getQualifiers();
+ Qualifiers ExprQuals = ExprPtr->getPointeeType().getQualifiers();
+ if (CastPtr->getPointeeType()->isObjCLifetimeType() &&
+ ExprPtr->getPointeeType()->isObjCLifetimeType() &&
+ !CastQuals.compatiblyIncludesObjCLifetime(ExprQuals)) {
+ Diag(castExpr->getLocStart(),
+ diag::err_typecheck_incompatible_lifetime)
+ << castExprType << castType << AA_Casting
+ << castExpr->getSourceRange();
+
+ return ExprError();
+ }
+ }
+ }
+ }
+
castExprRes = Owned(castExpr);
Kind = PrepareScalarCast(*this, castExprRes, castType);
if (castExprRes.isInvalid())
@@ -5677,8 +5715,8 @@
ExprValueKind VK = VK_RValue;
CXXCastPath BasePath;
ExprResult CastResult =
- CheckCastTypes(SourceRange(LParenLoc, RParenLoc), Ty->getType(), castExpr,
- Kind, VK, BasePath);
+ CheckCastTypes(LParenLoc, SourceRange(LParenLoc, RParenLoc), Ty->getType(),
+ castExpr, Kind, VK, BasePath);
if (CastResult.isInvalid())
return ExprError();
castExpr = CastResult.take();
@@ -6441,17 +6479,31 @@
// qualifiers of the type *pointed to* by the right;
Qualifiers lq;
+ // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay.
+ if (lhq.getObjCLifetime() != rhq.getObjCLifetime() &&
+ lhq.compatiblyIncludesObjCLifetime(rhq)) {
+ // Ignore lifetime for further calculation.
+ lhq.removeObjCLifetime();
+ rhq.removeObjCLifetime();
+ }
+
if (!lhq.compatiblyIncludes(rhq)) {
// Treat address-space mismatches as fatal. TODO: address subspaces
if (lhq.getAddressSpace() != rhq.getAddressSpace())
ConvTy = Sema::IncompatiblePointerDiscardsQualifiers;
- // It's okay to add or remove GC qualifiers when converting to
+ // It's okay to add or remove GC or lifetime qualifiers when converting to
// and from void*.
- else if (lhq.withoutObjCGCAttr().compatiblyIncludes(rhq.withoutObjCGCAttr())
+ else if (lhq.withoutObjCGCAttr().withoutObjCGLifetime()
+ .compatiblyIncludes(
+ rhq.withoutObjCGCAttr().withoutObjCGLifetime())
&& (lhptee->isVoidType() || rhptee->isVoidType()))
; // keep old
+ // Treat lifetime mismatches as fatal.
+ else if (lhq.getObjCLifetime() != rhq.getObjCLifetime())
+ ConvTy = Sema::IncompatiblePointerDiscardsQualifiers;
+
// For GCC compatibility, other qualifier mismatches are treated
// as still compatible in C.
else ConvTy = Sema::CompatiblePointerDiscardsQualifiers;
@@ -8118,7 +8170,40 @@
unsigned Diag = 0;
bool NeedType = false;
switch (IsLV) { // C99 6.5.16p2
- case Expr::MLV_ConstQualified: Diag = diag::err_typecheck_assign_const; break;
+ case Expr::MLV_ConstQualified:
+ Diag = diag::err_typecheck_assign_const;
+
+ // In ARC, use some specialized diagnostics for the times when we
+ // infer const.
+ 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()) {
+ ObjCMethodDecl *method = S.getCurMethodDecl();
+ if (method && var == method->getSelfDecl())
+ Diag = diag::err_typecheck_arr_assign_self;
+ else if (var->getType().getObjCLifetime()
+ == Qualifiers::OCL_ExplicitNone)
+ Diag = diag::err_typecheck_arr_assign_enumeration;
+ SourceRange Assign;
+ if (Loc != OrigLoc)
+ Assign = SourceRange(OrigLoc, OrigLoc);
+ S.Diag(Loc, Diag) << E->getSourceRange() << Assign;
+ // We need to preserve the AST regardless, so migration tool
+ // can do its job.
+ return false;
+ }
+ }
+ }
+
+ break;
case Expr::MLV_ArrayType:
Diag = diag::err_typecheck_array_not_modifiable_lvalue;
NeedType = true;
@@ -8233,6 +8318,13 @@
<< SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc());
}
}
+
+ if (ConvTy == Compatible) {
+ if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong)
+ checkRetainCycles(LHS, RHS.get());
+ else
+ checkUnsafeAssigns(Loc, LHSType, RHS.get());
+ }
} else {
// Compound assignment "x += y"
ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType);
@@ -8419,6 +8511,8 @@
LHS.get()->getObjectKind() == OK_ObjCProperty);
const ObjCPropertyRefExpr *PropRef = LHS.get()->getObjCProperty();
+ bool Consumed = false;
+
if (PropRef->isImplicitProperty()) {
// If using property-dot syntax notation for assignment, and there is a
// setter, RHS expression is being passed to the setter argument. So,
@@ -8426,6 +8520,8 @@
if (const ObjCMethodDecl *SetterMD = PropRef->getImplicitPropertySetter()) {
ObjCMethodDecl::param_iterator P = SetterMD->param_begin();
LHSTy = (*P)->getType();
+ Consumed = (getLangOptions().ObjCAutoRefCount &&
+ (*P)->hasAttr<NSConsumedAttr>());
// Otherwise, if the getter returns an l-value, just call that.
} else {
@@ -8437,14 +8533,26 @@
return;
}
}
+ } else if (getLangOptions().ObjCAutoRefCount) {
+ const ObjCMethodDecl *setter
+ = PropRef->getExplicitProperty()->getSetterMethodDecl();
+ if (setter) {
+ ObjCMethodDecl::param_iterator P = setter->param_begin();
+ LHSTy = (*P)->getType();
+ Consumed = (*P)->hasAttr<NSConsumedAttr>();
+ }
}
- if (getLangOptions().CPlusPlus && LHSTy->isRecordType()) {
+ if ((getLangOptions().CPlusPlus && LHSTy->isRecordType()) ||
+ getLangOptions().ObjCAutoRefCount) {
InitializedEntity Entity =
- InitializedEntity::InitializeParameter(Context, LHSTy);
+ InitializedEntity::InitializeParameter(Context, LHSTy, Consumed);
ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), RHS);
- if (!ArgE.isInvalid())
+ if (!ArgE.isInvalid()) {
RHS = ArgE;
+ if (getLangOptions().ObjCAutoRefCount && !PropRef->isSuperReceiver())
+ checkRetainCycles(const_cast<Expr*>(PropRef->getBase()), RHS.get());
+ }
}
}
@@ -9293,6 +9401,29 @@
Context.getPointerType(Context.VoidTy)));
}
+/// Given the last statement in a statement-expression, check whether
+/// the result is a producing expression (like a call to an
+/// ns_returns_retained function) and, if so, rebuild it to hoist the
+/// release out of the full-expression. Otherwise, return null.
+/// Cannot fail.
+static Expr *maybeRebuildARCConsumingStmt(Stmt *s) {
+ // Should always be wrapped with one of these.
+ ExprWithCleanups *cleanups = dyn_cast<ExprWithCleanups>(s);
+ if (!cleanups) return 0;
+
+ ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(cleanups->getSubExpr());
+ if (!cast || cast->getCastKind() != CK_ObjCConsumeObject)
+ return 0;
+
+ // Splice out the cast. This shouldn't modify any interesting
+ // features of the statement.
+ Expr *producer = cast->getSubExpr();
+ assert(producer->getType() == cast->getType());
+ assert(producer->getValueKind() == cast->getValueKind());
+ cleanups->setSubExpr(producer);
+ return cleanups;
+}
+
ExprResult
Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt,
SourceLocation RPLoc) { // "({..})"
@@ -9320,6 +9451,7 @@
LastLabelStmt = Label;
LastStmt = Label->getSubStmt();
}
+
if (Expr *LastE = dyn_cast<Expr>(LastStmt)) {
// Do function/array conversion on the last expression, but not
// lvalue-to-rvalue. However, initialize an unqualified type.
@@ -9329,12 +9461,24 @@
Ty = LastExpr.get()->getType().getUnqualifiedType();
if (!Ty->isDependentType() && !LastExpr.get()->isTypeDependent()) {
- LastExpr = PerformCopyInitialization(
+ // In ARC, if the final expression ends in a consume, splice
+ // the consume out and bind it later. In the alternate case
+ // (when dealing with a retainable type), the result
+ // initialization will create a produce. In both cases the
+ // result will be +1, and we'll need to balance that out with
+ // a bind.
+ if (Expr *rebuiltLastStmt
+ = maybeRebuildARCConsumingStmt(LastExpr.get())) {
+ LastExpr = rebuiltLastStmt;
+ } else {
+ LastExpr = PerformCopyInitialization(
InitializedEntity::InitializeResult(LPLoc,
Ty,
false),
SourceLocation(),
- LastExpr);
+ LastExpr);
+ }
+
if (LastExpr.isInvalid())
return ExprError();
if (LastExpr.get() != 0) {
@@ -9776,7 +9920,7 @@
// If we don't have a function type, just build one from nothing.
} else {
FunctionProtoType::ExtProtoInfo EPI;
- EPI.ExtInfo = FunctionType::ExtInfo(NoReturn, false, 0, CC_Default);
+ EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn);
BlockTy = Context.getFunctionType(RetTy, 0, 0, EPI);
}
@@ -9785,7 +9929,8 @@
BlockTy = Context.getBlockPointerType(BlockTy);
// If needed, diagnose invalid gotos and switches in the block.
- if (getCurFunction()->NeedsScopeChecking() && !hasAnyErrorsInThisFunction())
+ if (getCurFunction()->NeedsScopeChecking() &&
+ !hasAnyUnrecoverableErrorsInThisFunction())
DiagnoseInvalidJumps(cast<CompoundStmt>(Body));
BSI->TheDecl->setBody(cast<CompoundStmt>(Body));
@@ -9849,7 +9994,7 @@
<< TInfo->getTypeLoc().getSourceRange()))
return ExprError();
- if (!TInfo->getType()->isPODType())
+ if (!TInfo->getType().isPODType(Context))
Diag(TInfo->getTypeLoc().getBeginLoc(),
diag::warn_second_parameter_to_va_arg_not_pod)
<< TInfo->getType()
@@ -9948,6 +10093,11 @@
if (lhq.getAddressSpace() != rhq.getAddressSpace()) {
DiagKind = diag::err_typecheck_incompatible_address_space;
break;
+
+
+ } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) {
+ DiagKind = diag::err_typecheck_incompatible_lifetime;
+ break;
}
llvm_unreachable("unknown error case for discarding qualifiers!");
@@ -10062,7 +10212,10 @@
void
Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) {
ExprEvalContexts.push_back(
- ExpressionEvaluationContextRecord(NewContext, ExprTemporaries.size()));
+ ExpressionEvaluationContextRecord(NewContext,
+ ExprTemporaries.size(),
+ ExprNeedsCleanups));
+ ExprNeedsCleanups = false;
}
void
@@ -10097,15 +10250,27 @@
// temporaries that we may have created as part of the evaluation of
// the expression in that context: they aren't relevant because they
// will never be constructed.
- if (Rec.Context == Unevaluated &&
- ExprTemporaries.size() > Rec.NumTemporaries)
+ if (Rec.Context == Unevaluated) {
ExprTemporaries.erase(ExprTemporaries.begin() + Rec.NumTemporaries,
ExprTemporaries.end());
+ ExprNeedsCleanups = Rec.ParentNeedsCleanups;
+
+ // Otherwise, merge the contexts together.
+ } else {
+ ExprNeedsCleanups |= Rec.ParentNeedsCleanups;
+ }
// Destroy the popped expression evaluation record.
Rec.Destroy();
}
+void Sema::DiscardCleanupsInEvaluationContext() {
+ ExprTemporaries.erase(
+ ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries,
+ ExprTemporaries.end());
+ ExprNeedsCleanups = false;
+}
+
/// \brief Note that the given declaration was referenced in the source code.
///
/// This routine should be invoke whenever a given declaration is referenced
@@ -10399,7 +10564,7 @@
/// during overload resolution or within sizeof/alignof/typeof/typeid.
bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *stmt,
const PartialDiagnostic &PD) {
- switch (ExprEvalContexts.back().Context ) {
+ switch (ExprEvalContexts.back().Context) {
case Unevaluated:
// The argument will never be evaluated, so don't complain.
break;
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 2f5a890..c796f12 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -691,7 +691,8 @@
ExprValueKind VK = VK_RValue;
CXXCastPath BasePath;
ExprResult CastExpr =
- CheckCastTypes(TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0],
+ CheckCastTypes(TInfo->getTypeLoc().getBeginLoc(),
+ TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0],
Kind, VK, BasePath,
/*FunctionalStyle=*/true);
if (CastExpr.isInvalid())
@@ -902,8 +903,16 @@
if (CheckAllocatedType(AllocType, TypeRange.getBegin(), TypeRange))
return ExprError();
- QualType ResultType = Context.getPointerType(AllocType);
+ // In ARC, infer 'retaining' for the allocated
+ if (getLangOptions().ObjCAutoRefCount &&
+ AllocType.getObjCLifetime() == Qualifiers::OCL_None &&
+ AllocType->isObjCLifetimeType()) {
+ AllocType = Context.getLifetimeQualifiedType(AllocType,
+ AllocType->getObjCARCImplicitLifetime());
+ }
+ QualType ResultType = Context.getPointerType(AllocType);
+
// C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral
// or enumeration type with a non-negative value."
if (ArraySize && !ArraySize->isTypeDependent()) {
@@ -964,6 +973,14 @@
}
}
+ // ARC: warn about ABI issues.
+ if (getLangOptions().ObjCAutoRefCount) {
+ QualType BaseAllocType = Context.getBaseElementType(AllocType);
+ if (BaseAllocType.hasStrongOrWeakObjCLifetime())
+ Diag(StartLoc, diag::warn_err_new_delete_object_array)
+ << 0 << BaseAllocType;
+ }
+
// Note that we do *not* convert the argument in any way. It can
// be signed, larger than size_t, whatever.
}
@@ -1122,6 +1139,15 @@
else if (unsigned AddressSpace = AllocType.getAddressSpace())
return Diag(Loc, diag::err_address_space_qualified_new)
<< AllocType.getUnqualifiedType() << AddressSpace;
+ else if (getLangOptions().ObjCAutoRefCount) {
+ if (const ArrayType *AT = Context.getAsArrayType(AllocType)) {
+ QualType BaseAllocType = Context.getBaseElementType(AT);
+ if (BaseAllocType.getObjCLifetime() == Qualifiers::OCL_None &&
+ BaseAllocType->isObjCLifetimeType())
+ return Diag(Loc, diag::err_arc_new_array_without_lifetime)
+ << BaseAllocType;
+ }
+ }
return false;
}
@@ -1774,8 +1800,9 @@
// delete-expression; it is not necessary to cast away the constness
// (5.2.11) of the pointer expression before it is used as the operand
// of the delete-expression. ]
- Ex = ImpCastExprToType(Ex.take(), Context.getPointerType(Context.VoidTy),
- CK_NoOp);
+ if (!Context.hasSameType(Ex.get()->getType(), Context.VoidPtrTy))
+ Ex = Owned(ImplicitCastExpr::Create(Context, Context.VoidPtrTy, CK_NoOp,
+ Ex.take(), 0, VK_RValue));
if (Pointee->isArrayType() && !ArrayForm) {
Diag(StartLoc, diag::warn_delete_array_type)
@@ -1830,6 +1857,14 @@
if (!dtor || !dtor->isVirtual())
Diag(StartLoc, diag::warn_delete_non_virtual_dtor) << PointeeElem;
}
+
+ } else if (getLangOptions().ObjCAutoRefCount &&
+ PointeeElem->isObjCLifetimeType() &&
+ (PointeeElem.getObjCLifetime() == Qualifiers::OCL_Strong ||
+ PointeeElem.getObjCLifetime() == Qualifiers::OCL_Weak) &&
+ ArrayForm) {
+ Diag(StartLoc, diag::warn_err_new_delete_object_array)
+ << 1 << PointeeElem;
}
if (!OperatorDelete) {
@@ -1988,11 +2023,12 @@
ExprResult
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
const ImplicitConversionSequence &ICS,
- AssignmentAction Action, bool CStyle) {
+ AssignmentAction Action,
+ CheckedConversionKind CCK) {
switch (ICS.getKind()) {
case ImplicitConversionSequence::StandardConversion: {
ExprResult Res = PerformImplicitConversion(From, ToType, ICS.Standard,
- Action, CStyle);
+ Action, CCK);
if (Res.isInvalid())
return ExprError();
From = Res.take();
@@ -2027,7 +2063,7 @@
ExprResult Res =
PerformImplicitConversion(From, BeforeToType,
ICS.UserDefined.Before, AA_Converting,
- CStyle);
+ CCK);
if (Res.isInvalid())
return ExprError();
From = Res.take();
@@ -2047,7 +2083,7 @@
From = CastArg.take();
return PerformImplicitConversion(From, ToType, ICS.UserDefined.After,
- AA_Converting, CStyle);
+ AA_Converting, CCK);
}
case ImplicitConversionSequence::AmbiguousConversion:
@@ -2076,13 +2112,16 @@
ExprResult
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
const StandardConversionSequence& SCS,
- AssignmentAction Action, bool CStyle) {
+ AssignmentAction Action,
+ CheckedConversionKind CCK) {
+ bool CStyle = (CCK == CCK_CStyleCast || CCK == CCK_FunctionalCast);
+
// Overall FIXME: we are recomputing too many types here and doing far too
// much extra work. What this means is that we need to keep track of more
// information that is computed when we try the implicit conversion initially,
// so that we don't need to recompute anything here.
QualType FromType = From->getType();
-
+
if (SCS.CopyConstructor) {
// FIXME: When can ToType be a reference type?
assert(!ToType->isReferenceType());
@@ -2149,12 +2188,14 @@
case ICK_Array_To_Pointer:
FromType = Context.getArrayDecayedType(FromType);
- From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay).take();
+ From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Function_To_Pointer:
FromType = Context.getPointerType(FromType);
- From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay).take();
+ From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
default:
@@ -2178,17 +2219,20 @@
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
- From = ImpCastExprToType(From, ToType, CK_NoOp).take();
+ From = ImpCastExprToType(From, ToType, CK_NoOp,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Integral_Promotion:
case ICK_Integral_Conversion:
- From = ImpCastExprToType(From, ToType, CK_IntegralCast).take();
+ From = ImpCastExprToType(From, ToType, CK_IntegralCast,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Floating_Promotion:
case ICK_Floating_Conversion:
- From = ImpCastExprToType(From, ToType, CK_FloatingCast).take();
+ From = ImpCastExprToType(From, ToType, CK_FloatingCast,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Complex_Promotion:
@@ -2206,21 +2250,26 @@
} else {
CK = CK_IntegralComplexCast;
}
- From = ImpCastExprToType(From, ToType, CK).take();
+ From = ImpCastExprToType(From, ToType, CK,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
}
case ICK_Floating_Integral:
if (ToType->isRealFloatingType())
- From = ImpCastExprToType(From, ToType, CK_IntegralToFloating).take();
+ From = ImpCastExprToType(From, ToType, CK_IntegralToFloating,
+ VK_RValue, /*BasePath=*/0, CCK).take();
else
- From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral).take();
+ From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Compatible_Conversion:
- From = ImpCastExprToType(From, ToType, CK_NoOp).take();
+ From = ImpCastExprToType(From, ToType, CK_NoOp,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
+ case ICK_Writeback_Conversion:
case ICK_Pointer_Conversion: {
if (SCS.IncompatibleObjC && Action != AA_Casting) {
// Diagnose incompatible Objective-C conversions
@@ -2234,7 +2283,7 @@
diag::ext_typecheck_convert_incompatible_pointer)
<< From->getType() << ToType << Action
<< From->getSourceRange();
-
+
if (From->getType()->isObjCObjectPointerType() &&
ToType->isObjCObjectPointerType())
EmitRelatedResultTypeNote(From);
@@ -2244,7 +2293,8 @@
CXXCastPath BasePath;
if (CheckPointerConversion(From, ToType, Kind, BasePath, CStyle))
return ExprError();
- From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take();
+ From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK)
+ .take();
break;
}
@@ -2255,13 +2305,15 @@
return ExprError();
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
- From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take();
+ From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK)
+ .take();
break;
}
case ICK_Boolean_Conversion:
From = ImpCastExprToType(From, Context.BoolTy,
- ScalarTypeToBooleanCastKind(FromType)).take();
+ ScalarTypeToBooleanCastKind(FromType),
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Derived_To_Base: {
@@ -2276,16 +2328,18 @@
From = ImpCastExprToType(From, ToType.getNonReferenceType(),
CK_DerivedToBase, CastCategory(From),
- &BasePath).take();
+ &BasePath, CCK).take();
break;
}
case ICK_Vector_Conversion:
- From = ImpCastExprToType(From, ToType, CK_BitCast).take();
+ From = ImpCastExprToType(From, ToType, CK_BitCast,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Vector_Splat:
- From = ImpCastExprToType(From, ToType, CK_VectorSplat).take();
+ From = ImpCastExprToType(From, ToType, CK_VectorSplat,
+ VK_RValue, /*BasePath=*/0, CCK).take();
break;
case ICK_Complex_Real:
@@ -2321,27 +2375,30 @@
// _Complex x -> x
From = ImpCastExprToType(From, ElType,
isFloatingComplex ? CK_FloatingComplexToReal
- : CK_IntegralComplexToReal).take();
+ : CK_IntegralComplexToReal,
+ VK_RValue, /*BasePath=*/0, CCK).take();
// x -> y
if (Context.hasSameUnqualifiedType(ElType, ToType)) {
// do nothing
} else if (ToType->isRealFloatingType()) {
From = ImpCastExprToType(From, ToType,
- isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating).take();
+ isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating,
+ VK_RValue, /*BasePath=*/0, CCK).take();
} else {
assert(ToType->isIntegerType());
From = ImpCastExprToType(From, ToType,
- isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast).take();
+ isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast,
+ VK_RValue, /*BasePath=*/0, CCK).take();
}
}
break;
case ICK_Block_Pointer_Conversion: {
- From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast,
- VK_RValue).take();
- break;
- }
+ From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast,
+ VK_RValue, /*BasePath=*/0, CCK).take();
+ break;
+ }
case ICK_TransparentUnionConversion: {
ExprResult FromRes = Owned(From);
@@ -2376,7 +2433,7 @@
ExprValueKind VK = ToType->isReferenceType() ?
CastCategory(From) : VK_RValue;
From = ImpCastExprToType(From, ToType.getNonLValueExprType(Context),
- CK_NoOp, VK).take();
+ CK_NoOp, VK, /*BasePath=*/0, CCK).take();
if (SCS.DeprecatedStringLiteralToCharPtr &&
!getLangOptions().WritableStrings)
@@ -2553,6 +2610,23 @@
case UTT_IsObject:
return T->isObjectType();
case UTT_IsScalar:
+ // Note: semantic analysis depends on Objective-C lifetime types to be
+ // considered scalar types. However, such types do not actually behave
+ // like scalar types at run time (since they may require retain/release
+ // operations), so we report them as non-scalar.
+ if (T->isObjCLifetimeType()) {
+ switch (T.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+ }
+ }
+
return T->isScalarType();
case UTT_IsCompound:
return T->isCompoundType();
@@ -2566,13 +2640,13 @@
case UTT_IsVolatile:
return T.isVolatileQualified();
case UTT_IsTrivial:
- return T->isTrivialType();
+ return T.isTrivialType(Self.Context);
case UTT_IsTriviallyCopyable:
- return T->isTriviallyCopyableType();
+ return T.isTriviallyCopyableType(Self.Context);
case UTT_IsStandardLayout:
return T->isStandardLayoutType();
case UTT_IsPOD:
- return T->isPODType();
+ return T.isPODType(Self.Context);
case UTT_IsLiteral:
return T->isLiteralType();
case UTT_IsEmpty:
@@ -2605,7 +2679,7 @@
// If __is_pod (type) is true then the trait is true, else if type is
// a cv class or union type (or array thereof) with a trivial default
// constructor ([class.ctor]) then the trait is true, else it is false.
- if (T->isPODType())
+ if (T.isPODType(Self.Context))
return true;
if (const RecordType *RT =
C.getBaseElementType(T)->getAs<RecordType>())
@@ -2617,7 +2691,7 @@
// the trait is true, else if type is a cv class or union type
// with a trivial copy constructor ([class.copy]) then the trait
// is true, else it is false.
- if (T->isPODType() || T->isReferenceType())
+ if (T.isPODType(Self.Context) || T->isReferenceType())
return true;
if (const RecordType *RT = T->getAs<RecordType>())
return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyConstructor();
@@ -2637,7 +2711,7 @@
if (C.getBaseElementType(T).isConstQualified())
return false;
- if (T->isPODType())
+ if (T.isPODType(Self.Context))
return true;
if (const RecordType *RT = T->getAs<RecordType>())
return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyAssignment();
@@ -2649,8 +2723,14 @@
// type (or array thereof) with a trivial destructor
// ([class.dtor]) then the trait is true, else it is
// false.
- if (T->isPODType() || T->isReferenceType())
+ if (T.isPODType(Self.Context) || T->isReferenceType())
return true;
+
+ // Objective-C++ ARC: autorelease types don't require destruction.
+ if (T->isObjCLifetimeType() &&
+ T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
+ return true;
+
if (const RecordType *RT =
C.getBaseElementType(T)->getAs<RecordType>())
return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor();
@@ -2668,8 +2748,8 @@
return false;
if (T->isReferenceType())
return false;
- if (T->isPODType())
- return true;
+ if (T.isPODType(Self.Context) || T->isObjCLifetimeType())
+ return true;
if (const RecordType *RT = T->getAs<RecordType>()) {
CXXRecordDecl* RD = cast<CXXRecordDecl>(RT->getDecl());
if (RD->hasTrivialCopyAssignment())
@@ -2704,7 +2784,7 @@
// if type is a cv class or union type with copy constructors that are
// known not to throw an exception then the trait is true, else it is
// false.
- if (T->isPODType() || T->isReferenceType())
+ if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType())
return true;
if (const RecordType *RT = T->getAs<RecordType>()) {
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
@@ -2744,7 +2824,7 @@
// true, else if type is a cv class or union type (or array
// thereof) with a default constructor that is known not to
// throw an exception then the trait is true, else it is false.
- if (T->isPODType())
+ if (T.isPODType(C) || T->isObjCLifetimeType())
return true;
if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) {
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
@@ -3828,34 +3908,104 @@
if (!E)
return ExprError();
- if (!Context.getLangOptions().CPlusPlus)
+ assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?");
+
+ // If the result is a glvalue, we shouldn't bind it.
+ if (!E->isRValue())
return Owned(E);
- assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?");
+ // In ARC, calls that return a retainable type can return retained,
+ // in which case we have to insert a consuming cast.
+ if (getLangOptions().ObjCAutoRefCount &&
+ E->getType()->isObjCRetainableType()) {
+
+ bool ReturnsRetained;
+
+ // For actual calls, we compute this by examining the type of the
+ // called value.
+ if (CallExpr *Call = dyn_cast<CallExpr>(E)) {
+ Expr *Callee = Call->getCallee()->IgnoreParens();
+ QualType T = Callee->getType();
+
+ if (T == Context.BoundMemberTy) {
+ // Handle pointer-to-members.
+ if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Callee))
+ T = BinOp->getRHS()->getType();
+ else if (MemberExpr *Mem = dyn_cast<MemberExpr>(Callee))
+ T = Mem->getMemberDecl()->getType();
+ }
+
+ if (const PointerType *Ptr = T->getAs<PointerType>())
+ T = Ptr->getPointeeType();
+ else if (const BlockPointerType *Ptr = T->getAs<BlockPointerType>())
+ T = Ptr->getPointeeType();
+ else if (const MemberPointerType *MemPtr = T->getAs<MemberPointerType>())
+ T = MemPtr->getPointeeType();
+
+ const FunctionType *FTy = T->getAs<FunctionType>();
+ assert(FTy && "call to value not of function type?");
+ ReturnsRetained = FTy->getExtInfo().getProducesResult();
+
+ // ActOnStmtExpr arranges things so that StmtExprs of retainable
+ // type always produce a +1 object.
+ } else if (isa<StmtExpr>(E)) {
+ ReturnsRetained = true;
+
+ // For message sends and property references, we try to find an
+ // actual method. FIXME: we should infer retention by selector in
+ // cases where we don't have an actual method.
+ } else {
+ Decl *D = 0;
+ if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
+ D = Send->getMethodDecl();
+ } else {
+ CastExpr *CE = cast<CastExpr>(E);
+ // FIXME. What other cast kinds to check for?
+ if (CE->getCastKind() == CK_ObjCProduceObject ||
+ CE->getCastKind() == CK_LValueToRValue)
+ return MaybeBindToTemporary(CE->getSubExpr());
+ assert(CE->getCastKind() == CK_GetObjCProperty);
+ const ObjCPropertyRefExpr *PRE = CE->getSubExpr()->getObjCProperty();
+ D = (PRE->isImplicitProperty() ? PRE->getImplicitPropertyGetter() : 0);
+ }
+
+ ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
+ }
+
+ if (ReturnsRetained) {
+ ExprNeedsCleanups = true;
+ E = ImplicitCastExpr::Create(Context, E->getType(),
+ CK_ObjCConsumeObject, E, 0,
+ VK_RValue);
+ }
+ return Owned(E);
+ }
+
+ if (!getLangOptions().CPlusPlus)
+ return Owned(E);
const RecordType *RT = E->getType()->getAs<RecordType>();
if (!RT)
return Owned(E);
- // If the result is a glvalue, we shouldn't bind it.
- if (E->Classify(Context).isGLValue())
- return Owned(E);
-
// That should be enough to guarantee that this type is complete.
// If it has a trivial destructor, we can avoid the extra copy.
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if (RD->isInvalidDecl() || RD->hasTrivialDestructor())
return Owned(E);
- CXXTemporary *Temp = CXXTemporary::Create(Context, LookupDestructor(RD));
- ExprTemporaries.push_back(Temp);
- if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) {
+ CXXDestructorDecl *Destructor = LookupDestructor(RD);
+
+ CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor);
+ if (Destructor) {
MarkDeclarationReferenced(E->getExprLoc(), Destructor);
CheckDestructorAccess(E->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_temp)
<< E->getType());
+
+ ExprTemporaries.push_back(Temp);
+ ExprNeedsCleanups = true;
}
- // FIXME: Add the temporary to the temporaries vector.
return Owned(CXXBindTemporaryExpr::Create(Context, Temp, E));
}
@@ -3864,14 +4014,16 @@
unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries;
assert(ExprTemporaries.size() >= FirstTemporary);
- if (ExprTemporaries.size() == FirstTemporary)
+ assert(ExprNeedsCleanups || ExprTemporaries.size() == FirstTemporary);
+ if (!ExprNeedsCleanups)
return SubExpr;
Expr *E = ExprWithCleanups::Create(Context, SubExpr,
- &ExprTemporaries[FirstTemporary],
+ ExprTemporaries.begin() + FirstTemporary,
ExprTemporaries.size() - FirstTemporary);
ExprTemporaries.erase(ExprTemporaries.begin() + FirstTemporary,
ExprTemporaries.end());
+ ExprNeedsCleanups = false;
return E;
}
@@ -3887,9 +4039,7 @@
Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) {
assert(SubStmt && "sub statement can't be null!");
- unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries;
- assert(ExprTemporaries.size() >= FirstTemporary);
- if (ExprTemporaries.size() == FirstTemporary)
+ if (!ExprNeedsCleanups)
return SubStmt;
// FIXME: In order to attach the temporaries, wrap the statement into
@@ -4047,17 +4197,35 @@
QualType DestructedType = DestructedTypeInfo->getType();
SourceLocation DestructedTypeStart
= DestructedTypeInfo->getTypeLoc().getLocalSourceRange().getBegin();
- if (!DestructedType->isDependentType() && !ObjectType->isDependentType() &&
- !Context.hasSameUnqualifiedType(DestructedType, ObjectType)) {
- Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch)
- << ObjectType << DestructedType << Base->getSourceRange()
- << DestructedTypeInfo->getTypeLoc().getLocalSourceRange();
+ if (!DestructedType->isDependentType() && !ObjectType->isDependentType()) {
+ if (!Context.hasSameUnqualifiedType(DestructedType, ObjectType)) {
+ Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch)
+ << ObjectType << DestructedType << Base->getSourceRange()
+ << DestructedTypeInfo->getTypeLoc().getLocalSourceRange();
- // Recover by setting the destructed type to the object type.
- DestructedType = ObjectType;
- DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType,
+ // Recover by setting the destructed type to the object type.
+ DestructedType = ObjectType;
+ DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType,
DestructedTypeStart);
- Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
+ Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
+ } else if (DestructedType.getObjCLifetime() !=
+ ObjectType.getObjCLifetime()) {
+
+ if (DestructedType.getObjCLifetime() == Qualifiers::OCL_None) {
+ // Okay: just pretend that the user provided the correctly-qualified
+ // type.
+ } else {
+ Diag(DestructedTypeStart, diag::err_arc_pseudo_dtor_inconstant_quals)
+ << ObjectType << DestructedType << Base->getSourceRange()
+ << DestructedTypeInfo->getTypeLoc().getLocalSourceRange();
+ }
+
+ // Recover by setting the destructed type to the object type.
+ DestructedType = ObjectType;
+ DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType,
+ DestructedTypeStart);
+ Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
+ }
}
}
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index cb5c1e0..1d1f6ae 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "llvm/ADT/SmallString.h"
#include "clang/Lex/Preprocessor.h"
@@ -182,6 +183,29 @@
if (Pos == ReferencedSelectors.end())
ReferencedSelectors.insert(std::make_pair(Sel, SelLoc));
+ // In ARC, forbid the user from using @selector for
+ // retain/release/autorelease/dealloc/retainCount.
+ if (getLangOptions().ObjCAutoRefCount) {
+ switch (Sel.getMethodFamily()) {
+ case OMF_retain:
+ case OMF_release:
+ case OMF_autorelease:
+ case OMF_retainCount:
+ case OMF_dealloc:
+ Diag(AtLoc, diag::err_arc_illegal_selector) <<
+ Sel << SourceRange(LParenLoc, RParenLoc);
+ break;
+
+ case OMF_None:
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_init:
+ case OMF_mutableCopy:
+ case OMF_new:
+ case OMF_self:
+ break;
+ }
+ }
QualType Ty = Context.getObjCSelType();
return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc);
}
@@ -321,8 +345,12 @@
Args[i] = Result.take();
}
- unsigned DiagID = isClassMessage ? diag::warn_class_method_not_found :
- diag::warn_inst_method_not_found;
+ unsigned DiagID;
+ if (getLangOptions().ObjCAutoRefCount)
+ DiagID = diag::err_arc_method_not_found;
+ else
+ DiagID = isClassMessage ? diag::warn_class_method_not_found
+ : diag::warn_inst_method_not_found;
Diag(lbrac, DiagID)
<< Sel << isClassMessage << SourceRange(lbrac, rbrac);
ReturnType = Context.getObjCIdType();
@@ -404,17 +432,15 @@
return IsError;
}
-bool Sema::isSelfExpr(Expr *RExpr) {
+bool Sema::isSelfExpr(Expr *receiver) {
// 'self' is objc 'self' in an objc method only.
DeclContext *DC = CurContext;
while (isa<BlockDecl>(DC))
DC = DC->getParent();
if (DC && !isa<ObjCMethodDecl>(DC))
return false;
- if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RExpr))
- if (ICE->getCastKind() == CK_LValueToRValue)
- RExpr = ICE->getSubExpr();
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RExpr))
+ receiver = receiver->IgnoreParenLValueCasts();
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver))
if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self"))
return true;
return false;
@@ -1013,11 +1039,16 @@
// Find the method we are messaging.
if (!Method) {
if (Class->isForwardDecl()) {
+ if (getLangOptions().ObjCAutoRefCount) {
+ Diag(Loc, diag::err_arc_receiver_forward_class) << ReceiverType;
+ } else {
+ Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName();
+ }
+
// A forward class used in messaging is treated as a 'Class'
- Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName();
Method = LookupFactoryMethodInGlobalPool(Sel,
SourceRange(LBracLoc, RBracLoc));
- if (Method)
+ if (Method && !getLangOptions().ObjCAutoRefCount)
Diag(Method->getLocation(), diag::note_method_sent_forward_class)
<< Method->getDeclName();
}
@@ -1236,6 +1267,14 @@
= ReceiverType->getAsObjCInterfacePointerType()) {
// We allow sending a message to a pointer to an interface (an object).
ClassDecl = OCIType->getInterfaceDecl();
+
+ if (ClassDecl->isForwardDecl() && getLangOptions().ObjCAutoRefCount) {
+ Diag(Loc, diag::err_arc_receiver_forward_instance)
+ << OCIType->getPointeeType()
+ << (Receiver ? Receiver->getSourceRange() : SourceRange(SuperLoc));
+ return ExprError();
+ }
+
// FIXME: consider using LookupInstanceMethodInGlobalPool, since it will be
// faster than the following method (which can do *many* linear searches).
// The idea is to add class info to MethodPool.
@@ -1250,6 +1289,12 @@
// If we have implementations in scope, check "private" methods.
Method = LookupPrivateInstanceMethod(Sel, ClassDecl);
+ if (!Method && getLangOptions().ObjCAutoRefCount) {
+ Diag(Loc, diag::err_arc_may_not_respond)
+ << OCIType->getPointeeType() << Sel;
+ return ExprError();
+ }
+
if (!Method && (!Receiver || !isSelfExpr(Receiver))) {
// If we still haven't found a method, look in the global pool. This
// behavior isn't very desirable, however we need it for GCC
@@ -1267,10 +1312,12 @@
}
if (Method && DiagnoseUseOfDecl(Method, Loc, forwardClass))
return ExprError();
- } else if (!Context.getObjCIdType().isNull() &&
+ } else if (!getLangOptions().ObjCAutoRefCount &&
+ !Context.getObjCIdType().isNull() &&
(ReceiverType->isPointerType() ||
ReceiverType->isIntegerType())) {
// Implicitly convert integers and pointers to 'id' but emit a warning.
+ // But not in ARC.
Diag(Loc, diag::warn_bad_receiver_type)
<< ReceiverType
<< Receiver->getSourceRange();
@@ -1332,8 +1379,37 @@
diag::err_illegal_message_expr_incomplete_type))
return ExprError();
+ // In ARC, forbid the user from sending messages to
+ // retain/release/autorelease/dealloc/retainCount explicitly.
+ if (getLangOptions().ObjCAutoRefCount) {
+ ObjCMethodFamily family =
+ (Method ? Method->getMethodFamily() : Sel.getMethodFamily());
+ switch (family) {
+ case OMF_init:
+ if (Method)
+ checkInitMethod(Method, ReceiverType);
+
+ case OMF_None:
+ case OMF_alloc:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ case OMF_new:
+ case OMF_self:
+ break;
+
+ case OMF_dealloc:
+ case OMF_retain:
+ case OMF_release:
+ case OMF_autorelease:
+ case OMF_retainCount:
+ Diag(Loc, diag::err_arc_illegal_explicit_message)
+ << Sel << SelectorLoc;
+ break;
+ }
+ }
+
// Construct the appropriate ObjCMessageExpr instance.
- Expr *Result;
+ ObjCMessageExpr *Result;
if (SuperLoc.isValid())
Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc,
SuperLoc, /*IsInstanceSuper=*/true,
@@ -1343,6 +1419,27 @@
Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc,
Receiver, Sel, SelectorLoc, Method,
Args, NumArgs, RBracLoc);
+
+ if (getLangOptions().ObjCAutoRefCount) {
+ // In ARC, annotate delegate init calls.
+ if (Result->getMethodFamily() == OMF_init &&
+ (SuperLoc.isValid() || isSelfExpr(Receiver))) {
+ // Only consider init calls *directly* in init implementations,
+ // not within blocks.
+ ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CurContext);
+ if (method && method->getMethodFamily() == OMF_init) {
+ // The implicit assignment to self means we also don't want to
+ // consume the result.
+ Result->setDelegateInitCall(true);
+ return Owned(Result);
+ }
+ }
+
+ // In ARC, check for message sends which are likely to introduce
+ // retain cycles.
+ checkRetainCycles(Result);
+ }
+
return MaybeBindToTemporary(Result);
}
@@ -1364,3 +1461,293 @@
LBracLoc, SelectorLoc, RBracLoc, move(Args));
}
+enum ARCConversionTypeClass {
+ ACTC_none,
+ ACTC_retainable,
+ ACTC_indirectRetainable
+};
+static ARCConversionTypeClass classifyTypeForARCConversion(QualType type) {
+ ARCConversionTypeClass ACTC = ACTC_retainable;
+
+ // Ignore an outermost reference type.
+ if (const ReferenceType *ref = type->getAs<ReferenceType>())
+ type = ref->getPointeeType();
+
+ // Drill through pointers and arrays recursively.
+ while (true) {
+ if (const PointerType *ptr = type->getAs<PointerType>()) {
+ type = ptr->getPointeeType();
+ } else if (const ArrayType *array = type->getAsArrayTypeUnsafe()) {
+ type = QualType(array->getElementType()->getBaseElementTypeUnsafe(), 0);
+ } else {
+ break;
+ }
+ ACTC = ACTC_indirectRetainable;
+ }
+
+ if (!type->isObjCRetainableType()) return ACTC_none;
+ return ACTC;
+}
+
+namespace {
+ /// Return true if the given expression can be reasonably converted
+ /// between a retainable pointer type and a C pointer type.
+ struct ARCCastChecker : StmtVisitor<ARCCastChecker, bool> {
+ ASTContext &Context;
+ ARCCastChecker(ASTContext &Context) : Context(Context) {}
+ bool VisitStmt(Stmt *s) {
+ return false;
+ }
+ bool VisitExpr(Expr *e) {
+ return e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull);
+ }
+
+ bool VisitParenExpr(ParenExpr *e) {
+ return Visit(e->getSubExpr());
+ }
+ bool VisitCastExpr(CastExpr *e) {
+ switch (e->getCastKind()) {
+ case CK_NullToPointer:
+ return true;
+ case CK_NoOp:
+ case CK_LValueToRValue:
+ case CK_BitCast:
+ case CK_AnyPointerToObjCPointerCast:
+ case CK_AnyPointerToBlockPointerCast:
+ return Visit(e->getSubExpr());
+ default:
+ return false;
+ }
+ }
+ bool VisitUnaryExtension(UnaryOperator *e) {
+ return Visit(e->getSubExpr());
+ }
+ bool VisitBinComma(BinaryOperator *e) {
+ return Visit(e->getRHS());
+ }
+ bool VisitConditionalOperator(ConditionalOperator *e) {
+ // Conditional operators are okay if both sides are okay.
+ return Visit(e->getTrueExpr()) && Visit(e->getFalseExpr());
+ }
+ bool VisitObjCStringLiteral(ObjCStringLiteral *e) {
+ // Always white-list Objective-C string literals.
+ return true;
+ }
+ bool VisitStmtExpr(StmtExpr *e) {
+ return Visit(e->getSubStmt()->body_back());
+ }
+ bool VisitDeclRefExpr(DeclRefExpr *e) {
+ // White-list references to global extern strings from system
+ // headers.
+ if (VarDecl *var = dyn_cast<VarDecl>(e->getDecl()))
+ if (var->getStorageClass() == SC_Extern &&
+ var->getType().isConstQualified() &&
+ Context.getSourceManager().isInSystemHeader(var->getLocation()))
+ return true;
+ return false;
+ }
+ };
+}
+
+void
+Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType,
+ Expr *castExpr, CheckedConversionKind CCK) {
+ QualType castExprType = castExpr->getType();
+
+ ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType);
+ ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType);
+ if (exprACTC == castACTC) return;
+ if (exprACTC && castType->isBooleanType()) return;
+
+ // Allow casts between pointers to lifetime types (e.g., __strong id*)
+ // and pointers to void (e.g., cv void *). Casting from void* to lifetime*
+ // must be explicit.
+ if (const PointerType *CastPtr = castType->getAs<PointerType>()) {
+ if (const PointerType *CastExprPtr = castExprType->getAs<PointerType>()) {
+ QualType CastPointee = CastPtr->getPointeeType();
+ QualType CastExprPointee = CastExprPtr->getPointeeType();
+ if ((CCK != CCK_ImplicitConversion &&
+ CastPointee->isObjCIndirectLifetimeType() &&
+ CastExprPointee->isVoidType()) ||
+ (CastPointee->isVoidType() &&
+ CastExprPointee->isObjCIndirectLifetimeType()))
+ return;
+ }
+ }
+
+ if (ARCCastChecker(Context).Visit(castExpr))
+ return;
+
+ SourceLocation loc =
+ (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc());
+
+ if (makeUnavailableInSystemHeader(loc,
+ "converts between Objective-C and C pointers in -fobjc-arc"))
+ return;
+
+ unsigned srcKind;
+ switch (exprACTC) {
+ case ACTC_none:
+ srcKind = (castExprType->isPointerType() ? 1 : 0);
+ break;
+ case ACTC_retainable:
+ srcKind = (castExprType->isBlockPointerType() ? 2 : 3);
+ break;
+ case ACTC_indirectRetainable:
+ srcKind = 4;
+ break;
+ }
+
+ if (CCK == CCK_CStyleCast) {
+ // Check whether this could be fixed with a bridge cast.
+ SourceLocation AfterLParen = PP.getLocForEndOfToken(castRange.getBegin());
+ SourceLocation NoteLoc = AfterLParen.isValid()? AfterLParen : loc;
+
+ if (castType->isObjCARCBridgableType() &&
+ castExprType->isCARCBridgableType()) {
+ Diag(loc, diag::err_arc_cast_requires_bridge)
+ << 2
+ << castExprType
+ << (castType->isBlockPointerType()? 1 : 0)
+ << castType
+ << castRange
+ << castExpr->getSourceRange();
+ Diag(NoteLoc, diag::note_arc_bridge)
+ << FixItHint::CreateInsertion(AfterLParen, "__bridge ");
+ Diag(NoteLoc, diag::note_arc_bridge_transfer)
+ << castExprType
+ << FixItHint::CreateInsertion(AfterLParen, "__bridge_transfer ");
+
+ return;
+ }
+
+ if (castType->isCARCBridgableType() &&
+ castExprType->isObjCARCBridgableType()){
+ Diag(loc, diag::err_arc_cast_requires_bridge)
+ << (castExprType->isBlockPointerType()? 1 : 0)
+ << castExprType
+ << 2
+ << castType
+ << castRange
+ << castExpr->getSourceRange();
+
+ Diag(NoteLoc, diag::note_arc_bridge)
+ << FixItHint::CreateInsertion(AfterLParen, "__bridge ");
+ Diag(NoteLoc, diag::note_arc_bridge_retained)
+ << castType
+ << FixItHint::CreateInsertion(AfterLParen, "__bridge_retained ");
+ return;
+ }
+ }
+
+ Diag(loc, diag::err_arc_mismatched_cast)
+ << (CCK != CCK_ImplicitConversion) << srcKind << castExprType << castType
+ << castRange << castExpr->getSourceRange();
+}
+
+ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc,
+ ObjCBridgeCastKind Kind,
+ SourceLocation BridgeKeywordLoc,
+ TypeSourceInfo *TSInfo,
+ Expr *SubExpr) {
+ QualType T = TSInfo->getType();
+ QualType FromType = SubExpr->getType();
+
+ bool MustConsume = false;
+ if (T->isDependentType() || SubExpr->isTypeDependent()) {
+ // Okay: we'll build a dependent expression type.
+ } else if (T->isObjCARCBridgableType() && FromType->isCARCBridgableType()) {
+ // Casting CF -> id
+ switch (Kind) {
+ case OBC_Bridge:
+ break;
+
+ case OBC_BridgeRetained:
+ Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind)
+ << 2
+ << FromType
+ << (T->isBlockPointerType()? 1 : 0)
+ << T
+ << SubExpr->getSourceRange()
+ << Kind;
+ Diag(BridgeKeywordLoc, diag::note_arc_bridge)
+ << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge");
+ Diag(BridgeKeywordLoc, diag::note_arc_bridge_transfer)
+ << FromType
+ << FixItHint::CreateReplacement(BridgeKeywordLoc,
+ "__bridge_transfer ");
+
+ Kind = OBC_Bridge;
+ break;
+
+ case OBC_BridgeTransfer:
+ // We must consume the Objective-C object produced by the cast.
+ MustConsume = true;
+ break;
+ }
+ } else if (T->isCARCBridgableType() && FromType->isObjCARCBridgableType()) {
+ // Okay: id -> CF
+ switch (Kind) {
+ case OBC_Bridge:
+ break;
+
+ case OBC_BridgeRetained:
+ // Produce the object before casting it.
+ SubExpr = ImplicitCastExpr::Create(Context, FromType,
+ CK_ObjCProduceObject,
+ SubExpr, 0, VK_RValue);
+ break;
+
+ case OBC_BridgeTransfer:
+ Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind)
+ << (FromType->isBlockPointerType()? 1 : 0)
+ << FromType
+ << 2
+ << T
+ << SubExpr->getSourceRange()
+ << Kind;
+
+ Diag(BridgeKeywordLoc, diag::note_arc_bridge)
+ << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge ");
+ Diag(BridgeKeywordLoc, diag::note_arc_bridge_retained)
+ << T
+ << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge_retained ");
+
+ Kind = OBC_Bridge;
+ break;
+ }
+ } else {
+ Diag(LParenLoc, diag::err_arc_bridge_cast_incompatible)
+ << FromType << T << Kind
+ << SubExpr->getSourceRange()
+ << TSInfo->getTypeLoc().getSourceRange();
+ return ExprError();
+ }
+
+ Expr *Result = new (Context) ObjCBridgedCastExpr(LParenLoc, Kind,
+ BridgeKeywordLoc,
+ TSInfo, SubExpr);
+
+ if (MustConsume) {
+ ExprNeedsCleanups = true;
+ Result = ImplicitCastExpr::Create(Context, T, CK_ObjCConsumeObject, Result,
+ 0, VK_RValue);
+ }
+
+ return Result;
+}
+
+ExprResult Sema::ActOnObjCBridgedCast(Scope *S,
+ SourceLocation LParenLoc,
+ ObjCBridgeCastKind Kind,
+ SourceLocation BridgeKeywordLoc,
+ ParsedType Type,
+ SourceLocation RParenLoc,
+ Expr *SubExpr) {
+ TypeSourceInfo *TSInfo = 0;
+ QualType T = GetTypeFromParser(Type, &TSInfo);
+ if (!TSInfo)
+ TSInfo = Context.getTrivialTypeSourceInfo(T, LParenLoc);
+ return BuildObjCBridgedCast(LParenLoc, Kind, BridgeKeywordLoc, TSInfo,
+ SubExpr);
+}
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index a33f5d0..7793215 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -2029,10 +2029,10 @@
DeclarationName InitializedEntity::getName() const {
switch (getKind()) {
- case EK_Parameter:
- if (!VariableOrMember)
- return DeclarationName();
- // Fall through
+ case EK_Parameter: {
+ ParmVarDecl *D = reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1);
+ return (D ? D->getDeclName() : DeclarationName());
+ }
case EK_Variable:
case EK_Member:
@@ -2057,10 +2057,12 @@
DeclaratorDecl *InitializedEntity::getDecl() const {
switch (getKind()) {
case EK_Variable:
- case EK_Parameter:
case EK_Member:
return VariableOrMember;
+ case EK_Parameter:
+ return reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1);
+
case EK_Result:
case EK_Exception:
case EK_New:
@@ -2123,6 +2125,9 @@
case SK_StringInit:
case SK_ObjCObjectConversion:
case SK_ArrayInit:
+ case SK_PassByIndirectCopyRestore:
+ case SK_PassByIndirectRestore:
+ case SK_ProduceObjCObject:
break;
case SK_ConversionSequence:
@@ -2306,6 +2311,22 @@
Steps.push_back(S);
}
+void InitializationSequence::AddPassByIndirectCopyRestoreStep(QualType type,
+ bool shouldCopy) {
+ Step s;
+ s.Kind = (shouldCopy ? SK_PassByIndirectCopyRestore
+ : SK_PassByIndirectRestore);
+ s.Type = type;
+ Steps.push_back(s);
+}
+
+void InitializationSequence::AddProduceObjCObjectStep(QualType T) {
+ Step S;
+ S.Kind = SK_ProduceObjCObject;
+ S.Type = T;
+ Steps.push_back(S);
+}
+
void InitializationSequence::SetOverloadFailure(FailureKind Failure,
OverloadingResult Result) {
setSequenceKind(FailedSequence);
@@ -2317,6 +2338,33 @@
// Attempt initialization
//===----------------------------------------------------------------------===//
+static void MaybeProduceObjCObject(Sema &S,
+ InitializationSequence &Sequence,
+ const InitializedEntity &Entity) {
+ if (!S.getLangOptions().ObjCAutoRefCount) return;
+
+ /// When initializing a parameter, produce the value if it's marked
+ /// __attribute__((ns_consumed)).
+ if (Entity.getKind() == InitializedEntity::EK_Parameter) {
+ if (!Entity.isParameterConsumed())
+ return;
+
+ assert(Entity.getType()->isObjCRetainableType() &&
+ "consuming an object of unretainable type?");
+ Sequence.AddProduceObjCObjectStep(Entity.getType());
+
+ /// When initializing a return value, if the return type is a
+ /// retainable type, then returns need to immediately retain the
+ /// object. If an autorelease is required, it will be done at the
+ /// last instant.
+ } else if (Entity.getKind() == InitializedEntity::EK_Result) {
+ if (!Entity.getType()->isObjCRetainableType())
+ return;
+
+ Sequence.AddProduceObjCObjectStep(Entity.getType());
+ }
+}
+
/// \brief Attempt list initialization (C++0x [dcl.init.list])
static void TryListInitialization(Sema &S,
const InitializedEntity &Entity,
@@ -2380,13 +2428,16 @@
bool DerivedToBase;
bool ObjCConversion;
+ bool ObjCLifetimeConversion;
assert(!S.CompareReferenceRelationship(Initializer->getLocStart(),
T1, T2, DerivedToBase,
- ObjCConversion) &&
+ ObjCConversion,
+ ObjCLifetimeConversion) &&
"Must have incompatible references when binding via conversion");
(void)DerivedToBase;
(void)ObjCConversion;
-
+ (void)ObjCLifetimeConversion;
+
// Build the candidate set directly in the initialization sequence
// structure, so that it will persist if we fail.
OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet();
@@ -2513,10 +2564,12 @@
bool NewDerivedToBase = false;
bool NewObjCConversion = false;
+ bool NewObjCLifetimeConversion = false;
Sema::ReferenceCompareResult NewRefRelationship
= S.CompareReferenceRelationship(DeclLoc, T1,
T2.getNonLValueExprType(S.Context),
- NewDerivedToBase, NewObjCConversion);
+ NewDerivedToBase, NewObjCConversion,
+ NewObjCLifetimeConversion);
if (NewRefRelationship == Sema::Ref_Incompatible) {
// If the type we've converted to is not reference-related to the
// type we're looking for, then there is another conversion step
@@ -2584,10 +2637,11 @@
bool isRValueRef = !isLValueRef;
bool DerivedToBase = false;
bool ObjCConversion = false;
+ bool ObjCLifetimeConversion = false;
Expr::Classification InitCategory = Initializer->Classify(S.Context);
Sema::ReferenceCompareResult RefRelationship
= S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase,
- ObjCConversion);
+ ObjCConversion, ObjCLifetimeConversion);
// C++0x [dcl.init.ref]p5:
// A reference to type "cv1 T1" is initialized by an expression of type
@@ -2746,11 +2800,15 @@
InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(cv1T1);
- if (S.TryImplicitConversion(Sequence, TempEntity, Initializer,
+ ImplicitConversionSequence ICS
+ = S.TryImplicitConversion(Initializer, TempEntity.getType(),
/*SuppressUserConversions*/ false,
AllowExplicit,
/*FIXME:InOverloadResolution=*/false,
- /*CStyle=*/Kind.isCStyleOrFunctionalCast())) {
+ /*CStyle=*/Kind.isCStyleOrFunctionalCast(),
+ /*AllowObjCWritebackConversion=*/false);
+
+ if (ICS.isBad()) {
// FIXME: Use the conversion function set stored in ICS to turn
// this into an overloading ambiguity diagnostic. However, we need
// to keep that set as an OverloadCandidateSet rather than as some
@@ -2764,6 +2822,8 @@
else
Sequence.SetFailed(InitializationSequence::FK_ReferenceInitFailed);
return;
+ } else {
+ Sequence.AddConversionSequenceStep(ICS, TempEntity.getType());
}
// [...] If T1 is reference-related to T2, cv1 must be the
@@ -2953,10 +3013,8 @@
// C++ [dcl.init]p6:
// To default-initialize an object of type T means:
// - if T is an array type, each element is default-initialized;
- QualType DestType = Entity.getType();
- while (const ArrayType *Array = S.Context.getAsArrayType(DestType))
- DestType = Array->getElementType();
-
+ QualType DestType = S.Context.getBaseElementType(Entity.getType());
+
// - if T is a (possibly cv-qualified) class type (Clause 9), the default
// constructor for T is called (and the initialization is ill-formed if
// T has no accessible default constructor);
@@ -2970,8 +3028,16 @@
// If a program calls for the default initialization of an object of
// a const-qualified type T, T shall be a class type with a user-provided
// default constructor.
- if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus)
+ if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus) {
Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
+ return;
+ }
+
+ // If the destination type has a lifetime property, zero-initialize it.
+ if (DestType.getQualifiers().hasObjCLifetime()) {
+ Sequence.AddZeroInitializationStep(Entity.getType());
+ return;
+ }
}
/// \brief Attempt a user-defined conversion between two types (C++ [dcl.init]),
@@ -3125,6 +3191,77 @@
}
}
+/// The non-zero enum values here are indexes into diagnostic alternatives.
+enum InvalidICRKind { IIK_okay, IIK_nonlocal, IIK_nonscalar };
+
+/// Determines whether this expression is an acceptable ICR source.
+static InvalidICRKind isInvalidICRSource(ASTContext &C, Expr *e) {
+ // Skip parens.
+ e = e->IgnoreParens();
+
+ // Skip address-of nodes.
+ if (UnaryOperator *op = dyn_cast<UnaryOperator>(e)) {
+ if (op->getOpcode() == UO_AddrOf)
+ return isInvalidICRSource(C, op->getSubExpr());
+
+ // Skip certain casts.
+ } else if (CastExpr *cast = dyn_cast<CastExpr>(e)) {
+ switch (cast->getCastKind()) {
+ case CK_Dependent:
+ case CK_BitCast:
+ case CK_LValueBitCast:
+ case CK_LValueToRValue:
+ case CK_NoOp:
+ return isInvalidICRSource(C, cast->getSubExpr());
+
+ case CK_ArrayToPointerDecay:
+ return IIK_nonscalar;
+
+ case CK_NullToPointer:
+ return IIK_okay;
+
+ default:
+ break;
+ }
+
+ // If we have a declaration reference, it had better be a local variable.
+ } else if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(e)) {
+ if (VarDecl *var = dyn_cast<VarDecl>(declRef->getDecl()))
+ return (var->hasLocalStorage() ? IIK_okay : IIK_nonlocal);
+
+ // If we have a conditional operator, check both sides.
+ } else if (ConditionalOperator *cond = dyn_cast<ConditionalOperator>(e)) {
+ if (InvalidICRKind iik = isInvalidICRSource(C, cond->getLHS()))
+ return iik;
+
+ return isInvalidICRSource(C, cond->getRHS());
+
+ // These are never scalar.
+ } else if (isa<ArraySubscriptExpr>(e)) {
+ return IIK_nonscalar;
+
+ // Otherwise, it needs to be a null pointer constant.
+ } else {
+ return (e->isNullPointerConstant(C, Expr::NPC_ValueDependentIsNull)
+ ? IIK_okay : IIK_nonlocal);
+ }
+
+ return IIK_nonlocal;
+}
+
+/// Check whether the given expression is a valid operand for an
+/// indirect copy/restore.
+static void checkIndirectCopyRestoreSource(Sema &S, Expr *src) {
+ assert(src->isRValue());
+
+ InvalidICRKind iik = isInvalidICRSource(S.Context, src);
+ if (iik == IIK_okay) return;
+
+ S.Diag(src->getExprLoc(), diag::err_arc_nonlocal_writeback)
+ << ((unsigned) iik - 1) // shift index into diagnostic explanations
+ << src->getSourceRange();
+}
+
/// \brief Determine whether we have compatible array types for the
/// purposes of GNU by-copy array initialization.
static bool hasCompatibleArrayTypes(ASTContext &Context,
@@ -3144,6 +3281,53 @@
return Source->isConstantArrayType() && Dest->isIncompleteArrayType();
}
+static bool tryObjCWritebackConversion(Sema &S,
+ InitializationSequence &Sequence,
+ const InitializedEntity &Entity,
+ Expr *Initializer) {
+ bool ArrayDecay = false;
+ QualType ArgType = Initializer->getType();
+ QualType ArgPointee;
+ if (const ArrayType *ArgArrayType = S.Context.getAsArrayType(ArgType)) {
+ ArrayDecay = true;
+ ArgPointee = ArgArrayType->getElementType();
+ ArgType = S.Context.getPointerType(ArgPointee);
+ }
+
+ // Handle write-back conversion.
+ QualType ConvertedArgType;
+ if (!S.isObjCWritebackConversion(ArgType, Entity.getType(),
+ ConvertedArgType))
+ return false;
+
+ // We should copy unless we're passing to an argument explicitly
+ // marked 'out'.
+ bool ShouldCopy = true;
+ if (ParmVarDecl *param = cast_or_null<ParmVarDecl>(Entity.getDecl()))
+ ShouldCopy = (param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out);
+
+ // Do we need an lvalue conversion?
+ if (ArrayDecay || Initializer->isGLValue()) {
+ ImplicitConversionSequence ICS;
+ ICS.setStandard();
+ ICS.Standard.setAsIdentityConversion();
+
+ QualType ResultType;
+ if (ArrayDecay) {
+ ICS.Standard.First = ICK_Array_To_Pointer;
+ ResultType = S.Context.getPointerType(ArgPointee);
+ } else {
+ ICS.Standard.First = ICK_Lvalue_To_Rvalue;
+ ResultType = Initializer->getType().getNonLValueExprType(S.Context);
+ }
+
+ Sequence.AddConversionSequenceStep(ICS, ResultType);
+ }
+
+ Sequence.AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy);
+ return true;
+}
+
InitializationSequence::InitializationSequence(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
@@ -3255,12 +3439,28 @@
return;
}
- // Handle initialization in C
+ // Determine whether we should consider writeback conversions for
+ // Objective-C ARC.
+ bool allowObjCWritebackConversion = S.getLangOptions().ObjCAutoRefCount &&
+ Entity.getKind() == InitializedEntity::EK_Parameter;
+
+ // We're at the end of the line for C: it's either a write-back conversion
+ // or it's a C assignment. There's no need to check anything else.
if (!S.getLangOptions().CPlusPlus) {
+ // If allowed, check whether this is an Objective-C writeback conversion.
+ if (allowObjCWritebackConversion &&
+ tryObjCWritebackConversion(S, *this, Entity, Initializer)) {
+ return;
+ }
+
+ // Handle initialization in C
AddCAssignmentStep(DestType);
+ MaybeProduceObjCObject(S, *this, Entity);
return;
}
+ assert(S.getLangOptions().CPlusPlus);
+
// - If the destination type is a (possibly cv-qualified) class type:
if (DestType->isRecordType()) {
// - If the initialization is direct-initialization, or if it is
@@ -3294,6 +3494,7 @@
// type, conversion functions are considered.
if (!SourceType.isNull() && SourceType->isRecordType()) {
TryUserDefinedConversion(S, Entity, Kind, Initializer, *this);
+ MaybeProduceObjCObject(S, *this, Entity);
return;
}
@@ -3302,12 +3503,38 @@
// conversions (Clause 4) will be used, if necessary, to convert the
// initializer expression to the cv-unqualified version of the
// destination type; no user-defined conversions are considered.
- if (S.TryImplicitConversion(*this, Entity, Initializer,
- /*SuppressUserConversions*/ true,
+
+ ImplicitConversionSequence ICS
+ = S.TryImplicitConversion(Initializer, Entity.getType(),
+ /*SuppressUserConversions*/true,
/*AllowExplicitConversions*/ false,
/*InOverloadResolution*/ false,
- /*CStyle=*/Kind.isCStyleOrFunctionalCast()))
- {
+ /*CStyle=*/Kind.isCStyleOrFunctionalCast(),
+ allowObjCWritebackConversion);
+
+ if (ICS.isStandard() &&
+ ICS.Standard.Second == ICK_Writeback_Conversion) {
+ // Objective-C ARC writeback conversion.
+
+ // We should copy unless we're passing to an argument explicitly
+ // marked 'out'.
+ bool ShouldCopy = true;
+ if (ParmVarDecl *Param = cast_or_null<ParmVarDecl>(Entity.getDecl()))
+ ShouldCopy = (Param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out);
+
+ // If there was an lvalue adjustment, add it as a separate conversion.
+ if (ICS.Standard.First == ICK_Array_To_Pointer ||
+ ICS.Standard.First == ICK_Lvalue_To_Rvalue) {
+ ImplicitConversionSequence LvalueICS;
+ LvalueICS.setStandard();
+ LvalueICS.Standard.setAsIdentityConversion();
+ LvalueICS.Standard.setAllToTypes(ICS.Standard.getToType(0));
+ LvalueICS.Standard.First = ICS.Standard.First;
+ AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0));
+ }
+
+ AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy);
+ } else if (ICS.isBad()) {
DeclAccessPair dap;
if (Initializer->getType() == Context.OverloadTy &&
!S.ResolveAddressOfOverloadedFunction(Initializer
@@ -3315,6 +3542,8 @@
SetFailed(InitializationSequence::FK_AddressOfOverloadFailed);
else
SetFailed(InitializationSequence::FK_ConversionFailed);
+ } else {
+ AddConversionSequenceStep(ICS, Entity.getType());
}
}
@@ -3560,7 +3789,7 @@
<< (int)Entity.getKind() << CurInitExpr->getType()
<< CurInitExpr->getSourceRange();
S.Diag(Best->Function->getLocation(), diag::note_unavailable_here)
- << Best->Function->isDeleted();
+ << 1 << Best->Function->isDeleted();
return ExprError();
}
@@ -3733,7 +3962,10 @@
case SK_CAssignment:
case SK_StringInit:
case SK_ObjCObjectConversion:
- case SK_ArrayInit: {
+ case SK_ArrayInit:
+ case SK_PassByIndirectCopyRestore:
+ case SK_PassByIndirectRestore:
+ case SK_ProduceObjCObject: {
assert(Args.size() == 1);
CurInit = Args.get()[0];
if (!CurInit.get()) return ExprError();
@@ -3969,10 +4201,14 @@
}
case SK_ConversionSequence: {
+ Sema::CheckedConversionKind CCK
+ = Kind.isCStyleCast()? Sema::CCK_CStyleCast
+ : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast
+ : Kind.isExplicitCast()? Sema::CCK_OtherCast
+ : Sema::CCK_ImplicitConversion;
ExprResult CurInitExprRes =
S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS,
- getAssignmentAction(Entity),
- Kind.isCStyleOrFunctionalCast());
+ getAssignmentAction(Entity), CCK);
if (CurInitExprRes.isInvalid())
return ExprError();
CurInit = move(CurInitExprRes);
@@ -4182,7 +4418,20 @@
}
}
}
+ break;
+ case SK_PassByIndirectCopyRestore:
+ case SK_PassByIndirectRestore:
+ checkIndirectCopyRestoreSource(S, CurInit.get());
+ CurInit = S.Owned(new (S.Context)
+ ObjCIndirectCopyRestoreExpr(CurInit.take(), Step->Type,
+ Step->Kind == SK_PassByIndirectCopyRestore));
+ break;
+
+ case SK_ProduceObjCObject:
+ CurInit = S.Owned(ImplicitCastExpr::Create(S.Context, Step->Type,
+ CK_ObjCProduceObject,
+ CurInit.take(), 0, VK_RValue));
break;
}
}
@@ -4278,7 +4527,7 @@
true);
if (Ovl == OR_Deleted) {
S.Diag(Best->Function->getLocation(), diag::note_unavailable_here)
- << Best->Function->isDeleted();
+ << 1 << Best->Function->isDeleted();
} else {
llvm_unreachable("Inconsistent overload resolution?");
}
@@ -4443,7 +4692,7 @@
= FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best);
if (Ovl == OR_Deleted) {
S.Diag(Best->Function->getLocation(), diag::note_unavailable_here)
- << Best->Function->isDeleted();
+ << 1 << Best->Function->isDeleted();
} else {
llvm_unreachable("Inconsistent overload resolution?");
}
@@ -4674,6 +4923,18 @@
case SK_ArrayInit:
OS << "array initialization";
break;
+
+ case SK_PassByIndirectCopyRestore:
+ OS << "pass by indirect copy and restore";
+ break;
+
+ case SK_PassByIndirectRestore:
+ OS << "pass by indirect restore";
+ break;
+
+ case SK_ProduceObjCObject:
+ OS << "Objective-C object retension";
+ break;
}
}
}
diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp
index 6c4469c..2dbb740 100644
--- a/lib/Sema/SemaObjCProperty.cpp
+++ b/lib/Sema/SemaObjCProperty.cpp
@@ -24,6 +24,51 @@
// Grammar actions.
//===----------------------------------------------------------------------===//
+/// Check the internal consistency of a property declaration.
+static void checkARCPropertyDecl(Sema &S, ObjCPropertyDecl *property) {
+ if (property->isInvalidDecl()) return;
+
+ ObjCPropertyDecl::PropertyAttributeKind propertyKind
+ = property->getPropertyAttributes();
+ Qualifiers::ObjCLifetime propertyLifetime
+ = property->getType().getObjCLifetime();
+
+ // Nothing to do if we don't have a lifetime.
+ if (propertyLifetime == Qualifiers::OCL_None) return;
+
+ Qualifiers::ObjCLifetime expectedLifetime;
+ unsigned selector;
+
+ // Strong properties should have either strong or no lifetime.
+ if (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_strong |
+ ObjCPropertyDecl::OBJC_PR_copy)) {
+ expectedLifetime = Qualifiers::OCL_Strong;
+ selector = 0;
+ } else if (propertyKind & ObjCPropertyDecl::OBJC_PR_weak) {
+ expectedLifetime = Qualifiers::OCL_Weak;
+ selector = 1;
+ } else if (propertyKind & (ObjCPropertyDecl::OBJC_PR_assign |
+ ObjCPropertyDecl::OBJC_PR_unsafe_unretained) &&
+ property->getType()->isObjCRetainableType()) {
+ expectedLifetime = Qualifiers::OCL_ExplicitNone;
+ selector = 2;
+ } else {
+ // We have a lifetime qualifier but no dominating property
+ // attribute. That's okay.
+ return;
+ }
+
+ if (propertyLifetime == expectedLifetime) return;
+
+ property->setInvalidDecl();
+ S.Diag(property->getLocation(),
+ diag::err_arc_inconsistent_property_lifetime)
+ << property->getDeclName()
+ << selector
+ << propertyLifetime;
+}
+
Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
FieldDeclarator &FD,
ObjCDeclSpec &ODS,
@@ -34,6 +79,14 @@
tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC) {
unsigned Attributes = ODS.getPropertyAttributes();
+ TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S);
+ QualType T = TSI->getType();
+ if ((getLangOptions().getGCMode() != LangOptions::NonGC &&
+ T.isObjCGCWeak()) ||
+ (getLangOptions().ObjCAutoRefCount &&
+ T.getObjCLifetime() == Qualifiers::OCL_Weak))
+ Attributes |= ObjCDeclSpec::DQ_PR_weak;
+
bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) ||
// default is readwrite!
!(Attributes & ObjCDeclSpec::DQ_PR_readonly));
@@ -42,9 +95,10 @@
bool isAssign = ((Attributes & ObjCDeclSpec::DQ_PR_assign) ||
(isReadWrite &&
!(Attributes & ObjCDeclSpec::DQ_PR_retain) &&
- !(Attributes & ObjCDeclSpec::DQ_PR_copy)));
-
- TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S);
+ !(Attributes & ObjCDeclSpec::DQ_PR_strong) &&
+ !(Attributes & ObjCDeclSpec::DQ_PR_copy) &&
+ !(Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) &&
+ !(Attributes & ObjCDeclSpec::DQ_PR_weak)));
// Proceed with constructing the ObjCPropertDecls.
ObjCContainerDecl *ClassDecl =
@@ -58,20 +112,27 @@
Attributes,
isOverridingProperty, TSI,
MethodImplKind);
- if (Res)
+ if (Res) {
CheckObjCPropertyAttributes(Res, AtLoc, Attributes);
+ if (getLangOptions().ObjCAutoRefCount)
+ checkARCPropertyDecl(*this, cast<ObjCPropertyDecl>(Res));
+ }
return Res;
}
- Decl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD,
- GetterSel, SetterSel,
- isAssign, isReadWrite,
- Attributes, TSI, MethodImplKind);
+ ObjCPropertyDecl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD,
+ GetterSel, SetterSel,
+ isAssign, isReadWrite,
+ Attributes, TSI, MethodImplKind);
if (lexicalDC)
Res->setLexicalDeclContext(lexicalDC);
// Validate the attributes on the @property.
CheckObjCPropertyAttributes(Res, AtLoc, Attributes);
+
+ if (getLangOptions().ObjCAutoRefCount)
+ checkARCPropertyDecl(*this, Res);
+
return Res;
}
@@ -158,6 +219,7 @@
if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) {
unsigned retainCopyNonatomic =
(ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_strong |
ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_nonatomic);
if ((Attributes & retainCopyNonatomic) !=
@@ -189,6 +251,8 @@
PIDecl->makeitReadWriteAttribute();
if (Attributes & ObjCDeclSpec::DQ_PR_retain)
PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain);
+ if (Attributes & ObjCDeclSpec::DQ_PR_strong)
+ PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong);
if (Attributes & ObjCDeclSpec::DQ_PR_copy)
PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy);
PIDecl->setSetterName(SetterSel);
@@ -287,9 +351,18 @@
if (Attributes & ObjCDeclSpec::DQ_PR_retain)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain);
+ if (Attributes & ObjCDeclSpec::DQ_PR_strong)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong);
+
+ if (Attributes & ObjCDeclSpec::DQ_PR_weak)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_weak);
+
if (Attributes & ObjCDeclSpec::DQ_PR_copy)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy);
+ if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
+
if (isAssign)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign);
@@ -298,8 +371,17 @@
else if (Attributes & ObjCDeclSpec::DQ_PR_atomic)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic);
+ // FIXME: Why do PropertyAttributesAsWritten get set from PropertyAttributes,
+ // shouldn't PropertyAttributesAsWritten get set *only* through the attributes
+ // of the ObjCDeclSpec ?
PDecl->setPropertyAttributesAsWritten(PDecl->getPropertyAttributes());
-
+
+ // 'unsafe_unretained' is alias for 'assign'.
+ if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign);
+ if (isAssign)
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained);
+
if (MethodImplKind == tok::objc_required)
PDecl->setPropertyImplementation(ObjCPropertyDecl::Required);
else if (MethodImplKind == tok::objc_optional)
@@ -308,6 +390,93 @@
return PDecl;
}
+static void checkARCPropertyImpl(Sema &S, SourceLocation propertyImplLoc,
+ ObjCPropertyDecl *property,
+ ObjCIvarDecl *ivar) {
+ if (property->isInvalidDecl() || ivar->isInvalidDecl()) return;
+
+ QualType propertyType = property->getType();
+ Qualifiers::ObjCLifetime propertyLifetime = propertyType.getObjCLifetime();
+ ObjCPropertyDecl::PropertyAttributeKind propertyKind
+ = property->getPropertyAttributes();
+
+ QualType ivarType = ivar->getType();
+ Qualifiers::ObjCLifetime ivarLifetime = ivarType.getObjCLifetime();
+
+ // Case 1: strong properties.
+ if (propertyLifetime == Qualifiers::OCL_Strong ||
+ (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_strong |
+ ObjCPropertyDecl::OBJC_PR_copy))) {
+ switch (ivarLifetime) {
+ case Qualifiers::OCL_Strong:
+ // Okay.
+ return;
+
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_Autoreleasing:
+ // These aren't valid lifetimes for object ivars; don't diagnose twice.
+ return;
+
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Weak:
+ S.Diag(propertyImplLoc, diag::err_arc_strong_property_lifetime)
+ << property->getDeclName()
+ << ivar->getDeclName()
+ << ivarLifetime;
+ break;
+ }
+
+ // Case 2: weak properties.
+ } else if (propertyLifetime == Qualifiers::OCL_Weak ||
+ (propertyKind & ObjCPropertyDecl::OBJC_PR_weak)) {
+ switch (ivarLifetime) {
+ case Qualifiers::OCL_Weak:
+ // Okay.
+ return;
+
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_Autoreleasing:
+ // These aren't valid lifetimes for object ivars; don't diagnose twice.
+ return;
+
+ case Qualifiers::OCL_ExplicitNone:
+ case Qualifiers::OCL_Strong:
+ S.Diag(propertyImplLoc, diag::error_weak_property)
+ << property->getDeclName()
+ << ivar->getDeclName();
+ break;
+ }
+
+ // Case 3: assign properties.
+ } else if ((propertyKind & ObjCPropertyDecl::OBJC_PR_assign) &&
+ propertyType->isObjCRetainableType()) {
+ switch (ivarLifetime) {
+ case Qualifiers::OCL_ExplicitNone:
+ // Okay.
+ return;
+
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_Autoreleasing:
+ // These aren't valid lifetimes for object ivars; don't diagnose twice.
+ return;
+
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Strong:
+ S.Diag(propertyImplLoc, diag::err_arc_assign_property_lifetime)
+ << property->getDeclName()
+ << ivar->getDeclName();
+ break;
+ }
+
+ // Any other property should be ignored.
+ } else {
+ return;
+ }
+
+ S.Diag(property->getLocation(), diag::note_property_declare);
+}
+
/// ActOnPropertyImplDecl - This routine performs semantic checks and
/// builds the AST node for a property implementation declaration; declared
@@ -399,6 +568,8 @@
// @synthesize
if (!PropertyIvar)
PropertyIvar = PropertyId;
+ ObjCPropertyDecl::PropertyAttributeKind kind
+ = property->getPropertyAttributes();
QualType PropType = Context.getCanonicalType(property->getType());
QualType PropertyIvarType = PropType;
if (PropType->isReferenceType())
@@ -407,6 +578,45 @@
ObjCInterfaceDecl *ClassDeclared;
Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared);
if (!Ivar) {
+ // In ARC, give the ivar a lifetime qualifier based on its
+ // property attributes.
+ if (getLangOptions().ObjCAutoRefCount &&
+ !PropertyIvarType.getObjCLifetime()) {
+
+ // retain/copy have retaining lifetime.
+ if (kind & (ObjCPropertyDecl::OBJC_PR_retain |
+ ObjCPropertyDecl::OBJC_PR_strong |
+ ObjCPropertyDecl::OBJC_PR_copy)) {
+ Qualifiers qs;
+ qs.addObjCLifetime(Qualifiers::OCL_Strong);
+ PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs);
+ }
+ else if (kind & ObjCPropertyDecl::OBJC_PR_weak) {
+ if (getLangOptions().ObjCNoAutoRefCountRuntime) {
+ Diag(PropertyLoc, diag::err_arc_weak_no_runtime);
+ Diag(property->getLocation(), diag::note_property_declare);
+ }
+ Qualifiers qs;
+ qs.addObjCLifetime(Qualifiers::OCL_Weak);
+ PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs);
+ }
+ else if (kind & ObjCPropertyDecl::OBJC_PR_assign &&
+ PropertyIvarType->isObjCRetainableType()) {
+ // assume that an 'assign' property synthesizes __unsafe_unretained
+ // ivar
+ Qualifiers qs;
+ qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone);
+ PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs);
+ }
+ }
+
+ if (kind & ObjCPropertyDecl::OBJC_PR_weak &&
+ !getLangOptions().ObjCAutoRefCount &&
+ getLangOptions().getGCMode() == LangOptions::NonGC) {
+ Diag(PropertyLoc, diag::error_synthesize_weak_non_arc_or_gc);
+ Diag(property->getLocation(), diag::note_property_declare);
+ }
+
Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl,
PropertyLoc, PropertyLoc, PropertyIvar,
PropertyIvarType, /*Dinfo=*/0,
@@ -435,7 +645,7 @@
if (PropertyIvarType != IvarType) {
bool compat = false;
if (isa<ObjCObjectPointerType>(PropertyIvarType)
- && isa<ObjCObjectPointerType>(IvarType))
+ && isa<ObjCObjectPointerType>(IvarType))
compat =
Context.canAssignObjCInterfaces(
PropertyIvarType->getAs<ObjCObjectPointerType>(),
@@ -470,12 +680,13 @@
// Fall thru - see previous comment
}
// __weak is explicit. So it works on Canonical type.
- if (PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() &&
- getLangOptions().getGCMode() != LangOptions::NonGC) {
+ if ((PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() &&
+ getLangOptions().getGCMode() != LangOptions::NonGC)) {
Diag(PropertyLoc, diag::error_weak_property)
<< property->getDeclName() << Ivar->getDeclName();
// Fall thru - see previous comment
}
+ // Fall thru - see previous comment
if ((property->getType()->isObjCObjectPointerType() ||
PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() &&
getLangOptions().getGCMode() != LangOptions::NonGC) {
@@ -484,9 +695,12 @@
// Fall thru - see previous comment
}
}
+ if (getLangOptions().ObjCAutoRefCount)
+ checkARCPropertyImpl(*this, PropertyLoc, property, Ivar);
} else if (PropertyIvar)
// @dynamic
Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl);
+
assert (property && "ActOnPropertyImplDecl - property declaration missing");
ObjCPropertyImplDecl *PIDecl =
ObjCPropertyImplDecl::Create(Context, CurContext, AtLoc, PropertyLoc,
@@ -632,10 +846,19 @@
!= (SAttr & ObjCPropertyDecl::OBJC_PR_copy))
Diag(Property->getLocation(), diag::warn_property_attribute)
<< Property->getDeclName() << "copy" << inheritedName;
- else if ((CAttr & ObjCPropertyDecl::OBJC_PR_retain)
- != (SAttr & ObjCPropertyDecl::OBJC_PR_retain))
- Diag(Property->getLocation(), diag::warn_property_attribute)
- << Property->getDeclName() << "retain" << inheritedName;
+ else {
+ unsigned CAttrRetain =
+ (CAttr &
+ (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong));
+ unsigned SAttrRetain =
+ (SAttr &
+ (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong));
+ bool CStrong = (CAttrRetain != 0);
+ bool SStrong = (SAttrRetain != 0);
+ if (CStrong != SStrong)
+ Diag(Property->getLocation(), diag::warn_property_attribute)
+ << Property->getDeclName() << "retain (or strong)" << inheritedName;
+ }
if ((CAttr & ObjCPropertyDecl::OBJC_PR_nonatomic)
!= (SAttr & ObjCPropertyDecl::OBJC_PR_nonatomic))
@@ -1135,6 +1358,34 @@
}
}
+void Sema::DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D) {
+ if (getLangOptions().getGCMode() == LangOptions::GCOnly)
+ return;
+
+ for (ObjCImplementationDecl::propimpl_iterator
+ i = D->propimpl_begin(), e = D->propimpl_end(); i != e; ++i) {
+ ObjCPropertyImplDecl *PID = *i;
+ if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
+ continue;
+
+ const ObjCPropertyDecl *PD = PID->getPropertyDecl();
+ if (PD && !D->getInstanceMethod(PD->getGetterName())) {
+ ObjCMethodDecl *method = PD->getGetterMethodDecl();
+ if (!method)
+ continue;
+ ObjCMethodFamily family = method->getMethodFamily();
+ if (family == OMF_alloc || family == OMF_copy ||
+ family == OMF_mutableCopy || family == OMF_new) {
+ if (getLangOptions().ObjCAutoRefCount)
+ Diag(PID->getLocation(), diag::err_ownin_getter_rule);
+ else
+ Diag(PID->getLocation(), diag::warn_ownin_getter_rule);
+ Diag(PD->getLocation(), diag::note_property_declare);
+ }
+ }
+ }
+}
+
/// AddPropertyAttrs - Propagates attributes from a property to the
/// implicitly-declared getter or setter for that property.
static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod,
@@ -1245,7 +1496,7 @@
ParmVarDecl *Argument = ParmVarDecl::Create(Context, SetterMethod,
Loc, Loc,
property->getIdentifier(),
- property->getType(),
+ property->getType().getUnqualifiedType(),
/*TInfo=*/0,
SC_None,
SC_None,
@@ -1287,7 +1538,7 @@
SourceLocation Loc,
unsigned &Attributes) {
// FIXME: Improve the reported location.
- if (!PDecl)
+ if (!PDecl || PDecl->isInvalidDecl())
return;
ObjCPropertyDecl *PropertyDecl = cast<ObjCPropertyDecl>(PDecl);
@@ -1297,12 +1548,16 @@
if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
(Attributes & (ObjCDeclSpec::DQ_PR_readwrite |
ObjCDeclSpec::DQ_PR_assign |
+ ObjCDeclSpec::DQ_PR_unsafe_unretained |
ObjCDeclSpec::DQ_PR_copy |
- ObjCDeclSpec::DQ_PR_retain))) {
+ ObjCDeclSpec::DQ_PR_retain |
+ ObjCDeclSpec::DQ_PR_strong))) {
const char * which = (Attributes & ObjCDeclSpec::DQ_PR_readwrite) ?
"readwrite" :
(Attributes & ObjCDeclSpec::DQ_PR_assign) ?
"assign" :
+ (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) ?
+ "unsafe_unretained" :
(Attributes & ObjCDeclSpec::DQ_PR_copy) ?
"copy" : "retain";
@@ -1313,14 +1568,15 @@
}
// Check for copy or retain on non-object types.
- if ((Attributes & (ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain)) &&
- !PropertyTy->isObjCObjectPointerType() &&
- !PropertyTy->isBlockPointerType() &&
- !Context.isObjCNSObjectType(PropertyTy) &&
+ if ((Attributes & (ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy |
+ ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong)) &&
+ !PropertyTy->isObjCRetainableType() &&
!PropertyDecl->getAttr<ObjCNSObjectAttr>()) {
Diag(Loc, diag::err_objc_property_requires_object)
- << (Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain");
- Attributes &= ~(ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain);
+ << (Attributes & ObjCDeclSpec::DQ_PR_weak ? "weak" :
+ Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain (or strong)");
+ Attributes &= ~(ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy |
+ ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong);
}
// Check for more than one of { assign, copy, retain }.
@@ -1335,27 +1591,88 @@
<< "assign" << "retain";
Attributes &= ~ObjCDeclSpec::DQ_PR_retain;
}
+ if (Attributes & ObjCDeclSpec::DQ_PR_strong) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "assign" << "strong";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_strong;
+ }
+ if (getLangOptions().ObjCAutoRefCount &&
+ (Attributes & ObjCDeclSpec::DQ_PR_weak)) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "assign" << "weak";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
+ }
+ } else if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) {
+ if (Attributes & ObjCDeclSpec::DQ_PR_copy) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "unsafe_unretained" << "copy";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_copy;
+ }
+ if (Attributes & ObjCDeclSpec::DQ_PR_retain) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "unsafe_unretained" << "retain";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_retain;
+ }
+ if (Attributes & ObjCDeclSpec::DQ_PR_strong) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "unsafe_unretained" << "strong";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_strong;
+ }
+ if (getLangOptions().ObjCAutoRefCount &&
+ (Attributes & ObjCDeclSpec::DQ_PR_weak)) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "unsafe_unretained" << "weak";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
+ }
} else if (Attributes & ObjCDeclSpec::DQ_PR_copy) {
if (Attributes & ObjCDeclSpec::DQ_PR_retain) {
Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
<< "copy" << "retain";
Attributes &= ~ObjCDeclSpec::DQ_PR_retain;
}
+ if (Attributes & ObjCDeclSpec::DQ_PR_strong) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "copy" << "strong";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_strong;
+ }
+ if (Attributes & ObjCDeclSpec::DQ_PR_weak) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "copy" << "weak";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
+ }
+ }
+ else if ((Attributes & ObjCDeclSpec::DQ_PR_retain) &&
+ (Attributes & ObjCDeclSpec::DQ_PR_weak)) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "retain" << "weak";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
+ }
+ else if ((Attributes & ObjCDeclSpec::DQ_PR_strong) &&
+ (Attributes & ObjCDeclSpec::DQ_PR_weak)) {
+ Diag(Loc, diag::err_objc_property_attr_mutually_exclusive)
+ << "strong" << "weak";
+ Attributes &= ~ObjCDeclSpec::DQ_PR_weak;
}
// Warn if user supplied no assignment attribute, property is
// readwrite, and this is an object type.
if (!(Attributes & (ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_copy |
- ObjCDeclSpec::DQ_PR_retain)) &&
+ ObjCDeclSpec::DQ_PR_unsafe_unretained |
+ ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong |
+ ObjCDeclSpec::DQ_PR_weak)) &&
!(Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
PropertyTy->isObjCObjectPointerType()) {
- // Skip this warning in gc-only mode.
- if (getLangOptions().getGCMode() != LangOptions::GCOnly)
- Diag(Loc, diag::warn_objc_property_no_assignment_attribute);
+ if (getLangOptions().ObjCAutoRefCount)
+ Diag(Loc, diag::err_arc_objc_property_default_assign_on_object);
+ else {
+ // Skip this warning in gc-only mode.
+ if (getLangOptions().getGCMode() != LangOptions::GCOnly)
+ Diag(Loc, diag::warn_objc_property_no_assignment_attribute);
- // If non-gc code warn that this is likely inappropriate.
- if (getLangOptions().getGCMode() == LangOptions::NonGC)
- Diag(Loc, diag::warn_objc_property_default_assign_on_object);
+ // If non-gc code warn that this is likely inappropriate.
+ if (getLangOptions().getGCMode() == LangOptions::NonGC)
+ Diag(Loc, diag::warn_objc_property_default_assign_on_object);
+ }
// FIXME: Implement warning dependent on NSCopying being
// implemented. See also:
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index 4bba6f8..2995e2e 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -49,7 +49,8 @@
static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
bool InOverloadResolution,
StandardConversionSequence &SCS,
- bool CStyle);
+ bool CStyle,
+ bool AllowObjCWritebackConversion);
static bool IsTransparentUnionStandardConversion(Sema &S, Expr* From,
QualType &ToType,
@@ -106,6 +107,7 @@
ICC_Conversion,
ICC_Conversion,
ICC_Conversion,
+ ICC_Conversion,
ICC_Conversion
};
return Category[(int)Kind];
@@ -138,7 +140,8 @@
ICR_Conversion,
ICR_Complex_Real_Conversion,
ICR_Conversion,
- ICR_Conversion
+ ICR_Conversion,
+ ICR_Writeback_Conversion
};
return Rank[(int)Kind];
}
@@ -170,6 +173,7 @@
"Complex-real conversion",
"Block Pointer conversion",
"Transparent Union Conversion"
+ "Writeback conversion"
};
return Name[Kind];
}
@@ -181,12 +185,14 @@
Second = ICK_Identity;
Third = ICK_Identity;
DeprecatedStringLiteralToCharPtr = false;
+ QualificationIncludesObjCLifetime = false;
ReferenceBinding = false;
DirectBinding = false;
IsLvalueReference = true;
BindsToFunctionLvalue = false;
BindsToRvalue = false;
BindsImplicitObjectArgumentWithoutRefQualifier = false;
+ ObjCLifetimeConversionBinding = false;
CopyConstructor = 0;
}
@@ -753,15 +759,20 @@
/// not permitted.
/// If @p AllowExplicit, then explicit user-defined conversions are
/// permitted.
+///
+/// \param AllowObjCWritebackConversion Whether we allow the Objective-C
+/// writeback conversion, which allows __autoreleasing id* parameters to
+/// be initialized with __strong id* or __weak id* arguments.
static ImplicitConversionSequence
TryImplicitConversion(Sema &S, Expr *From, QualType ToType,
bool SuppressUserConversions,
bool AllowExplicit,
bool InOverloadResolution,
- bool CStyle) {
+ bool CStyle,
+ bool AllowObjCWritebackConversion) {
ImplicitConversionSequence ICS;
if (IsStandardConversion(S, From, ToType, InOverloadResolution,
- ICS.Standard, CStyle)) {
+ ICS.Standard, CStyle, AllowObjCWritebackConversion)){
ICS.setStandard();
return ICS;
}
@@ -867,24 +878,17 @@
return ICS;
}
-bool Sema::TryImplicitConversion(InitializationSequence &Sequence,
- const InitializedEntity &Entity,
- Expr *Initializer,
- bool SuppressUserConversions,
- bool AllowExplicitConversions,
- bool InOverloadResolution,
- bool CStyle) {
- ImplicitConversionSequence ICS
- = clang::TryImplicitConversion(*this, Initializer, Entity.getType(),
- SuppressUserConversions,
- AllowExplicitConversions,
- InOverloadResolution,
- CStyle);
- if (ICS.isBad()) return true;
-
- // Perform the actual conversion.
- Sequence.AddConversionSequenceStep(ICS, Entity.getType());
- return false;
+ImplicitConversionSequence
+Sema::TryImplicitConversion(Expr *From, QualType ToType,
+ bool SuppressUserConversions,
+ bool AllowExplicit,
+ bool InOverloadResolution,
+ bool CStyle,
+ bool AllowObjCWritebackConversion) {
+ return clang::TryImplicitConversion(*this, From, ToType,
+ SuppressUserConversions, AllowExplicit,
+ InOverloadResolution, CStyle,
+ AllowObjCWritebackConversion);
}
/// PerformImplicitConversion - Perform an implicit conversion of the
@@ -903,11 +907,18 @@
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
AssignmentAction Action, bool AllowExplicit,
ImplicitConversionSequence& ICS) {
+ // Objective-C ARC: Determine whether we will allow the writeback conversion.
+ bool AllowObjCWritebackConversion
+ = getLangOptions().ObjCAutoRefCount &&
+ (Action == AA_Passing || Action == AA_Sending);
+
+
ICS = clang::TryImplicitConversion(*this, From, ToType,
/*SuppressUserConversions=*/false,
AllowExplicit,
/*InOverloadResolution=*/false,
- /*CStyle=*/false);
+ /*CStyle=*/false,
+ AllowObjCWritebackConversion);
return PerformImplicitConversion(From, ToType, ICS, Action);
}
@@ -1016,7 +1027,8 @@
static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
bool InOverloadResolution,
StandardConversionSequence &SCS,
- bool CStyle) {
+ bool CStyle,
+ bool AllowObjCWritebackConversion) {
QualType FromType = From->getType();
// Standard conversions (C++ [conv])
@@ -1123,6 +1135,7 @@
// conversion (4.4). (C++ 4.2p2)
SCS.Second = ICK_Identity;
SCS.Third = ICK_Qualification;
+ SCS.QualificationIncludesObjCLifetime = false;
SCS.setAllToTypes(FromType);
return true;
}
@@ -1199,7 +1212,10 @@
SCS.Second = ICK_Floating_Integral;
FromType = ToType.getUnqualifiedType();
} else if (S.IsBlockPointerConversion(FromType, ToType, FromType)) {
- SCS.Second = ICK_Block_Pointer_Conversion;
+ SCS.Second = ICK_Block_Pointer_Conversion;
+ } else if (AllowObjCWritebackConversion &&
+ S.isObjCWritebackConversion(FromType, ToType, FromType)) {
+ SCS.Second = ICK_Writeback_Conversion;
} else if (S.IsPointerConversion(From, FromType, ToType, InOverloadResolution,
FromType, IncompatibleObjC)) {
// Pointer conversions (C++ 4.10).
@@ -1235,8 +1251,11 @@
QualType CanonFrom;
QualType CanonTo;
// The third conversion can be a qualification conversion (C++ 4p1).
- if (S.IsQualificationConversion(FromType, ToType, CStyle)) {
+ bool ObjCLifetimeConversion;
+ if (S.IsQualificationConversion(FromType, ToType, CStyle,
+ ObjCLifetimeConversion)) {
SCS.Third = ICK_Qualification;
+ SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion;
FromType = ToType;
CanonFrom = S.Context.getCanonicalType(FromType);
CanonTo = S.Context.getCanonicalType(ToType);
@@ -1253,7 +1272,8 @@
if (CanonFrom.getLocalUnqualifiedType()
== CanonTo.getLocalUnqualifiedType() &&
(CanonFrom.getLocalCVRQualifiers() != CanonTo.getLocalCVRQualifiers()
- || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr())) {
+ || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr()
+ || CanonFrom.getObjCLifetime() != CanonTo.getObjCLifetime())) {
FromType = ToType;
CanonFrom = CanonTo;
}
@@ -1284,7 +1304,8 @@
for (RecordDecl::field_iterator it = UD->field_begin(),
itend = UD->field_end();
it != itend; ++it) {
- if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS, CStyle)) {
+ if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS,
+ CStyle, /*ObjCWritebackConversion=*/false)) {
ToType = it->getType();
return true;
}
@@ -1479,16 +1500,18 @@
/// same type qualifiers as FromPtr has on its pointee type. ToType,
/// if non-empty, will be a pointer to ToType that may or may not have
/// the right set of qualifiers on its pointee.
+///
static QualType
BuildSimilarlyQualifiedPointerType(const Type *FromPtr,
QualType ToPointee, QualType ToType,
- ASTContext &Context) {
+ ASTContext &Context,
+ bool StripObjCLifetime = false) {
assert((FromPtr->getTypeClass() == Type::Pointer ||
FromPtr->getTypeClass() == Type::ObjCObjectPointer) &&
"Invalid similarly-qualified pointer type");
- /// \brief Conversions to 'id' subsume cv-qualifier conversions.
- if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType())
+ /// Conversions to 'id' subsume cv-qualifier conversions.
+ if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType())
return ToType.getUnqualifiedType();
QualType CanonFromPointee
@@ -1496,6 +1519,9 @@
QualType CanonToPointee = Context.getCanonicalType(ToPointee);
Qualifiers Quals = CanonFromPointee.getQualifiers();
+ if (StripObjCLifetime)
+ Quals.removeObjCLifetime();
+
// Exact qualifier match -> return the pointer type we're converting to.
if (CanonToPointee.getLocalQualifiers() == Quals) {
// ToType is exactly what we need. Return it.
@@ -1599,7 +1625,8 @@
// Beyond this point, both types need to be pointers
// , including objective-c pointers.
QualType ToPointeeType = ToTypePtr->getPointeeType();
- if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType()) {
+ if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType() &&
+ !getLangOptions().ObjCAutoRefCount) {
ConvertedType = BuildSimilarlyQualifiedPointerType(
FromType->getAs<ObjCObjectPointerType>(),
ToPointeeType,
@@ -1624,7 +1651,8 @@
ToPointeeType->isVoidType()) {
ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr,
ToPointeeType,
- ToType, Context);
+ ToType, Context,
+ /*StripObjCLifetime=*/true);
return true;
}
@@ -1814,6 +1842,7 @@
ToPointeeType->getAs<ObjCObjectPointerType>() &&
isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType,
IncompatibleObjC)) {
+
ConvertedType = Context.getPointerType(ConvertedType);
ConvertedType = AdoptQualifiers(Context, ConvertedType, FromQualifiers);
return true;
@@ -1885,6 +1914,73 @@
return false;
}
+/// \brief Determine whether this is an Objective-C writeback conversion,
+/// used for parameter passing when performing automatic reference counting.
+///
+/// \param FromType The type we're converting form.
+///
+/// \param ToType The type we're converting to.
+///
+/// \param ConvertedType The type that will be produced after applying
+/// this conversion.
+bool Sema::isObjCWritebackConversion(QualType FromType, QualType ToType,
+ QualType &ConvertedType) {
+ if (!getLangOptions().ObjCAutoRefCount ||
+ Context.hasSameUnqualifiedType(FromType, ToType))
+ return false;
+
+ // Parameter must be a pointer to __autoreleasing (with no other qualifiers).
+ QualType ToPointee;
+ if (const PointerType *ToPointer = ToType->getAs<PointerType>())
+ ToPointee = ToPointer->getPointeeType();
+ else
+ return false;
+
+ Qualifiers ToQuals = ToPointee.getQualifiers();
+ if (!ToPointee->isObjCLifetimeType() ||
+ ToQuals.getObjCLifetime() != Qualifiers::OCL_Autoreleasing ||
+ !ToQuals.withoutObjCGLifetime().empty())
+ return false;
+
+ // Argument must be a pointer to __strong to __weak.
+ QualType FromPointee;
+ if (const PointerType *FromPointer = FromType->getAs<PointerType>())
+ FromPointee = FromPointer->getPointeeType();
+ else
+ return false;
+
+ Qualifiers FromQuals = FromPointee.getQualifiers();
+ if (!FromPointee->isObjCLifetimeType() ||
+ (FromQuals.getObjCLifetime() != Qualifiers::OCL_Strong &&
+ FromQuals.getObjCLifetime() != Qualifiers::OCL_Weak))
+ return false;
+
+ // Make sure that we have compatible qualifiers.
+ FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing);
+ if (!ToQuals.compatiblyIncludes(FromQuals))
+ return false;
+
+ // Remove qualifiers from the pointee type we're converting from; they
+ // aren't used in the compatibility check belong, and we'll be adding back
+ // qualifiers (with __autoreleasing) if the compatibility check succeeds.
+ FromPointee = FromPointee.getUnqualifiedType();
+
+ // The unqualified form of the pointee types must be compatible.
+ ToPointee = ToPointee.getUnqualifiedType();
+ bool IncompatibleObjC;
+ if (Context.typesAreCompatible(FromPointee, ToPointee))
+ FromPointee = ToPointee;
+ else if (!isObjCPointerConversion(FromPointee, ToPointee, FromPointee,
+ IncompatibleObjC))
+ return false;
+
+ /// \brief Construct the type we're converting to, which is a pointer to
+ /// __autoreleasing pointee.
+ FromPointee = Context.getQualifiedType(FromPointee, FromQuals);
+ ConvertedType = Context.getPointerType(FromPointee);
+ return true;
+}
+
bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType,
QualType& ConvertedType) {
QualType ToPointeeType;
@@ -2178,12 +2274,17 @@
/// IsQualificationConversion - Determines whether the conversion from
/// an rvalue of type FromType to ToType is a qualification conversion
/// (C++ 4.4).
+///
+/// \param ObjCLifetimeConversion Output parameter that will be set to indicate
+/// when the qualification conversion involves a change in the Objective-C
+/// object lifetime.
bool
Sema::IsQualificationConversion(QualType FromType, QualType ToType,
- bool CStyle) {
+ bool CStyle, bool &ObjCLifetimeConversion) {
FromType = Context.getCanonicalType(FromType);
ToType = Context.getCanonicalType(ToType);
-
+ ObjCLifetimeConversion = false;
+
// If FromType and ToType are the same type, this is not a
// qualification conversion.
if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType())
@@ -2206,6 +2307,21 @@
Qualifiers FromQuals = FromType.getQualifiers();
Qualifiers ToQuals = ToType.getQualifiers();
+ // Objective-C ARC:
+ // Check Objective-C lifetime conversions.
+ if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime() &&
+ UnwrappedAnyPointer) {
+ if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) {
+ ObjCLifetimeConversion = true;
+ FromQuals.removeObjCLifetime();
+ ToQuals.removeObjCLifetime();
+ } else {
+ // Qualification conversions cannot cast between different
+ // Objective-C lifetime qualifiers.
+ return false;
+ }
+ }
+
// Allow addition/removal of GC attributes but not changing GC attributes.
if (FromQuals.getObjCGCAttr() != ToQuals.getObjCGCAttr() &&
(!FromQuals.hasObjCGCAttr() || !ToQuals.hasObjCGCAttr())) {
@@ -2713,6 +2829,15 @@
QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals);
QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals);
if (UnqualT1 == UnqualT2) {
+ // Objective-C++ ARC: If the references refer to objects with different
+ // lifetimes, prefer bindings that don't change lifetime.
+ if (SCS1.ObjCLifetimeConversionBinding !=
+ SCS2.ObjCLifetimeConversionBinding) {
+ return SCS1.ObjCLifetimeConversionBinding
+ ? ImplicitConversionSequence::Worse
+ : ImplicitConversionSequence::Better;
+ }
+
// If the type is an array type, promote the element qualifiers to the
// type for comparison.
if (isa<ArrayType>(T1) && T1Quals)
@@ -2722,7 +2847,7 @@
if (T2.isMoreQualifiedThan(T1))
return ImplicitConversionSequence::Better;
else if (T1.isMoreQualifiedThan(T2))
- return ImplicitConversionSequence::Worse;
+ return ImplicitConversionSequence::Worse;
}
}
@@ -2770,6 +2895,17 @@
ImplicitConversionSequence::CompareKind Result
= ImplicitConversionSequence::Indistinguishable;
+
+ // Objective-C++ ARC:
+ // Prefer qualification conversions not involving a change in lifetime
+ // to qualification conversions that do not change lifetime.
+ if (SCS1.QualificationIncludesObjCLifetime !=
+ SCS2.QualificationIncludesObjCLifetime) {
+ Result = SCS1.QualificationIncludesObjCLifetime
+ ? ImplicitConversionSequence::Worse
+ : ImplicitConversionSequence::Better;
+ }
+
while (S.Context.UnwrapSimilarPointerTypes(T1, T2)) {
// Within each iteration of the loop, we check the qualifiers to
// determine if this still looks like a qualification
@@ -3039,7 +3175,8 @@
Sema::CompareReferenceRelationship(SourceLocation Loc,
QualType OrigT1, QualType OrigT2,
bool &DerivedToBase,
- bool &ObjCConversion) {
+ bool &ObjCConversion,
+ bool &ObjCLifetimeConversion) {
assert(!OrigT1->isReferenceType() &&
"T1 must be the pointee type of the reference type");
assert(!OrigT2->isReferenceType() && "T2 cannot be a reference type");
@@ -3056,6 +3193,7 @@
// T1 is a base class of T2.
DerivedToBase = false;
ObjCConversion = false;
+ ObjCLifetimeConversion = false;
if (UnqualT1 == UnqualT2) {
// Nothing to do.
} else if (!RequireCompleteType(Loc, OrigT2, PDiag()) &&
@@ -3090,9 +3228,16 @@
// qualifiers when performing these computations, so that e.g., an int in
// address space 1 is not reference-compatible with an int in address
// space 2.
+ if (T1Quals.getObjCLifetime() != T2Quals.getObjCLifetime() &&
+ T1Quals.compatiblyIncludesObjCLifetime(T2Quals)) {
+ T1Quals.removeObjCLifetime();
+ T2Quals.removeObjCLifetime();
+ ObjCLifetimeConversion = true;
+ }
+
if (T1Quals == T2Quals)
return Ref_Compatible;
- else if (T1.isMoreQualifiedThan(T2))
+ else if (T1Quals.compatiblyIncludes(T2Quals))
return Ref_Compatible_With_Added_Qualification;
else
return Ref_Related;
@@ -3135,13 +3280,14 @@
if (AllowRvalues) {
bool DerivedToBase = false;
bool ObjCConversion = false;
+ bool ObjCLifetimeConversion = false;
if (!ConvTemplate &&
S.CompareReferenceRelationship(
DeclLoc,
Conv->getConversionType().getNonReferenceType()
.getUnqualifiedType(),
DeclType.getNonReferenceType().getUnqualifiedType(),
- DerivedToBase, ObjCConversion) ==
+ DerivedToBase, ObjCConversion, ObjCLifetimeConversion) ==
Sema::Ref_Incompatible)
continue;
} else {
@@ -3242,10 +3388,11 @@
bool isRValRef = DeclType->isRValueReferenceType();
bool DerivedToBase = false;
bool ObjCConversion = false;
+ bool ObjCLifetimeConversion = false;
Expr::Classification InitCategory = Init->Classify(S.Context);
Sema::ReferenceCompareResult RefRelationship
= S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase,
- ObjCConversion);
+ ObjCConversion, ObjCLifetimeConversion);
// C++0x [dcl.init.ref]p5:
@@ -3283,6 +3430,7 @@
ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
ICS.Standard.BindsToRvalue = false;
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
+ ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion;
ICS.Standard.CopyConstructor = 0;
// Nothing more to do: the inaccessibility/ambiguity check for
@@ -3328,7 +3476,7 @@
// -- If the initializer expression
//
// -- is an xvalue, class prvalue, array prvalue or function
- // lvalue and "cv1T1" is reference-compatible with "cv2 T2", or
+ // lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
if (RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification &&
(InitCategory.isXValue() ||
(InitCategory.isPRValue() && (T2->isRecordType() || T2->isArrayType())) ||
@@ -3356,6 +3504,7 @@
ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
ICS.Standard.BindsToRvalue = InitCategory.isRValue();
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
+ ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion;
ICS.Standard.CopyConstructor = 0;
return ICS;
}
@@ -3398,7 +3547,17 @@
// we would be reference-compatible or reference-compatible with
// added qualification. But that wasn't the case, so the reference
// initialization fails.
- return ICS;
+ //
+ // Note that we only want to check address spaces and cvr-qualifiers here.
+ // ObjC GC and lifetime qualifiers aren't important.
+ Qualifiers T1Quals = T1.getQualifiers();
+ Qualifiers T2Quals = T2.getQualifiers();
+ T1Quals.removeObjCGCAttr();
+ T1Quals.removeObjCLifetime();
+ T2Quals.removeObjCGCAttr();
+ T2Quals.removeObjCLifetime();
+ if (!T1Quals.compatiblyIncludes(T2Quals))
+ return ICS;
}
// If at least one of the types is a class type, the types are not
@@ -3429,7 +3588,8 @@
ICS = TryImplicitConversion(S, Init, T1, SuppressUserConversions,
/*AllowExplicit=*/false,
/*InOverloadResolution=*/false,
- /*CStyle=*/false);
+ /*CStyle=*/false,
+ /*AllowObjCWritebackConversion=*/false);
// Of course, that's still a reference binding.
if (ICS.isStandard()) {
@@ -3438,12 +3598,14 @@
ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
ICS.Standard.BindsToRvalue = true;
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
+ ICS.Standard.ObjCLifetimeConversionBinding = false;
} else if (ICS.isUserDefined()) {
ICS.UserDefined.After.ReferenceBinding = true;
ICS.Standard.IsLvalueReference = !isRValRef;
ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
ICS.Standard.BindsToRvalue = true;
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
+ ICS.Standard.ObjCLifetimeConversionBinding = false;
}
return ICS;
@@ -3458,7 +3620,8 @@
static ImplicitConversionSequence
TryCopyInitialization(Sema &S, Expr *From, QualType ToType,
bool SuppressUserConversions,
- bool InOverloadResolution) {
+ bool InOverloadResolution,
+ bool AllowObjCWritebackConversion) {
if (ToType->isReferenceType())
return TryReferenceInit(S, From, ToType,
/*FIXME:*/From->getLocStart(),
@@ -3469,7 +3632,8 @@
SuppressUserConversions,
/*AllowExplicit=*/false,
InOverloadResolution,
- /*CStyle=*/false);
+ /*CStyle=*/false,
+ AllowObjCWritebackConversion);
}
/// TryObjectArgumentInitialization - Try to initialize the object
@@ -3659,7 +3823,8 @@
/*SuppressUserConversions=*/false,
/*AllowExplicit=*/true,
/*InOverloadResolution=*/false,
- /*CStyle=*/false);
+ /*CStyle=*/false,
+ /*AllowObjCWritebackConversion=*/false);
}
/// PerformContextuallyConvertToBool - Perform a contextual conversion
@@ -3686,7 +3851,8 @@
/*SuppressUserConversions=*/false,
/*AllowExplicit=*/true,
/*InOverloadResolution=*/false,
- /*CStyle=*/false);
+ /*CStyle=*/false,
+ /*AllowObjCWritebackConversion=*/false);
}
/// PerformContextuallyConvertToObjCId - Perform a contextual conversion
@@ -3980,7 +4146,9 @@
Candidate.Conversions[ArgIdx]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
SuppressUserConversions,
- /*InOverloadResolution=*/true);
+ /*InOverloadResolution=*/true,
+ /*AllowObjCWritebackConversion=*/
+ getLangOptions().ObjCAutoRefCount);
if (Candidate.Conversions[ArgIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -4153,7 +4321,9 @@
Candidate.Conversions[ArgIdx + 1]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
SuppressUserConversions,
- /*InOverloadResolution=*/true);
+ /*InOverloadResolution=*/true,
+ /*AllowObjCWritebackConversion=*/
+ getLangOptions().ObjCAutoRefCount);
if (Candidate.Conversions[ArgIdx + 1].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -4374,7 +4544,8 @@
ImplicitConversionSequence ICS =
TryCopyInitialization(*this, &Call, ToType,
/*SuppressUserConversions=*/true,
- /*InOverloadResolution=*/false);
+ /*InOverloadResolution=*/false,
+ /*AllowObjCWritebackConversion=*/false);
switch (ICS.getKind()) {
case ImplicitConversionSequence::StandardConversion:
@@ -4544,7 +4715,9 @@
Candidate.Conversions[ArgIdx + 1]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
/*SuppressUserConversions=*/false,
- /*InOverloadResolution=*/false);
+ /*InOverloadResolution=*/false,
+ /*AllowObjCWritebackConversion=*/
+ getLangOptions().ObjCAutoRefCount);
if (Candidate.Conversions[ArgIdx + 1].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -4662,7 +4835,9 @@
Candidate.Conversions[ArgIdx]
= TryCopyInitialization(*this, Args[ArgIdx], ParamTys[ArgIdx],
ArgIdx == 0 && IsAssignmentOperator,
- /*InOverloadResolution=*/false);
+ /*InOverloadResolution=*/false,
+ /*AllowObjCWritebackConversion=*/
+ getLangOptions().ObjCAutoRefCount);
}
if (Candidate.Conversions[ArgIdx].isBad()) {
Candidate.Viable = false;
@@ -6619,6 +6794,17 @@
return;
}
+ if (FromQs.getObjCLifetime() != ToQs.getObjCLifetime()) {
+ S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_lifetime)
+ << (unsigned) FnKind << FnDesc
+ << (FromExpr ? FromExpr->getSourceRange() : SourceRange())
+ << FromTy
+ << FromQs.getObjCLifetime() << ToQs.getObjCLifetime()
+ << (unsigned) isObjectArgument << I+1;
+ MaybeEmitInheritedConstructorNote(S, Fn);
+ return;
+ }
+
if (FromQs.getObjCGCAttr() != ToQs.getObjCGCAttr()) {
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_gc)
<< (unsigned) FnKind << FnDesc
@@ -7152,7 +7338,9 @@
= TryCopyInitialization(S, Args[ConvIdx],
Cand->BuiltinTypes.ParamTypes[ConvIdx],
SuppressUserConversions,
- /*InOverloadResolution*/ true);
+ /*InOverloadResolution*/ true,
+ /*AllowObjCWritebackConversion=*/
+ S.getLangOptions().ObjCAutoRefCount);
return;
}
@@ -7163,7 +7351,9 @@
Cand->Conversions[ConvIdx]
= TryCopyInitialization(S, Args[ArgIdx], Proto->getArgType(ArgIdx),
SuppressUserConversions,
- /*InOverloadResolution=*/true);
+ /*InOverloadResolution=*/true,
+ /*AllowObjCWritebackConversion=*/
+ S.getLangOptions().ObjCAutoRefCount);
else
Cand->Conversions[ConvIdx].setEllipsis();
}
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index d7c0a54..18e5f7e 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -66,8 +66,29 @@
// If we have an invalid decl, just return.
if (DG.isNull() || !DG.isSingleDecl()) return;
+ VarDecl *var = cast<VarDecl>(DG.getSingleDecl());
+
// suppress any potential 'unused variable' warning.
- DG.getSingleDecl()->setUsed();
+ 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();
+
+ // 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);
+ }
+ }
}
void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
@@ -114,6 +135,10 @@
}
}
} else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
+ if (getLangOptions().ObjCAutoRefCount && ME->isDelegateInitCall()) {
+ Diag(Loc, diag::err_arc_unused_init_message) << R1;
+ return;
+ }
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD && MD->getAttr<WarnUnusedResultAttr>()) {
Diag(Loc, diag::warn_unused_call) << R1 << R2 << "warn_unused_result";
@@ -951,14 +976,13 @@
return StmtError(Diag((*DS->decl_begin())->getLocation(),
diag::err_toomany_element_decls));
- Decl *D = DS->getSingleDecl();
- FirstType = cast<ValueDecl>(D)->getType();
+ VarDecl *D = cast<VarDecl>(DS->getSingleDecl());
+ FirstType = D->getType();
// C99 6.8.5p3: The declaration part of a 'for' statement shall only
// declare identifiers for objects having storage class 'auto' or
// 'register'.
- VarDecl *VD = cast<VarDecl>(D);
- if (VD->isLocalVarDecl() && !VD->hasLocalStorage())
- return StmtError(Diag(VD->getLocation(),
+ if (!D->hasLocalStorage())
+ return StmtError(Diag(D->getLocation(),
diag::err_non_variable_decl_in_for));
} else {
Expr *FirstE = cast<Expr>(First);
@@ -1047,6 +1071,13 @@
Decl->setTypeSourceInfo(InitTSI);
Decl->setType(InitTSI->getType());
+ // In ARC, infer lifetime.
+ // FIXME: ARC may want to turn this into 'const __unsafe_unretained' if
+ // we're doing the equivalent of fast iteration.
+ if (SemaRef.getLangOptions().ObjCAutoRefCount &&
+ SemaRef.inferObjCARCLifetime(Decl))
+ Decl->setInvalidDecl();
+
SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false,
/*TypeMayContainAuto=*/false);
SemaRef.FinalizeDeclaration(Decl);
@@ -1797,7 +1828,7 @@
if (getLangOptions().CPlusPlus && FnRetType->isRecordType() &&
!CurContext->isDependentContext())
FunctionScopes.back()->Returns.push_back(Result);
-
+
return Owned(Result);
}
@@ -2179,6 +2210,12 @@
HandlerBlock));
}
+StmtResult
+Sema::ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, Stmt *Body) {
+ getCurFunction()->setHasBranchProtectedScope();
+ return Owned(new (Context) ObjCAutoreleasePoolStmt(AtLoc, Body));
+}
+
namespace {
class TypeWithHandler {
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index d458050..b8caad8 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -3503,9 +3503,11 @@
return true;
}
+ bool ObjCLifetimeConversion;
if (ParamType->isPointerType() &&
!ParamType->getAs<PointerType>()->getPointeeType()->isFunctionType() &&
- S.IsQualificationConversion(ArgType, ParamType, false)) {
+ S.IsQualificationConversion(ArgType, ParamType, false,
+ ObjCLifetimeConversion)) {
// For pointer-to-object types, qualification conversions are
// permitted.
} else {
@@ -3865,8 +3867,9 @@
return Owned(Arg);
}
+ bool ObjCLifetimeConversion;
if (IsQualificationConversion(ArgType, ParamType.getNonReferenceType(),
- false)) {
+ false, ObjCLifetimeConversion)) {
Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take();
} else if (!Context.hasSameUnqualifiedType(ArgType,
ParamType.getNonReferenceType())) {
@@ -3933,9 +3936,11 @@
// member, qualification conversions (4.4) are applied.
assert(ParamType->isMemberPointerType() && "Only pointers to members remain");
+ bool ObjCLifetimeConversion;
if (Context.hasSameUnqualifiedType(ParamType, ArgType)) {
// Types match exactly: nothing more to do here.
- } else if (IsQualificationConversion(ArgType, ParamType, false)) {
+ } else if (IsQualificationConversion(ArgType, ParamType, false,
+ ObjCLifetimeConversion)) {
Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take();
} else {
// We can't perform this conversion.
@@ -4043,8 +4048,10 @@
// We might need to perform a trailing qualification conversion, since
// the element type on the parameter could be more qualified than the
// element type in the expression we constructed.
+ bool ObjCLifetimeConversion;
if (IsQualificationConversion(((Expr*) RefExpr.get())->getType(),
- ParamType.getUnqualifiedType(), false))
+ ParamType.getUnqualifiedType(), false,
+ ObjCLifetimeConversion))
RefExpr = ImpCastExprToType(RefExpr.take(), ParamType.getUnqualifiedType(), CK_NoOp);
assert(!RefExpr.isInvalid() &&
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index 9011cdf..359c931 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -819,6 +819,11 @@
ParamQs.hasAddressSpace())
return true;
+ // Mismatched (but not missing) Objective-C lifetime qualifiers.
+ if (ParamQs.getObjCLifetime() != ArgQs.getObjCLifetime() &&
+ ParamQs.hasObjCLifetime())
+ return true;
+
// CVR qualifier superset.
return (ParamQs.getCVRQualifiers() != ArgQs.getCVRQualifiers()) &&
((ParamQs.getCVRQualifiers() | ArgQs.getCVRQualifiers())
@@ -1009,6 +1014,8 @@
DeducedQs.removeObjCGCAttr();
if (ParamQs.hasAddressSpace())
DeducedQs.removeAddressSpace();
+ if (ParamQs.hasObjCLifetime())
+ DeducedQs.removeObjCLifetime();
DeducedType = S.Context.getQualifiedType(DeducedType.getUnqualifiedType(),
DeducedQs);
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index ef3c262..bfffb9e 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -26,6 +26,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/DelayedDiagnostic.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang;
@@ -110,7 +111,8 @@
// objc_gc applies to Objective-C pointers or, otherwise, to the
// smallest available pointer type (i.e. 'void*' in 'void**').
#define OBJC_POINTER_TYPE_ATTRS_CASELIST \
- case AttributeList::AT_objc_gc
+ case AttributeList::AT_objc_gc: \
+ case AttributeList::AT_objc_lifetime
// Function type attributes.
#define FUNCTION_TYPE_ATTRS_CASELIST \
@@ -295,11 +297,15 @@
static bool handleObjCGCTypeAttr(TypeProcessingState &state,
AttributeList &attr, QualType &type);
+static bool handleObjCLifetimeTypeAttr(TypeProcessingState &state,
+ AttributeList &attr, QualType &type);
+
static bool handleObjCPointerTypeAttr(TypeProcessingState &state,
AttributeList &attr, QualType &type) {
- // Right now, we have exactly one of these attributes: objc_gc.
- assert(attr.getKind() == AttributeList::AT_objc_gc);
- return handleObjCGCTypeAttr(state, attr, type);
+ if (attr.getKind() == AttributeList::AT_objc_gc)
+ return handleObjCGCTypeAttr(state, attr, type);
+ assert(attr.getKind() == AttributeList::AT_objc_lifetime);
+ return handleObjCLifetimeTypeAttr(state, attr, type);
}
/// Given that an objc_gc attribute was written somewhere on a
@@ -447,7 +453,12 @@
return true;
}
- return handleFunctionTypeAttr(state, attr, declSpecType);
+ if (handleFunctionTypeAttr(state, attr, declSpecType)) {
+ spliceAttrOutOfList(attr, attrList);
+ return true;
+ }
+
+ return false;
}
/// A function type attribute was written in the decl spec. Try to
@@ -512,6 +523,11 @@
distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType);
break;
+ case AttributeList::AT_ns_returns_retained:
+ if (!state.getSema().getLangOptions().ObjCAutoRefCount)
+ break;
+ // fallthrough
+
FUNCTION_TYPE_ATTRS_CASELIST:
distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType);
break;
@@ -1017,6 +1033,51 @@
return Context.getParenType(T);
}
+/// Given that we're building a pointer or reference to the given
+static QualType inferARCLifetimeForPointee(Sema &S, QualType type,
+ SourceLocation loc,
+ bool isReference) {
+ // Bail out if retention is unrequired or already specified.
+ if (!type->isObjCLifetimeType() ||
+ type.getObjCLifetime() != Qualifiers::OCL_None)
+ return type;
+
+ Qualifiers::ObjCLifetime implicitLifetime = Qualifiers::OCL_None;
+
+ // If the object type is const-qualified, we can safely use
+ // __unsafe_unretained. This is safe (because there are no read
+ // barriers), and it'll be safe to coerce anything but __weak* to
+ // the resulting type.
+ if (type.isConstQualified()) {
+ implicitLifetime = Qualifiers::OCL_ExplicitNone;
+
+ // Otherwise, check whether the static type does not require
+ // retaining. This currently only triggers for Class (possibly
+ // protocol-qualifed, and arrays thereof).
+ } else if (type->isObjCARCImplicitlyUnretainedType()) {
+ implicitLifetime = Qualifiers::OCL_ExplicitNone;
+
+ // If that failed, give an error and recover using __autoreleasing.
+ } else {
+ // These types can show up in private ivars in system headers, so
+ // we need this to not be an error in those cases. Instead we
+ // want to delay.
+ if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
+ S.DelayedDiagnostics.add(
+ sema::DelayedDiagnostic::makeForbiddenType(loc,
+ diag::err_arc_indirect_no_lifetime, type, isReference));
+ } else {
+ S.Diag(loc, diag::err_arc_indirect_no_lifetime) << type << isReference;
+ }
+ implicitLifetime = Qualifiers::OCL_Autoreleasing;
+ }
+ assert(implicitLifetime && "didn't infer any lifetime!");
+
+ Qualifiers qs;
+ qs.addObjCLifetime(implicitLifetime);
+ return S.Context.getQualifiedType(type, qs);
+}
+
/// \brief Build a pointer type.
///
/// \param T The type to which we'll be building a pointer.
@@ -1041,6 +1102,10 @@
assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType");
+ // In ARC, it is forbidden to build pointers to unqualified pointers.
+ if (getLangOptions().ObjCAutoRefCount)
+ T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ false);
+
// Build the pointer type.
return Context.getPointerType(T);
}
@@ -1094,6 +1159,10 @@
return QualType();
}
+ // In ARC, it is forbidden to build references to unqualified pointers.
+ if (getLangOptions().ObjCAutoRefCount)
+ T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ true);
+
// Handle restrict on references.
if (LValueRef)
return Context.getLValueReferenceType(T, SpelledAsLValue);
@@ -1266,10 +1335,12 @@
if (!getLangOptions().C99) {
if (T->isVariableArrayType()) {
// Prohibit the use of non-POD types in VLAs.
+ QualType BaseT = Context.getBaseElementType(T);
if (!T->isDependentType() &&
- !Context.getBaseElementType(T)->isPODType()) {
+ !BaseT.isPODType(Context) &&
+ !BaseT->isObjCLifetimeType()) {
Diag(Loc, diag::err_vla_non_pod)
- << Context.getBaseElementType(T);
+ << BaseT;
return QualType();
}
// Prohibit the use of VLAs during template argument deduction.
@@ -1490,6 +1561,111 @@
return QT;
}
+/// Given that this is the declaration of a parameter under ARC,
+/// attempt to infer attributes and such for pointer-to-whatever
+/// types.
+static void inferARCWriteback(TypeProcessingState &state,
+ QualType &declSpecType) {
+ Sema &S = state.getSema();
+ Declarator &declarator = state.getDeclarator();
+
+ // TODO: should we care about decl qualifiers?
+
+ // Check whether the declarator has the expected form. We walk
+ // from the inside out in order to make the block logic work.
+ unsigned outermostPointerIndex = 0;
+ bool isBlockPointer = false;
+ unsigned numPointers = 0;
+ for (unsigned i = 0, e = declarator.getNumTypeObjects(); i != e; ++i) {
+ unsigned chunkIndex = i;
+ DeclaratorChunk &chunk = declarator.getTypeObject(chunkIndex);
+ switch (chunk.Kind) {
+ case DeclaratorChunk::Paren:
+ // Ignore parens.
+ break;
+
+ case DeclaratorChunk::Reference:
+ case DeclaratorChunk::Pointer:
+ // Count the number of pointers. Treat references
+ // interchangeably as pointers; if they're mis-ordered, normal
+ // type building will discover that.
+ outermostPointerIndex = chunkIndex;
+ numPointers++;
+ break;
+
+ case DeclaratorChunk::BlockPointer:
+ // If we have a pointer to block pointer, that's an acceptable
+ // indirect reference; anything else is not an application of
+ // the rules.
+ if (numPointers != 1) return;
+ numPointers++;
+ outermostPointerIndex = chunkIndex;
+ isBlockPointer = true;
+
+ // We don't care about pointer structure in return values here.
+ goto done;
+
+ case DeclaratorChunk::Array: // suppress if written (id[])?
+ case DeclaratorChunk::Function:
+ case DeclaratorChunk::MemberPointer:
+ return;
+ }
+ }
+ done:
+
+ // If we have *one* pointer, then we want to throw the qualifier on
+ // the declaration-specifiers, which means that it needs to be a
+ // retainable object type.
+ if (numPointers == 1) {
+ // If it's not a retainable object type, the rule doesn't apply.
+ if (!declSpecType->isObjCRetainableType()) return;
+
+ // If it already has lifetime, don't do anything.
+ if (declSpecType.getObjCLifetime()) return;
+
+ // Otherwise, modify the type in-place.
+ Qualifiers qs;
+
+ if (declSpecType->isObjCARCImplicitlyUnretainedType())
+ qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone);
+ else
+ qs.addObjCLifetime(Qualifiers::OCL_Autoreleasing);
+ declSpecType = S.Context.getQualifiedType(declSpecType, qs);
+
+ // If we have *two* pointers, then we want to throw the qualifier on
+ // the outermost pointer.
+ } else if (numPointers == 2) {
+ // If we don't have a block pointer, we need to check whether the
+ // declaration-specifiers gave us something that will turn into a
+ // retainable object pointer after we slap the first pointer on it.
+ if (!isBlockPointer && !declSpecType->isObjCObjectType())
+ return;
+
+ // Look for an explicit lifetime attribute there.
+ DeclaratorChunk &chunk = declarator.getTypeObject(outermostPointerIndex);
+ assert(chunk.Kind == DeclaratorChunk::Pointer ||
+ chunk.Kind == DeclaratorChunk::BlockPointer);
+ for (const AttributeList *attr = chunk.getAttrs(); attr;
+ attr = attr->getNext())
+ if (attr->getKind() == AttributeList::AT_objc_lifetime)
+ return;
+
+ // If there wasn't one, add one (with an invalid source location
+ // so that we don't make an AttributedType for it).
+ AttributeList *attr = declarator.getAttributePool()
+ .create(&S.Context.Idents.get("objc_lifetime"), SourceLocation(),
+ /*scope*/ 0, SourceLocation(),
+ &S.Context.Idents.get("autoreleasing"), SourceLocation(),
+ /*args*/ 0, 0,
+ /*declspec*/ false, /*C++0x*/ false);
+ spliceAttrIntoList(*attr, chunk.getAttrListRef());
+
+ // Any other number of pointers/references does not trigger the rule.
+ } else return;
+
+ // TODO: mark whether we did this inference?
+}
+
static void DiagnoseIgnoredQualifiers(unsigned Quals,
SourceLocation ConstQualLoc,
SourceLocation VolatileQualLoc,
@@ -1599,6 +1775,9 @@
if (D.getAttributes())
distributeTypeAttrsFromDeclarator(state, T);
+ if (D.isPrototypeContext() && getLangOptions().ObjCAutoRefCount)
+ inferARCWriteback(state, T);
+
// C++0x [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context.
// In C++0x, a function declarator using 'auto' must have a trailing return
// type (this is checked later) and we can skip this. In other languages
@@ -1922,6 +2101,10 @@
llvm::SmallVector<QualType, 16> ArgTys;
ArgTys.reserve(FTI.NumArgs);
+ llvm::SmallVector<bool, 16> ConsumedArguments;
+ ConsumedArguments.reserve(FTI.NumArgs);
+ bool HasAnyConsumedArguments = false;
+
for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) {
ParmVarDecl *Param = cast<ParmVarDecl>(FTI.ArgInfo[i].Param);
QualType ArgTy = Param->getType();
@@ -1967,9 +2150,18 @@
}
}
+ if (getLangOptions().ObjCAutoRefCount) {
+ bool Consumed = Param->hasAttr<NSConsumedAttr>();
+ ConsumedArguments.push_back(Consumed);
+ HasAnyConsumedArguments |= Consumed;
+ }
+
ArgTys.push_back(ArgTy);
}
+ if (HasAnyConsumedArguments)
+ EPI.ConsumedArguments = ConsumedArguments.data();
+
llvm::SmallVector<QualType, 4> Exceptions;
EPI.ExceptionSpecType = FTI.getExceptionSpecType();
if (FTI.getExceptionSpecType() == EST_Dynamic) {
@@ -2282,6 +2474,8 @@
return AttributeList::AT_neon_polyvector_type;
case AttributedType::attr_objc_gc:
return AttributeList::AT_objc_gc;
+ case AttributedType::attr_objc_lifetime:
+ return AttributeList::AT_objc_lifetime;
case AttributedType::attr_noreturn:
return AttributeList::AT_noreturn;
case AttributedType::attr_cdecl:
@@ -2512,6 +2706,9 @@
llvm_unreachable("qualified type locs not expected here!");
}
+ void VisitAttributedTypeLoc(AttributedTypeLoc TL) {
+ fillAttributedTypeLoc(TL, Chunk.getAttrs());
+ }
void VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) {
assert(Chunk.Kind == DeclaratorChunk::BlockPointer);
TL.setCaretLoc(Chunk.Loc);
@@ -2707,8 +2904,6 @@
return CreateParsedType(T, TInfo);
}
-
-
//===----------------------------------------------------------------------===//
// Type Attribute Processing
//===----------------------------------------------------------------------===//
@@ -2767,6 +2962,83 @@
Type = S.Context.getAddrSpaceQualType(Type, ASIdx);
}
+/// handleObjCLifetimeTypeAttr - Process an objc_lifetime
+/// attribute on the specified type.
+///
+/// Returns 'true' if the attribute was handled.
+static bool handleObjCLifetimeTypeAttr(TypeProcessingState &state,
+ AttributeList &attr,
+ QualType &type) {
+ if (!type->isObjCRetainableType() && !type->isDependentType())
+ return false;
+
+ Sema &S = state.getSema();
+
+ if (type.getQualifiers().getObjCLifetime()) {
+ S.Diag(attr.getLoc(), diag::err_attr_objc_lifetime_redundant)
+ << type;
+ return true;
+ }
+
+ if (!attr.getParameterName()) {
+ S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string)
+ << "objc_lifetime" << 1;
+ attr.setInvalid();
+ return true;
+ }
+
+ Qualifiers::ObjCLifetime lifetime;
+ if (attr.getParameterName()->isStr("none"))
+ lifetime = Qualifiers::OCL_ExplicitNone;
+ else if (attr.getParameterName()->isStr("strong"))
+ lifetime = Qualifiers::OCL_Strong;
+ else if (attr.getParameterName()->isStr("weak"))
+ lifetime = Qualifiers::OCL_Weak;
+ else if (attr.getParameterName()->isStr("autoreleasing"))
+ lifetime = Qualifiers::OCL_Autoreleasing;
+ else {
+ S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported)
+ << "objc_lifetime" << attr.getParameterName();
+ attr.setInvalid();
+ return true;
+ }
+
+ // Consume lifetime attributes without further comment outside of
+ // ARC mode.
+ if (!S.getLangOptions().ObjCAutoRefCount)
+ return true;
+
+ Qualifiers qs;
+ qs.setObjCLifetime(lifetime);
+ QualType origType = type;
+ type = S.Context.getQualifiedType(type, qs);
+
+ // If we have a valid source location for the attribute, use an
+ // AttributedType instead.
+ if (attr.getLoc().isValid())
+ type = S.Context.getAttributedType(AttributedType::attr_objc_lifetime,
+ origType, type);
+
+ // Forbid __weak if we don't have a runtime.
+ if (lifetime == Qualifiers::OCL_Weak &&
+ S.getLangOptions().ObjCNoAutoRefCountRuntime) {
+
+ // Actually, delay this until we know what we're parsing.
+ if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
+ S.DelayedDiagnostics.add(
+ sema::DelayedDiagnostic::makeForbiddenType(attr.getLoc(),
+ diag::err_arc_weak_no_runtime, type, /*ignored*/ 0));
+ } else {
+ S.Diag(attr.getLoc(), diag::err_arc_weak_no_runtime);
+ }
+
+ attr.setInvalid();
+ return true;
+ }
+
+ return true;
+}
+
/// handleObjCGCTypeAttr - Process the __attribute__((objc_gc)) type
/// attribute on the specified type. Returns true to indicate that
/// the attribute was handled, false to indicate that the type does
@@ -2977,6 +3249,23 @@
return true;
}
+ // ns_returns_retained is not always a type attribute, but if we got
+ // here, we're treating it as one right now.
+ if (attr.getKind() == AttributeList::AT_ns_returns_retained) {
+ assert(S.getLangOptions().ObjCAutoRefCount &&
+ "ns_returns_retained treated as type attribute in non-ARC");
+ if (attr.getNumArgs()) return true;
+
+ // Delay if this is not a function type.
+ if (!unwrapped.isFunctionType())
+ return false;
+
+ FunctionType::ExtInfo EI
+ = unwrapped.get()->getExtInfo().withProducesResult(true);
+ type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
+ return true;
+ }
+
if (attr.getKind() == AttributeList::AT_regparm) {
unsigned value;
if (S.CheckRegparmAttr(attr, value))
@@ -3288,11 +3577,15 @@
VectorType::NeonPolyVector,
"neon_polyvector_type");
break;
-
case AttributeList::AT_opencl_image_access:
HandleOpenCLImageAccessAttribute(type, attr, state.getSema());
break;
+ case AttributeList::AT_ns_returns_retained:
+ if (!state.getSema().getLangOptions().ObjCAutoRefCount)
+ break;
+ // fallthrough into the function attrs
+
FUNCTION_TYPE_ATTRS_CASELIST:
// Never process function type attributes as part of the
// declaration-specifiers.
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index ff2e46a..ba4ee34 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -1192,6 +1192,16 @@
Body);
}
+ /// \brief Build a new Objective-C @autoreleasepool statement.
+ ///
+ /// By default, performs semantic analysis to build the new statement.
+ /// Subclasses may override this routine to provide different behavior.
+ StmtResult RebuildObjCAutoreleasePoolStmt(SourceLocation AtLoc,
+ Stmt *Body) {
+ return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body);
+ }
+
+
/// \brief Build a new Objective-C fast enumeration statement.
///
/// By default, performs semantic analysis to build the new statement.
@@ -3162,6 +3172,13 @@
if (Result->isFunctionType() || Result->isReferenceType())
return Result;
+ // Suppress Objective-C lifetime qualifiers if they don't make sense for the
+ // resulting type or if the resulting type already has one.
+ if (Quals.hasObjCLifetime() &&
+ (Result.getObjCLifetime() ||
+ (!Result->isObjCLifetimeType() && !Result->isDependentType())))
+ Quals.removeObjCLifetime();
+
if (!Quals.empty()) {
Result = SemaRef.BuildQualifiedType(Result, T.getBeginLoc(), Quals);
TLB.push<QualifiedTypeLoc>(Result);
@@ -3333,7 +3350,11 @@
if (Result.isNull())
return QualType();
}
-
+
+ // Objective-C ARC can add lifetime qualifiers to the type that we're
+ // pointing to.
+ TLB.TypeWasModifiedSafely(Result->getPointeeType());
+
PointerTypeLoc NewT = TLB.push<PointerTypeLoc>(Result);
NewT.setSigilLoc(TL.getSigilLoc());
return Result;
@@ -3387,6 +3408,11 @@
return QualType();
}
+ // Objective-C ARC can add lifetime qualifiers to the type that we're
+ // referring to.
+ TLB.TypeWasModifiedSafely(
+ Result->getAs<ReferenceType>()->getPointeeTypeAsWritten());
+
// r-value references can be rebuilt as l-value references.
ReferenceTypeLoc NewTL;
if (isa<LValueReferenceType>(Result))
@@ -5435,6 +5461,25 @@
template<typename Derived>
StmtResult
+TreeTransform<Derived>::TransformObjCAutoreleasePoolStmt(
+ ObjCAutoreleasePoolStmt *S) {
+ // Transform the body.
+ StmtResult Body = getDerived().TransformStmt(S->getSubStmt());
+ if (Body.isInvalid())
+ return StmtError();
+
+ // If nothing changed, just retain this statement.
+ if (!getDerived().AlwaysRebuild() &&
+ Body.get() == S->getSubStmt())
+ return SemaRef.Owned(S);
+
+ // Build a new statement.
+ return getDerived().RebuildObjCAutoreleasePoolStmt(
+ S->getAtLoc(), Body.get());
+}
+
+template<typename Derived>
+StmtResult
TreeTransform<Derived>::TransformObjCForCollectionStmt(
ObjCForCollectionStmt *S) {
// Transform the element statement.
@@ -7609,6 +7654,43 @@
}
template<typename Derived>
+ExprResult TreeTransform<Derived>::
+TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) {
+ ExprResult result = getDerived().TransformExpr(E->getSubExpr());
+ if (result.isInvalid()) return ExprError();
+ Expr *subExpr = result.take();
+
+ if (!getDerived().AlwaysRebuild() &&
+ subExpr == E->getSubExpr())
+ return SemaRef.Owned(E);
+
+ return SemaRef.Owned(new(SemaRef.Context)
+ ObjCIndirectCopyRestoreExpr(subExpr, E->getType(), E->shouldCopy()));
+}
+
+template<typename Derived>
+ExprResult TreeTransform<Derived>::
+TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) {
+ TypeSourceInfo *TSInfo
+ = getDerived().TransformType(E->getTypeInfoAsWritten());
+ if (!TSInfo)
+ return ExprError();
+
+ ExprResult Result = getDerived().TransformExpr(E->getSubExpr());
+ if (Result.isInvalid())
+ return ExprError();
+
+ if (!getDerived().AlwaysRebuild() &&
+ TSInfo == E->getTypeInfoAsWritten() &&
+ Result.get() == E->getSubExpr())
+ return SemaRef.Owned(E);
+
+ return SemaRef.BuildObjCBridgedCast(E->getLParenLoc(), E->getBridgeKind(),
+ E->getBridgeKeywordLoc(), TSInfo,
+ Result.get());
+}
+
+template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) {
// Transform arguments.
diff --git a/lib/Sema/TypeLocBuilder.h b/lib/Sema/TypeLocBuilder.h
index 3570737..f0944f1 100644
--- a/lib/Sema/TypeLocBuilder.h
+++ b/lib/Sema/TypeLocBuilder.h
@@ -87,6 +87,15 @@
Index = Capacity;
}
+ /// \brief Tell the TypeLocBuilder that the type it is storing has been
+ /// modified in some safe way that doesn't affect type-location information.
+ void TypeWasModifiedSafely(QualType T) {
+#ifndef NDEBUG
+ assert(T.getLocalUnqualifiedType() == LastTy.getLocalUnqualifiedType());
+ LastTy = T;
+#endif
+ }
+
/// Pushes space for a new TypeLoc of the given type. Invalidates
/// any TypeLocs previously retrieved from this builder.
template <class TyLocType> TyLocType push(QualType T) {