diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp | 743 |
1 files changed, 413 insertions, 330 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp index f139c83..1cb84e4 100644 --- a/contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp +++ b/contrib/llvm/tools/clang/lib/Sema/SemaObjCProperty.cpp @@ -61,8 +61,10 @@ static Qualifiers::ObjCLifetime getImpliedARCOwnership( return Qualifiers::OCL_None; } -/// Check the internal consistency of a property declaration. -static void checkARCPropertyDecl(Sema &S, ObjCPropertyDecl *property) { +/// Check the internal consistency of a property declaration with +/// an explicit ownership qualifier. +static void checkPropertyDeclWithOwnership(Sema &S, + ObjCPropertyDecl *property) { if (property->isInvalidDecl()) return; ObjCPropertyDecl::PropertyAttributeKind propertyKind @@ -70,8 +72,7 @@ static void checkARCPropertyDecl(Sema &S, ObjCPropertyDecl *property) { Qualifiers::ObjCLifetime propertyLifetime = property->getType().getObjCLifetime(); - // Nothing to do if we don't have a lifetime. - if (propertyLifetime == Qualifiers::OCL_None) return; + assert(propertyLifetime != Qualifiers::OCL_None); Qualifiers::ObjCLifetime expectedLifetime = getImpliedARCOwnership(propertyKind, property->getType()); @@ -127,32 +128,71 @@ CheckPropertyAgainstProtocol(Sema &S, ObjCPropertyDecl *Prop, CheckPropertyAgainstProtocol(S, Prop, P, Known); } +static unsigned deducePropertyOwnershipFromType(Sema &S, QualType T) { + // In GC mode, just look for the __weak qualifier. + if (S.getLangOpts().getGC() != LangOptions::NonGC) { + if (T.isObjCGCWeak()) return ObjCDeclSpec::DQ_PR_weak; + + // In ARC/MRC, look for an explicit ownership qualifier. + // For some reason, this only applies to __weak. + } else if (auto ownership = T.getObjCLifetime()) { + switch (ownership) { + case Qualifiers::OCL_Weak: + return ObjCDeclSpec::DQ_PR_weak; + case Qualifiers::OCL_Strong: + return ObjCDeclSpec::DQ_PR_strong; + case Qualifiers::OCL_ExplicitNone: + return ObjCDeclSpec::DQ_PR_unsafe_unretained; + case Qualifiers::OCL_Autoreleasing: + case Qualifiers::OCL_None: + return 0; + } + llvm_unreachable("bad qualifier"); + } + + return 0; +} + +static const unsigned OwnershipMask = + (ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_weak | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained); + +static unsigned getOwnershipRule(unsigned attr) { + unsigned result = attr & OwnershipMask; + + // From an ownership perspective, assign and unsafe_unretained are + // identical; make sure one also implies the other. + if (result & (ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained)) { + result |= ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained; + } + + return result; +} + Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, SourceLocation LParenLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, Selector GetterSel, Selector SetterSel, - bool *isOverridingProperty, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC) { unsigned Attributes = ODS.getPropertyAttributes(); FD.D.setObjCWeakProperty((Attributes & ObjCDeclSpec::DQ_PR_weak) != 0); TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S); QualType T = TSI->getType(); - Attributes |= deduceWeakPropertyFromType(T); + if (!getOwnershipRule(Attributes)) { + Attributes |= deducePropertyOwnershipFromType(*this, T); + } bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) || // default is readwrite! !(Attributes & ObjCDeclSpec::DQ_PR_readonly)); - // property is defaulted to 'assign' if it is readwrite and is - // not retain or copy - bool isAssign = ((Attributes & ObjCDeclSpec::DQ_PR_assign) || - (isReadWrite && - !(Attributes & ObjCDeclSpec::DQ_PR_retain) && - !(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 ObjCPropertyDecls. ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext); @@ -161,11 +201,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, if (CDecl->IsClassExtension()) { Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc, FD, GetterSel, SetterSel, - isAssign, isReadWrite, + isReadWrite, Attributes, ODS.getPropertyAttributes(), - isOverridingProperty, T, TSI, - MethodImplKind); + T, TSI, MethodImplKind); if (!Res) return nullptr; } @@ -173,7 +212,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, if (!Res) { Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD, - GetterSel, SetterSel, isAssign, isReadWrite, + GetterSel, SetterSel, isReadWrite, Attributes, ODS.getPropertyAttributes(), T, TSI, MethodImplKind); if (lexicalDC) @@ -181,12 +220,13 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, } // Validate the attributes on the @property. - CheckObjCPropertyAttributes(Res, AtLoc, Attributes, + CheckObjCPropertyAttributes(Res, AtLoc, Attributes, (isa<ObjCInterfaceDecl>(ClassDecl) || isa<ObjCProtocolDecl>(ClassDecl))); - if (getLangOpts().ObjCAutoRefCount) - checkARCPropertyDecl(*this, Res); + // Check consistency if the type has explicit ownership qualification. + if (Res->getType().getObjCLifetime()) + checkPropertyDeclWithOwnership(*this, Res); llvm::SmallPtrSet<ObjCProtocolDecl *, 16> KnownProtos; if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(ClassDecl)) { @@ -220,8 +260,12 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, } } } else if (ObjCCategoryDecl *Cat = dyn_cast<ObjCCategoryDecl>(ClassDecl)) { - for (auto *P : Cat->protocols()) - CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); + // We don't check if class extension. Because properties in class extension + // are meant to override some of the attributes and checking has already done + // when property in class extension is constructed. + if (!Cat->IsClassExtension()) + for (auto *P : Cat->protocols()) + CheckPropertyAgainstProtocol(*this, Res, P, KnownProtos); } else { ObjCProtocolDecl *Proto = cast<ObjCProtocolDecl>(ClassDecl); for (auto *P : Proto->protocols()) @@ -293,13 +337,73 @@ static bool LocPropertyAttribute( ASTContext &Context, const char *attrName, } -static unsigned getOwnershipRule(unsigned attr) { - return attr & (ObjCPropertyDecl::OBJC_PR_assign | - ObjCPropertyDecl::OBJC_PR_retain | - ObjCPropertyDecl::OBJC_PR_copy | - ObjCPropertyDecl::OBJC_PR_weak | - ObjCPropertyDecl::OBJC_PR_strong | - ObjCPropertyDecl::OBJC_PR_unsafe_unretained); +/// Check for a mismatch in the atomicity of the given properties. +static void checkAtomicPropertyMismatch(Sema &S, + ObjCPropertyDecl *OldProperty, + ObjCPropertyDecl *NewProperty, + bool PropagateAtomicity) { + // If the atomicity of both matches, we're done. + bool OldIsAtomic = + (OldProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) + == 0; + bool NewIsAtomic = + (NewProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) + == 0; + if (OldIsAtomic == NewIsAtomic) return; + + // Determine whether the given property is readonly and implicitly + // atomic. + auto isImplicitlyReadonlyAtomic = [](ObjCPropertyDecl *Property) -> bool { + // Is it readonly? + auto Attrs = Property->getPropertyAttributes(); + if ((Attrs & ObjCPropertyDecl::OBJC_PR_readonly) == 0) return false; + + // Is it nonatomic? + if (Attrs & ObjCPropertyDecl::OBJC_PR_nonatomic) return false; + + // Was 'atomic' specified directly? + if (Property->getPropertyAttributesAsWritten() & + ObjCPropertyDecl::OBJC_PR_atomic) + return false; + + return true; + }; + + // If we're allowed to propagate atomicity, and the new property did + // not specify atomicity at all, propagate. + const unsigned AtomicityMask = + (ObjCPropertyDecl::OBJC_PR_atomic | ObjCPropertyDecl::OBJC_PR_nonatomic); + if (PropagateAtomicity && + ((NewProperty->getPropertyAttributesAsWritten() & AtomicityMask) == 0)) { + unsigned Attrs = NewProperty->getPropertyAttributes(); + Attrs = Attrs & ~AtomicityMask; + if (OldIsAtomic) + Attrs |= ObjCPropertyDecl::OBJC_PR_atomic; + else + Attrs |= ObjCPropertyDecl::OBJC_PR_nonatomic; + + NewProperty->overwritePropertyAttributes(Attrs); + return; + } + + // One of the properties is atomic; if it's a readonly property, and + // 'atomic' wasn't explicitly specified, we're okay. + if ((OldIsAtomic && isImplicitlyReadonlyAtomic(OldProperty)) || + (NewIsAtomic && isImplicitlyReadonlyAtomic(NewProperty))) + return; + + // Diagnose the conflict. + const IdentifierInfo *OldContextName; + auto *OldDC = OldProperty->getDeclContext(); + if (auto Category = dyn_cast<ObjCCategoryDecl>(OldDC)) + OldContextName = Category->getClassInterface()->getIdentifier(); + else + OldContextName = cast<ObjCContainerDecl>(OldDC)->getIdentifier(); + + S.Diag(NewProperty->getLocation(), diag::warn_property_attribute) + << NewProperty->getDeclName() << "atomic" + << OldContextName; + S.Diag(OldProperty->getLocation(), diag::note_property_declare); } ObjCPropertyDecl * @@ -308,11 +412,9 @@ Sema::HandlePropertyInClassExtension(Scope *S, SourceLocation LParenLoc, FieldDeclarator &FD, Selector GetterSel, Selector SetterSel, - const bool isAssign, const bool isReadWrite, - const unsigned Attributes, + unsigned &Attributes, const unsigned AttributesAsWritten, - bool *isOverridingProperty, QualType T, TypeSourceInfo *TSI, tok::ObjCKeywordKind MethodImplKind) { @@ -322,80 +424,102 @@ Sema::HandlePropertyInClassExtension(Scope *S, IdentifierInfo *PropertyId = FD.D.getIdentifier(); ObjCInterfaceDecl *CCPrimary = CDecl->getClassInterface(); - if (CCPrimary) { - // Check for duplicate declaration of this property in current and - // other class extensions. - for (const auto *Ext : CCPrimary->known_extensions()) { - if (ObjCPropertyDecl *prevDecl - = ObjCPropertyDecl::findPropertyDecl(Ext, PropertyId)) { - Diag(AtLoc, diag::err_duplicate_property); - Diag(prevDecl->getLocation(), diag::note_property_declare); - return nullptr; - } - } - } - - // Create a new ObjCPropertyDecl with the DeclContext being - // the class extension. - // FIXME. We should really be using CreatePropertyDecl for this. - ObjCPropertyDecl *PDecl = - ObjCPropertyDecl::Create(Context, DC, FD.D.getIdentifierLoc(), - PropertyId, AtLoc, LParenLoc, T, TSI); - PDecl->setPropertyAttributesAsWritten( - makePropertyAttributesAsWritten(AttributesAsWritten)); - if (Attributes & ObjCDeclSpec::DQ_PR_readonly) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); - if (Attributes & ObjCDeclSpec::DQ_PR_readwrite) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readwrite); - if (Attributes & ObjCDeclSpec::DQ_PR_nonatomic) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nonatomic); - if (Attributes & ObjCDeclSpec::DQ_PR_atomic) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic); - if (Attributes & ObjCDeclSpec::DQ_PR_nullability) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_nullability); - if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable) - PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable); - - // Set setter/getter selector name. Needed later. - PDecl->setGetterName(GetterSel); - PDecl->setSetterName(SetterSel); - ProcessDeclAttributes(S, PDecl, FD.D); - DC->addDecl(PDecl); - // We need to look in the @interface to see if the @property was // already declared. if (!CCPrimary) { Diag(CDecl->getLocation(), diag::err_continuation_class); - *isOverridingProperty = true; return nullptr; } - // Find the property in continuation class's primary class only. + // Find the property in the extended class's primary class or + // extensions. ObjCPropertyDecl *PIDecl = CCPrimary->FindPropertyVisibleInPrimaryClass(PropertyId); + // If we found a property in an extension, complain. + if (PIDecl && isa<ObjCCategoryDecl>(PIDecl->getDeclContext())) { + Diag(AtLoc, diag::err_duplicate_property); + Diag(PIDecl->getLocation(), diag::note_property_declare); + return nullptr; + } + + // Check for consistency with the previous declaration, if there is one. + if (PIDecl) { + // A readonly property declared in the primary class can be refined + // by adding a readwrite property within an extension. + // Anything else is an error. + if (!(PIDecl->isReadOnly() && isReadWrite)) { + // Tailor the diagnostics for the common case where a readwrite + // property is declared both in the @interface and the continuation. + // This is a common error where the user often intended the original + // declaration to be readonly. + unsigned diag = + (Attributes & ObjCDeclSpec::DQ_PR_readwrite) && + (PIDecl->getPropertyAttributesAsWritten() & + ObjCPropertyDecl::OBJC_PR_readwrite) + ? diag::err_use_continuation_class_redeclaration_readwrite + : diag::err_use_continuation_class; + Diag(AtLoc, diag) + << CCPrimary->getDeclName(); + Diag(PIDecl->getLocation(), diag::note_property_declare); + return nullptr; + } + + // Check for consistency of getters. + if (PIDecl->getGetterName() != GetterSel) { + // If the getter was written explicitly, complain. + if (AttributesAsWritten & ObjCDeclSpec::DQ_PR_getter) { + Diag(AtLoc, diag::warn_property_redecl_getter_mismatch) + << PIDecl->getGetterName() << GetterSel; + Diag(PIDecl->getLocation(), diag::note_property_declare); + } + + // Always adopt the getter from the original declaration. + GetterSel = PIDecl->getGetterName(); + Attributes |= ObjCDeclSpec::DQ_PR_getter; + } + + // Check consistency of ownership. + unsigned ExistingOwnership + = getOwnershipRule(PIDecl->getPropertyAttributes()); + unsigned NewOwnership = getOwnershipRule(Attributes); + if (ExistingOwnership && NewOwnership != ExistingOwnership) { + // If the ownership was written explicitly, complain. + if (getOwnershipRule(AttributesAsWritten)) { + Diag(AtLoc, diag::warn_property_attr_mismatch); + Diag(PIDecl->getLocation(), diag::note_property_declare); + } + + // Take the ownership from the original property. + Attributes = (Attributes & ~OwnershipMask) | ExistingOwnership; + } + + // If the redeclaration is 'weak' but the original property is not, + if ((Attributes & ObjCPropertyDecl::OBJC_PR_weak) && + !(PIDecl->getPropertyAttributesAsWritten() + & ObjCPropertyDecl::OBJC_PR_weak) && + PIDecl->getType()->getAs<ObjCObjectPointerType>() && + PIDecl->getType().getObjCLifetime() == Qualifiers::OCL_None) { + Diag(AtLoc, diag::warn_property_implicitly_mismatched); + Diag(PIDecl->getLocation(), diag::note_property_declare); + } + } + + // Create a new ObjCPropertyDecl with the DeclContext being + // the class extension. + ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc, + FD, GetterSel, SetterSel, + isReadWrite, + Attributes, AttributesAsWritten, + T, TSI, MethodImplKind, DC); + + // If there was no declaration of a property with the same name in + // the primary class, we're done. if (!PIDecl) { - // No matching property found in the primary class. Just fall thru - // and add property to continuation class's primary class. - ObjCPropertyDecl *PrimaryPDecl = - CreatePropertyDecl(S, CCPrimary, AtLoc, LParenLoc, - FD, GetterSel, SetterSel, isAssign, isReadWrite, - Attributes,AttributesAsWritten, T, TSI, MethodImplKind, - DC); - - // A case of continuation class adding a new property in the class. This - // is not what it was meant for. However, gcc supports it and so should we. - // Make sure setter/getters are declared here. - ProcessPropertyDecl(PrimaryPDecl, CCPrimary, - /* redeclaredProperty = */ nullptr, - /* lexicalDC = */ CDecl); - PDecl->setGetterMethodDecl(PrimaryPDecl->getGetterMethodDecl()); - PDecl->setSetterMethodDecl(PrimaryPDecl->getSetterMethodDecl()); - if (ASTMutationListener *L = Context.getASTMutationListener()) - L->AddedObjCPropertyInClassExtension(PrimaryPDecl, /*OrigProp=*/nullptr, - CDecl); - return PrimaryPDecl; + ProcessPropertyDecl(PDecl); + return PDecl; } + if (!Context.hasSameType(PIDecl->getType(), PDecl->getType())) { bool IncompatibleObjC = false; QualType ConvertedType; @@ -418,103 +542,13 @@ Sema::HandlePropertyInClassExtension(Scope *S, return nullptr; } } - - // The property 'PIDecl's readonly attribute will be over-ridden - // with continuation class's readwrite property attribute! - unsigned PIkind = PIDecl->getPropertyAttributesAsWritten(); - if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) { - PIkind &= ~ObjCPropertyDecl::OBJC_PR_readonly; - PIkind |= ObjCPropertyDecl::OBJC_PR_readwrite; - PIkind |= deduceWeakPropertyFromType(PIDecl->getType()); - unsigned ClassExtensionMemoryModel = getOwnershipRule(Attributes); - unsigned PrimaryClassMemoryModel = getOwnershipRule(PIkind); - if (PrimaryClassMemoryModel && ClassExtensionMemoryModel && - (PrimaryClassMemoryModel != ClassExtensionMemoryModel)) { - Diag(AtLoc, diag::warn_property_attr_mismatch); - Diag(PIDecl->getLocation(), diag::note_property_declare); - } - else if (getLangOpts().ObjCAutoRefCount) { - QualType PrimaryPropertyQT = - Context.getCanonicalType(PIDecl->getType()).getUnqualifiedType(); - if (isa<ObjCObjectPointerType>(PrimaryPropertyQT)) { - bool PropertyIsWeak = ((PIkind & ObjCPropertyDecl::OBJC_PR_weak) != 0); - Qualifiers::ObjCLifetime PrimaryPropertyLifeTime = - PrimaryPropertyQT.getObjCLifetime(); - if (PrimaryPropertyLifeTime == Qualifiers::OCL_None && - (Attributes & ObjCDeclSpec::DQ_PR_weak) && - !PropertyIsWeak) { - Diag(AtLoc, diag::warn_property_implicitly_mismatched); - Diag(PIDecl->getLocation(), diag::note_property_declare); - } - } - } - - DeclContext *DC = cast<DeclContext>(CCPrimary); - if (!ObjCPropertyDecl::findPropertyDecl(DC, - PIDecl->getDeclName().getAsIdentifierInfo())) { - // In mrr mode, 'readwrite' property must have an explicit - // memory attribute. If none specified, select the default (assign). - if (!getLangOpts().ObjCAutoRefCount) { - if (!(PIkind & (ObjCDeclSpec::DQ_PR_assign | - ObjCDeclSpec::DQ_PR_retain | - ObjCDeclSpec::DQ_PR_strong | - ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_unsafe_unretained | - ObjCDeclSpec::DQ_PR_weak))) - PIkind |= ObjCPropertyDecl::OBJC_PR_assign; - } - - // Protocol is not in the primary class. Must build one for it. - ObjCDeclSpec ProtocolPropertyODS; - // FIXME. Assuming that ObjCDeclSpec::ObjCPropertyAttributeKind - // and ObjCPropertyDecl::PropertyAttributeKind have identical - // values. Should consolidate both into one enum type. - ProtocolPropertyODS. - setPropertyAttributes((ObjCDeclSpec::ObjCPropertyAttributeKind) - PIkind); - // Must re-establish the context from class extension to primary - // class context. - ContextRAII SavedContext(*this, CCPrimary); - - Decl *ProtocolPtrTy = - ActOnProperty(S, AtLoc, LParenLoc, FD, ProtocolPropertyODS, - PIDecl->getGetterName(), - PIDecl->getSetterName(), - isOverridingProperty, - MethodImplKind, - /* lexicalDC = */ CDecl); - PIDecl = cast<ObjCPropertyDecl>(ProtocolPtrTy); - } - 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); - } else { - // Tailor the diagnostics for the common case where a readwrite - // property is declared both in the @interface and the continuation. - // This is a common error where the user often intended the original - // declaration to be readonly. - unsigned diag = - (Attributes & ObjCDeclSpec::DQ_PR_readwrite) && - (PIkind & ObjCPropertyDecl::OBJC_PR_readwrite) - ? diag::err_use_continuation_class_redeclaration_readwrite - : diag::err_use_continuation_class; - Diag(AtLoc, diag) - << CCPrimary->getDeclName(); - Diag(PIDecl->getLocation(), diag::note_property_declare); - return nullptr; - } - *isOverridingProperty = true; - // Make sure setter decl is synthesized, and added to primary class's list. - ProcessPropertyDecl(PIDecl, CCPrimary, PDecl, CDecl); - PDecl->setGetterMethodDecl(PIDecl->getGetterMethodDecl()); - PDecl->setSetterMethodDecl(PIDecl->getSetterMethodDecl()); - if (ASTMutationListener *L = Context.getASTMutationListener()) - L->AddedObjCPropertyInClassExtension(PDecl, PIDecl, CDecl); + + // Check that atomicity of property in class extension matches the previous + // declaration. + checkAtomicPropertyMismatch(*this, PIDecl, PDecl, true); + + // Make sure getter/setter are appropriately synthesized. + ProcessPropertyDecl(PDecl); return PDecl; } @@ -525,7 +559,6 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, FieldDeclarator &FD, Selector GetterSel, Selector SetterSel, - const bool isAssign, const bool isReadWrite, const unsigned Attributes, const unsigned AttributesAsWritten, @@ -535,10 +568,23 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, DeclContext *lexicalDC){ IdentifierInfo *PropertyId = FD.D.getIdentifier(); - // Issue a warning if property is 'assign' as default and its object, which is - // gc'able conforms to NSCopying protocol + // Property defaults to 'assign' if it is readwrite, unless this is ARC + // and the type is retainable. + bool isAssign; + if (Attributes & (ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained)) { + isAssign = true; + } else if (getOwnershipRule(Attributes) || !isReadWrite) { + isAssign = false; + } else { + isAssign = (!getLangOpts().ObjCAutoRefCount || + !T->isObjCRetainableType()); + } + + // Issue a warning if property is 'assign' as default and its + // object, which is gc'able conforms to NSCopying protocol if (getLangOpts().getGC() != LangOptions::NonGC && - isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) + isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) { if (const ObjCObjectPointerType *ObjPtrTy = T->getAs<ObjCObjectPointerType>()) { ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); @@ -548,6 +594,7 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, if (IDecl->ClassImplementsProtocol(PNSCopying, true)) Diag(AtLoc, diag::warn_implements_nscopying) << PropertyId; } + } if (T->isObjCObjectType()) { SourceLocation StarLoc = TInfo->getTypeLoc().getLocEnd(); @@ -663,8 +710,10 @@ static void checkARCPropertyImpl(Sema &S, SourceLocation propertyImplLoc, // We're fine if they match. if (propertyLifetime == ivarLifetime) return; - // These aren't valid lifetimes for object ivars; don't diagnose twice. - if (ivarLifetime == Qualifiers::OCL_None || + // None isn't a valid lifetime for an object ivar in ARC, and + // __autoreleasing is never valid; don't diagnose twice. + if ((ivarLifetime == Qualifiers::OCL_None && + S.getLangOpts().ObjCAutoRefCount) || ivarLifetime == Qualifiers::OCL_Autoreleasing) return; @@ -797,6 +846,38 @@ DiagnosePropertyMismatchDeclInProtocols(Sema &S, SourceLocation AtLoc, S.Diag(AtLoc, diag::note_property_synthesize); } +/// Determine whether any storage attributes were written on the property. +static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop) { + if (Prop->getPropertyAttributesAsWritten() & OwnershipMask) return true; + + // If this is a readwrite property in a class extension that refines + // a readonly property in the original class definition, check it as + // well. + + // If it's a readonly property, we're not interested. + if (Prop->isReadOnly()) return false; + + // Is it declared in an extension? + auto Category = dyn_cast<ObjCCategoryDecl>(Prop->getDeclContext()); + if (!Category || !Category->IsClassExtension()) return false; + + // Find the corresponding property in the primary class definition. + auto OrigClass = Category->getClassInterface(); + for (auto Found : OrigClass->lookup(Prop->getDeclName())) { + if (ObjCPropertyDecl *OrigProp = dyn_cast<ObjCPropertyDecl>(Found)) + return OrigProp->getPropertyAttributesAsWritten() & OwnershipMask; + } + + // Look through all of the protocols. + for (const auto *Proto : OrigClass->all_referenced_protocols()) { + if (ObjCPropertyDecl *OrigProp = + Proto->FindPropertyDeclaration(Prop->getIdentifier())) + return OrigProp->getPropertyAttributesAsWritten() & OwnershipMask; + } + + return false; +} + /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared /// as \@synthesize or \@dynamic. @@ -953,18 +1034,49 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, ObjCPropertyDecl::PropertyAttributeKind kind = property->getPropertyAttributes(); - // Add GC __weak to the ivar type if the property is weak. - if ((kind & ObjCPropertyDecl::OBJC_PR_weak) && - getLangOpts().getGC() != LangOptions::NonGC) { - assert(!getLangOpts().ObjCAutoRefCount); - if (PropertyIvarType.isObjCGCStrong()) { - Diag(PropertyDiagLoc, diag::err_gc_weak_property_strong_type); - Diag(property->getLocation(), diag::note_property_declare); + bool isARCWeak = false; + if (kind & ObjCPropertyDecl::OBJC_PR_weak) { + // Add GC __weak to the ivar type if the property is weak. + if (getLangOpts().getGC() != LangOptions::NonGC) { + assert(!getLangOpts().ObjCAutoRefCount); + if (PropertyIvarType.isObjCGCStrong()) { + Diag(PropertyDiagLoc, diag::err_gc_weak_property_strong_type); + Diag(property->getLocation(), diag::note_property_declare); + } else { + PropertyIvarType = + Context.getObjCGCQualType(PropertyIvarType, Qualifiers::Weak); + } + + // Otherwise, check whether ARC __weak is enabled and works with + // the property type. } else { - PropertyIvarType = - Context.getObjCGCQualType(PropertyIvarType, Qualifiers::Weak); + if (!getLangOpts().ObjCWeak) { + // Only complain here when synthesizing an ivar. + if (!Ivar) { + Diag(PropertyDiagLoc, + getLangOpts().ObjCWeakRuntime + ? diag::err_synthesizing_arc_weak_property_disabled + : diag::err_synthesizing_arc_weak_property_no_runtime); + Diag(property->getLocation(), diag::note_property_declare); + } + CompleteTypeErr = true; // suppress later diagnostics about the ivar + } else { + isARCWeak = true; + if (const ObjCObjectPointerType *ObjT = + PropertyIvarType->getAs<ObjCObjectPointerType>()) { + const ObjCInterfaceDecl *ObjI = ObjT->getInterfaceDecl(); + if (ObjI && ObjI->isArcWeakrefUnavailable()) { + Diag(property->getLocation(), + diag::err_arc_weak_unavailable_property) + << PropertyIvarType; + Diag(ClassImpDecl->getLocation(), diag::note_implemented_by_class) + << ClassImpDecl->getName(); + } + } + } } } + if (AtLoc.isInvalid()) { // Check when default synthesizing a property that there is // an ivar matching property name and issue warning; since this @@ -987,13 +1099,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (!Ivar) { // In ARC, give the ivar a lifetime qualifier based on the // property attributes. - if (getLangOpts().ObjCAutoRefCount && + if ((getLangOpts().ObjCAutoRefCount || isARCWeak) && !PropertyIvarType.getObjCLifetime() && PropertyIvarType->isObjCRetainableType()) { // It's an error if we have to do this and the user didn't // explicitly write an ownership attribute on the property. - if (!property->hasWrittenStorageAttribute() && + if (!hasWrittenStorageAttribute(property) && !(kind & ObjCPropertyDecl::OBJC_PR_strong)) { Diag(PropertyDiagLoc, diag::err_arc_objc_property_default_assign_on_object); @@ -1002,24 +1114,6 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, Qualifiers::ObjCLifetime lifetime = getImpliedARCOwnership(kind, PropertyIvarType); assert(lifetime && "no lifetime for property?"); - if (lifetime == Qualifiers::OCL_Weak) { - bool err = false; - if (const ObjCObjectPointerType *ObjT = - PropertyIvarType->getAs<ObjCObjectPointerType>()) { - const ObjCInterfaceDecl *ObjI = ObjT->getInterfaceDecl(); - if (ObjI && ObjI->isArcWeakrefUnavailable()) { - Diag(property->getLocation(), - diag::err_arc_weak_unavailable_property) << PropertyIvarType; - Diag(ClassImpDecl->getLocation(), diag::note_implemented_by_class) - << ClassImpDecl->getName(); - err = true; - } - } - if (!err && !getLangOpts().ObjCARCWeak) { - Diag(PropertyDiagLoc, diag::err_arc_weak_no_runtime); - Diag(property->getLocation(), diag::note_property_declare); - } - } Qualifiers qs; qs.addObjCLifetime(lifetime); @@ -1027,13 +1121,6 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } } - if (kind & ObjCPropertyDecl::OBJC_PR_weak && - !getLangOpts().ObjCAutoRefCount && - getLangOpts().getGC() == LangOptions::NonGC) { - Diag(PropertyDiagLoc, diag::error_synthesize_weak_non_arc_or_gc); - Diag(property->getLocation(), diag::note_property_declare); - } - Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl, PropertyIvarLoc,PropertyIvarLoc, PropertyIvar, PropertyIvarType, /*Dinfo=*/nullptr, @@ -1121,7 +1208,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // Fall thru - see previous comment } } - if (getLangOpts().ObjCAutoRefCount) + if (getLangOpts().ObjCAutoRefCount || isARCWeak || + Ivar->getType().getObjCLifetime()) checkARCPropertyImpl(*this, PropertyLoc, property, Ivar); } else if (PropertyIvar) // @dynamic @@ -1349,12 +1437,10 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, } } - if ((CAttr & ObjCPropertyDecl::OBJC_PR_nonatomic) - != (SAttr & ObjCPropertyDecl::OBJC_PR_nonatomic)) { - Diag(Property->getLocation(), diag::warn_property_attribute) - << Property->getDeclName() << "atomic" << inheritedName; - Diag(SuperProperty->getLocation(), diag::note_property_declare); - } + // Check for nonatomic; note that nonatomic is effectively + // meaningless for readonly properties, so don't diagnose if the + // atomic property is 'readonly'. + checkAtomicPropertyMismatch(*this, SuperProperty, Property, false); if (Property->getSetterName() != SuperProperty->getSetterName()) { Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "setter" << inheritedName; @@ -1395,12 +1481,11 @@ bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, QualType PropertyIvarType = property->getType().getNonReferenceType(); bool compat = Context.hasSameType(PropertyIvarType, GetterType); if (!compat) { - if (isa<ObjCObjectPointerType>(PropertyIvarType) && - isa<ObjCObjectPointerType>(GetterType)) - compat = - Context.canAssignObjCInterfaces( - GetterType->getAs<ObjCObjectPointerType>(), - PropertyIvarType->getAs<ObjCObjectPointerType>()); + const ObjCObjectPointerType *propertyObjCPtr = nullptr; + const ObjCObjectPointerType *getterObjCPtr = nullptr; + if ((propertyObjCPtr = PropertyIvarType->getAs<ObjCObjectPointerType>()) && + (getterObjCPtr = GetterType->getAs<ObjCObjectPointerType>())) + compat = Context.canAssignObjCInterfaces(getterObjCPtr, propertyObjCPtr); else if (CheckAssignmentConstraints(Loc, GetterType, PropertyIvarType) != Compatible) { Diag(Loc, diag::error_property_accessor_type) @@ -1438,6 +1523,11 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl)) { for (auto *Prop : IDecl->properties()) PropMap[Prop->getIdentifier()] = Prop; + + // Collect the properties from visible extensions. + for (auto *Ext : IDecl->visible_extensions()) + CollectImmediateProperties(Ext, PropMap, SuperPropMap, IncludeProtocols); + if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : IDecl->all_referenced_protocols()) @@ -1445,9 +1535,8 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } if (ObjCCategoryDecl *CATDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) { - if (!CATDecl->IsClassExtension()) - for (auto *Prop : CATDecl->properties()) - PropMap[Prop->getIdentifier()] = Prop; + for (auto *Prop : CATDecl->properties()) + PropMap[Prop->getIdentifier()] = Prop; if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : CATDecl->protocols()) @@ -1507,6 +1596,14 @@ Sema::IvarBacksCurrentMethodAccessor(ObjCInterfaceDecl *IFace, (Property->getPropertyIvarDecl() == IV)) return true; } + // Also look up property declaration in class extension whose one of its + // accessors is implemented by this method. + for (const auto *Ext : IFace->known_extensions()) + for (const auto *Property : Ext->properties()) + if ((Property->getGetterName() == IMD->getSelector() || + Property->getSetterName() == IMD->getSelector()) && + (Property->getPropertyIvarDecl() == IV)) + return true; return false; } @@ -1563,7 +1660,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, IMPDecl->FindPropertyImplIvarDecl(Prop->getIdentifier())) { Diag(Prop->getLocation(), diag::warn_no_autosynthesis_shared_ivar_property) << Prop->getIdentifier(); - if (!PID->getLocation().isInvalid()) + if (PID->getLocation().isValid()) Diag(PID->getLocation(), diag::note_property_synthesize); continue; } @@ -1791,11 +1888,20 @@ void Sema::diagnoseNullResettableSynthesizedSetters(const ObjCImplDecl *impDecl) void Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, - ObjCContainerDecl* IDecl) { + ObjCInterfaceDecl* IDecl) { // Rules apply in non-GC mode only if (getLangOpts().getGC() != LangOptions::NonGC) return; - for (const auto *Property : IDecl->properties()) { + ObjCContainerDecl::PropertyMap PM; + for (auto *Prop : IDecl->properties()) + PM[Prop->getIdentifier()] = Prop; + for (const auto *Ext : IDecl->known_extensions()) + for (auto *Prop : Ext->properties()) + PM[Prop->getIdentifier()] = Prop; + + for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); + I != E; ++I) { + const ObjCPropertyDecl *Property = I->second; ObjCMethodDecl *GetterMethod = nullptr; ObjCMethodDecl *SetterMethod = nullptr; bool LookedUpGetterSetter = false; @@ -1842,30 +1948,23 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, << Property->getIdentifier() << (GetterMethod != nullptr) << (SetterMethod != nullptr); // fixit stuff. - if (!AttributesAsWritten) { - if (Property->getLParenLoc().isValid()) { - // @property () ... case. - SourceRange PropSourceRange(Property->getAtLoc(), - Property->getLParenLoc()); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic"); - } - else { - //@property id etc. - SourceLocation endLoc = - Property->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); - endLoc = endLoc.getLocWithOffset(-1); - SourceRange PropSourceRange(Property->getAtLoc(), endLoc); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic) "); - } - } - else if (!(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_atomic)) { + if (Property->getLParenLoc().isValid() && + !(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_atomic)) { // @property () ... case. - SourceLocation endLoc = Property->getLParenLoc(); - SourceRange PropSourceRange(Property->getAtLoc(), endLoc); - Diag(Property->getLocation(), diag::note_atomic_property_fixup_suggest) << - FixItHint::CreateReplacement(PropSourceRange, "@property (nonatomic, "); + SourceLocation AfterLParen = + getLocForEndOfToken(Property->getLParenLoc()); + StringRef NonatomicStr = AttributesAsWritten? "nonatomic, " + : "nonatomic"; + Diag(Property->getLocation(), + diag::note_atomic_property_fixup_suggest) + << FixItHint::CreateInsertion(AfterLParen, NonatomicStr); + } else if (Property->getLParenLoc().isInvalid()) { + //@property id etc. + SourceLocation startLoc = + Property->getTypeSourceInfo()->getTypeLoc().getBeginLoc(); + Diag(Property->getLocation(), + diag::note_atomic_property_fixup_suggest) + << FixItHint::CreateInsertion(startLoc, "(nonatomic) "); } else Diag(MethodLoc, diag::note_atomic_property_fixup_suggest); @@ -1950,10 +2049,16 @@ void Sema::DiagnoseMissingDesignatedInitOverrides( I = DesignatedInits.begin(), E = DesignatedInits.end(); I != E; ++I) { const ObjCMethodDecl *MD = *I; if (!InitSelSet.count(MD->getSelector())) { - Diag(ImplD->getLocation(), - diag::warn_objc_implementation_missing_designated_init_override) - << MD->getSelector(); - Diag(MD->getLocation(), diag::note_objc_designated_init_marked_here); + bool Ignore = false; + if (auto *IMD = IFD->getInstanceMethod(MD->getSelector())) { + Ignore = IMD->isUnavailable(); + } + if (!Ignore) { + Diag(ImplD->getLocation(), + diag::warn_objc_implementation_missing_designated_init_override) + << MD->getSelector(); + Diag(MD->getLocation(), diag::note_objc_designated_init_marked_here); + } } } } @@ -1974,20 +2079,28 @@ static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods /// have the property type and issue diagnostics if they don't. /// Also synthesize a getter/setter method if none exist (and update the -/// appropriate lookup tables. FIXME: Should reconsider if adding synthesized -/// methods is the "right" thing to do. -void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, - ObjCContainerDecl *CD, - ObjCPropertyDecl *redeclaredProperty, - ObjCContainerDecl *lexicalDC) { - +/// appropriate lookup tables. +void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { ObjCMethodDecl *GetterMethod, *SetterMethod; - + ObjCContainerDecl *CD = cast<ObjCContainerDecl>(property->getDeclContext()); if (CD->isInvalidDecl()) return; GetterMethod = CD->getInstanceMethod(property->getGetterName()); + // if setter or getter is not found in class extension, it might be + // in the primary class. + if (!GetterMethod) + if (const ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CD)) + if (CatDecl->IsClassExtension()) + GetterMethod = CatDecl->getClassInterface()-> + getInstanceMethod(property->getGetterName()); + SetterMethod = CD->getInstanceMethod(property->getSetterName()); + if (!SetterMethod) + if (const ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CD)) + if (CatDecl->IsClassExtension()) + SetterMethod = CatDecl->getClassInterface()-> + getInstanceMethod(property->getSetterName()); DiagnosePropertyAccessorMismatch(property, GetterMethod, property->getLocation()); @@ -2020,9 +2133,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // No instance method of same name as property getter name was found. // Declare a getter method and add it to the list of methods // for this class. - SourceLocation Loc = redeclaredProperty ? - redeclaredProperty->getLocation() : - property->getLocation(); + SourceLocation Loc = property->getLocation(); // If the property is null_resettable, the getter returns nonnull. QualType resultTy = property->getType(); @@ -2050,10 +2161,6 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, AddPropertyAttrs(*this, GetterMethod, property); - // FIXME: Eventually this shouldn't be needed, as the lexical context - // and the real context should be the same. - if (lexicalDC) - GetterMethod->setLexicalDeclContext(lexicalDC); if (property->hasAttr<NSReturnsNotRetainedAttr>()) GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context, Loc)); @@ -2082,9 +2189,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // No instance method of same name as property setter name was found. // Declare a setter method and add it to the list of methods // for this class. - SourceLocation Loc = redeclaredProperty ? - redeclaredProperty->getLocation() : - property->getLocation(); + SourceLocation Loc = property->getLocation(); SetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc, @@ -2126,10 +2231,6 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, AddPropertyAttrs(*this, SetterMethod, property); CD->addDecl(SetterMethod); - // FIXME: Eventually this shouldn't be needed, as the lexical context - // and the real context should be the same. - if (lexicalDC) - SetterMethod->setLexicalDeclContext(lexicalDC); if (const SectionAttr *SA = property->getAttr<SectionAttr>()) SetterMethod->addAttr( SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section, @@ -2189,15 +2290,6 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, ObjCPropertyDecl *PropertyDecl = cast<ObjCPropertyDecl>(PDecl); QualType PropertyTy = PropertyDecl->getType(); - unsigned PropertyOwnership = getOwnershipRule(Attributes); - - // 'readonly' property with no obvious lifetime. - // its life time will be determined by its backing ivar. - if (getLangOpts().ObjCAutoRefCount && - Attributes & ObjCDeclSpec::DQ_PR_readonly && - PropertyTy->isObjCRetainableType() && - !PropertyOwnership) - return; // Check for copy or retain on non-object types. if ((Attributes & (ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy | @@ -2295,13 +2387,6 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, if (*nullability == NullabilityKind::NonNull) Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "nonnull" << "weak"; - } else { - PropertyTy = - Context.getAttributedType( - AttributedType::getNullabilityAttrKind(NullabilityKind::Nullable), - PropertyTy, PropertyTy); - TypeSourceInfo *TSInfo = PropertyDecl->getTypeSourceInfo(); - PropertyDecl->setType(PropertyTy, TSInfo); } } @@ -2314,16 +2399,14 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, // 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_unsafe_unretained | - ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong | - ObjCDeclSpec::DQ_PR_weak)) && - PropertyTy->isObjCObjectPointerType()) { - if (getLangOpts().ObjCAutoRefCount) - // With arc, @property definitions should default to (strong) when - // not specified; including when property is 'readonly'. - PropertyDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); - else if (!(Attributes & ObjCDeclSpec::DQ_PR_readonly)) { + if (!getOwnershipRule(Attributes) && PropertyTy->isObjCRetainableType()) { + if (Attributes & ObjCDeclSpec::DQ_PR_readonly) { + // do nothing + } else if (getLangOpts().ObjCAutoRefCount) { + // With arc, @property definitions should default to strong when + // not specified. + PropertyDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); + } else if (PropertyTy->isObjCObjectPointerType()) { bool isAnyClassTy = (PropertyTy->isObjCClassType() || PropertyTy->isObjCQualifiedClassType()); @@ -2342,7 +2425,7 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, if (getLangOpts().getGC() == LangOptions::NonGC) Diag(Loc, diag::warn_objc_property_default_assign_on_object); } - } + } // FIXME: Implement warning dependent on NSCopying being // implemented. See also: |