Objective-C properties: merge attributes when redeclaring 'readonly' as 'readwrite' in an extension.

r251874 stopped back-patching the AST when an Objective-C 'readonly'
property is redeclared in a class extension as 'readwrite'. However,
it did not properly handle merging of Objective-C property attributes
(e.g., getter name, ownership, atomicity) to the redeclaration,
leading to bad metadata. Merge (and check!) those property attributes
so we get the right metadata and reasonable ASTs. Fixes
rdar://problem/23823989.

llvm-svn: 255309
This commit is contained in:
Douglas Gregor 2015-12-10 23:02:09 +00:00
parent f5d34b7b9d
commit 9dd25b7696
8 changed files with 190 additions and 112 deletions

View File

@ -2509,17 +2509,14 @@ public:
void setPropertyAttributes(PropertyAttributeKind PRVal) { void setPropertyAttributes(PropertyAttributeKind PRVal) {
PropertyAttributes |= PRVal; PropertyAttributes |= PRVal;
} }
void overwritePropertyAttributes(unsigned PRVal) {
PropertyAttributes = PRVal;
}
PropertyAttributeKind getPropertyAttributesAsWritten() const { PropertyAttributeKind getPropertyAttributesAsWritten() const {
return PropertyAttributeKind(PropertyAttributesAsWritten); return PropertyAttributeKind(PropertyAttributesAsWritten);
} }
bool hasWrittenStorageAttribute() const {
return PropertyAttributesAsWritten & (OBJC_PR_assign | OBJC_PR_copy |
OBJC_PR_unsafe_unretained | OBJC_PR_retain | OBJC_PR_strong |
OBJC_PR_weak);
}
void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) { void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) {
PropertyAttributesAsWritten = PRVal; PropertyAttributesAsWritten = PRVal;
} }

View File

@ -930,6 +930,9 @@ def warn_missing_explicit_synthesis : Warning <
def warn_property_getter_owning_mismatch : Warning< def warn_property_getter_owning_mismatch : Warning<
"property declared as returning non-retained objects" "property declared as returning non-retained objects"
"; getter returning retained objects">; "; getter returning retained objects">;
def warn_property_redecl_getter_mismatch : Warning<
"getter name mismatch between property redeclaration (%1) and its original "
"declaration (%0)">, InGroup<PropertyAttr>;
def error_property_setter_ambiguous_use : Error< def error_property_setter_ambiguous_use : Error<
"synthesized properties %0 and %1 both claim setter %2 -" "synthesized properties %0 and %1 both claim setter %2 -"
" use of this setter will cause unexpected behavior">; " use of this setter will cause unexpected behavior">;

View File

@ -3057,11 +3057,9 @@ public:
FieldDeclarator &FD, FieldDeclarator &FD,
Selector GetterSel, Selector GetterSel,
Selector SetterSel, Selector SetterSel,
const bool isAssign,
const bool isReadWrite, const bool isReadWrite,
const unsigned Attributes, unsigned &Attributes,
const unsigned AttributesAsWritten, const unsigned AttributesAsWritten,
bool *isOverridingProperty,
QualType T, QualType T,
TypeSourceInfo *TSI, TypeSourceInfo *TSI,
tok::ObjCKeywordKind MethodImplKind); tok::ObjCKeywordKind MethodImplKind);
@ -3075,7 +3073,6 @@ public:
FieldDeclarator &FD, FieldDeclarator &FD,
Selector GetterSel, Selector GetterSel,
Selector SetterSel, Selector SetterSel,
const bool isAssign,
const bool isReadWrite, const bool isReadWrite,
const unsigned Attributes, const unsigned Attributes,
const unsigned AttributesAsWritten, const unsigned AttributesAsWritten,
@ -7333,7 +7330,6 @@ public:
SourceLocation LParenLoc, SourceLocation LParenLoc,
FieldDeclarator &FD, ObjCDeclSpec &ODS, FieldDeclarator &FD, ObjCDeclSpec &ODS,
Selector GetterSel, Selector SetterSel, Selector GetterSel, Selector SetterSel,
bool *OverridingProperty,
tok::ObjCKeywordKind MethodImplKind, tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC = nullptr); DeclContext *lexicalDC = nullptr);

View File

@ -633,7 +633,6 @@ ObjCTypeParamList *Parser::parseObjCTypeParamList() {
void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
Decl *CDecl) { Decl *CDecl) {
SmallVector<Decl *, 32> allMethods; SmallVector<Decl *, 32> allMethods;
SmallVector<Decl *, 16> allProperties;
SmallVector<DeclGroupPtrTy, 8> allTUVariables; SmallVector<DeclGroupPtrTy, 8> allTUVariables;
tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword; tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword;
@ -789,12 +788,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
SetterSel = SelectorTable::constructSetterSelector( SetterSel = SelectorTable::constructSetterSelector(
PP.getIdentifierTable(), PP.getSelectorTable(), PP.getIdentifierTable(), PP.getSelectorTable(),
FD.D.getIdentifier()); FD.D.getIdentifier());
bool isOverridingProperty = false;
Decl *Property = Actions.ActOnProperty( Decl *Property = Actions.ActOnProperty(
getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel, getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel,
&isOverridingProperty, MethodImplKind); MethodImplKind);
if (!isOverridingProperty)
allProperties.push_back(Property);
FD.complete(Property); FD.complete(Property);
}; };

View File

@ -153,13 +153,26 @@ static unsigned deducePropertyOwnershipFromType(Sema &S, QualType T) {
return 0; return 0;
} }
static unsigned getOwnershipRule(unsigned attr) { static const unsigned OwnershipMask =
return attr & (ObjCPropertyDecl::OBJC_PR_assign | (ObjCPropertyDecl::OBJC_PR_assign |
ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_retain |
ObjCPropertyDecl::OBJC_PR_copy | ObjCPropertyDecl::OBJC_PR_copy |
ObjCPropertyDecl::OBJC_PR_weak | ObjCPropertyDecl::OBJC_PR_weak |
ObjCPropertyDecl::OBJC_PR_strong | ObjCPropertyDecl::OBJC_PR_strong |
ObjCPropertyDecl::OBJC_PR_unsafe_unretained); 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, Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
@ -168,7 +181,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
ObjCDeclSpec &ODS, ObjCDeclSpec &ODS,
Selector GetterSel, Selector GetterSel,
Selector SetterSel, Selector SetterSel,
bool *isOverridingProperty,
tok::ObjCKeywordKind MethodImplKind, tok::ObjCKeywordKind MethodImplKind,
DeclContext *lexicalDC) { DeclContext *lexicalDC) {
unsigned Attributes = ODS.getPropertyAttributes(); unsigned Attributes = ODS.getPropertyAttributes();
@ -182,19 +194,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
// default is readwrite! // default is readwrite!
!(Attributes & ObjCDeclSpec::DQ_PR_readonly)); !(Attributes & ObjCDeclSpec::DQ_PR_readonly));
// 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());
}
// Proceed with constructing the ObjCPropertyDecls. // Proceed with constructing the ObjCPropertyDecls.
ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext); ObjCContainerDecl *ClassDecl = cast<ObjCContainerDecl>(CurContext);
ObjCPropertyDecl *Res = nullptr; ObjCPropertyDecl *Res = nullptr;
@ -202,11 +201,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (CDecl->IsClassExtension()) { if (CDecl->IsClassExtension()) {
Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc, Res = HandlePropertyInClassExtension(S, AtLoc, LParenLoc,
FD, GetterSel, SetterSel, FD, GetterSel, SetterSel,
isAssign, isReadWrite, isReadWrite,
Attributes, Attributes,
ODS.getPropertyAttributes(), ODS.getPropertyAttributes(),
isOverridingProperty, T, TSI, T, TSI, MethodImplKind);
MethodImplKind);
if (!Res) if (!Res)
return nullptr; return nullptr;
} }
@ -214,7 +212,7 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc,
if (!Res) { if (!Res) {
Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD, Res = CreatePropertyDecl(S, ClassDecl, AtLoc, LParenLoc, FD,
GetterSel, SetterSel, isAssign, isReadWrite, GetterSel, SetterSel, isReadWrite,
Attributes, ODS.getPropertyAttributes(), Attributes, ODS.getPropertyAttributes(),
T, TSI, MethodImplKind); T, TSI, MethodImplKind);
if (lexicalDC) if (lexicalDC)
@ -342,12 +340,15 @@ static bool LocPropertyAttribute( ASTContext &Context, const char *attrName,
/// Check for a mismatch in the atomicity of the given properties. /// Check for a mismatch in the atomicity of the given properties.
static void checkAtomicPropertyMismatch(Sema &S, static void checkAtomicPropertyMismatch(Sema &S,
ObjCPropertyDecl *OldProperty, ObjCPropertyDecl *OldProperty,
ObjCPropertyDecl *NewProperty) { ObjCPropertyDecl *NewProperty,
bool PropagateAtomicity) {
// If the atomicity of both matches, we're done. // If the atomicity of both matches, we're done.
bool OldIsAtomic = bool OldIsAtomic =
(OldProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0; (OldProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic)
== 0;
bool NewIsAtomic = bool NewIsAtomic =
(NewProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0; (NewProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic)
== 0;
if (OldIsAtomic == NewIsAtomic) return; if (OldIsAtomic == NewIsAtomic) return;
// Determine whether the given property is readonly and implicitly // Determine whether the given property is readonly and implicitly
@ -355,18 +356,36 @@ static void checkAtomicPropertyMismatch(Sema &S,
auto isImplicitlyReadonlyAtomic = [](ObjCPropertyDecl *Property) -> bool { auto isImplicitlyReadonlyAtomic = [](ObjCPropertyDecl *Property) -> bool {
// Is it readonly? // Is it readonly?
auto Attrs = Property->getPropertyAttributes(); auto Attrs = Property->getPropertyAttributes();
if ((Attrs & ObjCDeclSpec::DQ_PR_readonly) == 0) return false; if ((Attrs & ObjCPropertyDecl::OBJC_PR_readonly) == 0) return false;
// Is it nonatomic? // Is it nonatomic?
if (Attrs & ObjCDeclSpec::DQ_PR_nonatomic) return false; if (Attrs & ObjCPropertyDecl::OBJC_PR_nonatomic) return false;
// Was 'atomic' specified directly? // Was 'atomic' specified directly?
if (Property->getPropertyAttributesAsWritten() & ObjCDeclSpec::DQ_PR_atomic) if (Property->getPropertyAttributesAsWritten() &
ObjCPropertyDecl::OBJC_PR_atomic)
return false; return false;
return true; 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 // One of the properties is atomic; if it's a readonly property, and
// 'atomic' wasn't explicitly specified, we're okay. // 'atomic' wasn't explicitly specified, we're okay.
if ((OldIsAtomic && isImplicitlyReadonlyAtomic(OldProperty)) || if ((OldIsAtomic && isImplicitlyReadonlyAtomic(OldProperty)) ||
@ -393,11 +412,9 @@ Sema::HandlePropertyInClassExtension(Scope *S,
SourceLocation LParenLoc, SourceLocation LParenLoc,
FieldDeclarator &FD, FieldDeclarator &FD,
Selector GetterSel, Selector SetterSel, Selector GetterSel, Selector SetterSel,
const bool isAssign,
const bool isReadWrite, const bool isReadWrite,
const unsigned Attributes, unsigned &Attributes,
const unsigned AttributesAsWritten, const unsigned AttributesAsWritten,
bool *isOverridingProperty,
QualType T, QualType T,
TypeSourceInfo *TSI, TypeSourceInfo *TSI,
tok::ObjCKeywordKind MethodImplKind) { tok::ObjCKeywordKind MethodImplKind) {
@ -411,7 +428,6 @@ Sema::HandlePropertyInClassExtension(Scope *S,
// already declared. // already declared.
if (!CCPrimary) { if (!CCPrimary) {
Diag(CDecl->getLocation(), diag::err_continuation_class); Diag(CDecl->getLocation(), diag::err_continuation_class);
*isOverridingProperty = true;
return nullptr; return nullptr;
} }
@ -427,11 +443,73 @@ Sema::HandlePropertyInClassExtension(Scope *S,
return nullptr; 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 // Create a new ObjCPropertyDecl with the DeclContext being
// the class extension. // the class extension.
ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc, ObjCPropertyDecl *PDecl = CreatePropertyDecl(S, CDecl, AtLoc, LParenLoc,
FD, GetterSel, SetterSel, FD, GetterSel, SetterSel,
isAssign, isReadWrite, isReadWrite,
Attributes, AttributesAsWritten, Attributes, AttributesAsWritten,
T, TSI, MethodImplKind, DC); T, TSI, MethodImplKind, DC);
@ -465,56 +543,9 @@ Sema::HandlePropertyInClassExtension(Scope *S,
} }
} }
// A readonly property declared in the primary class can be refined
// by adding a readwrite property within an extension.
// Anything else is an error.
unsigned PIkind = PIDecl->getPropertyAttributesAsWritten();
if (!(isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly))) {
// 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;
}
PIkind &= ~ObjCPropertyDecl::OBJC_PR_readonly;
PIkind |= ObjCPropertyDecl::OBJC_PR_readwrite;
PIkind |= deducePropertyOwnershipFromType(*this, 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);
}
}
}
// Check that atomicity of property in class extension matches the previous // Check that atomicity of property in class extension matches the previous
// declaration. // declaration.
checkAtomicPropertyMismatch(*this, PIDecl, PDecl); checkAtomicPropertyMismatch(*this, PIDecl, PDecl, true);
*isOverridingProperty = true;
// Make sure getter/setter are appropriately synthesized. // Make sure getter/setter are appropriately synthesized.
ProcessPropertyDecl(PDecl); ProcessPropertyDecl(PDecl);
@ -528,7 +559,6 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
FieldDeclarator &FD, FieldDeclarator &FD,
Selector GetterSel, Selector GetterSel,
Selector SetterSel, Selector SetterSel,
const bool isAssign,
const bool isReadWrite, const bool isReadWrite,
const unsigned Attributes, const unsigned Attributes,
const unsigned AttributesAsWritten, const unsigned AttributesAsWritten,
@ -538,10 +568,23 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
DeclContext *lexicalDC){ DeclContext *lexicalDC){
IdentifierInfo *PropertyId = FD.D.getIdentifier(); IdentifierInfo *PropertyId = FD.D.getIdentifier();
// Issue a warning if property is 'assign' as default and its object, which is // Property defaults to 'assign' if it is readwrite, unless this is ARC
// gc'able conforms to NSCopying protocol // 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 && if (getLangOpts().getGC() != LangOptions::NonGC &&
isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) isAssign && !(Attributes & ObjCDeclSpec::DQ_PR_assign)) {
if (const ObjCObjectPointerType *ObjPtrTy = if (const ObjCObjectPointerType *ObjPtrTy =
T->getAs<ObjCObjectPointerType>()) { T->getAs<ObjCObjectPointerType>()) {
ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
@ -551,6 +594,7 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
if (IDecl->ClassImplementsProtocol(PNSCopying, true)) if (IDecl->ClassImplementsProtocol(PNSCopying, true))
Diag(AtLoc, diag::warn_implements_nscopying) << PropertyId; Diag(AtLoc, diag::warn_implements_nscopying) << PropertyId;
} }
}
if (T->isObjCObjectType()) { if (T->isObjCObjectType()) {
SourceLocation StarLoc = TInfo->getTypeLoc().getLocEnd(); SourceLocation StarLoc = TInfo->getTypeLoc().getLocEnd();
@ -802,6 +846,31 @@ DiagnosePropertyMismatchDeclInProtocols(Sema &S, SourceLocation AtLoc,
S.Diag(AtLoc, diag::note_property_synthesize); 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;
}
return false;
}
/// ActOnPropertyImplDecl - This routine performs semantic checks and /// ActOnPropertyImplDecl - This routine performs semantic checks and
/// builds the AST node for a property implementation declaration; declared /// builds the AST node for a property implementation declaration; declared
/// as \@synthesize or \@dynamic. /// as \@synthesize or \@dynamic.
@ -1029,7 +1098,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
// It's an error if we have to do this and the user didn't // It's an error if we have to do this and the user didn't
// explicitly write an ownership attribute on the property. // explicitly write an ownership attribute on the property.
if (!property->hasWrittenStorageAttribute() && if (!hasWrittenStorageAttribute(property) &&
!(kind & ObjCPropertyDecl::OBJC_PR_strong)) { !(kind & ObjCPropertyDecl::OBJC_PR_strong)) {
Diag(PropertyDiagLoc, Diag(PropertyDiagLoc,
diag::err_arc_objc_property_default_assign_on_object); diag::err_arc_objc_property_default_assign_on_object);
@ -1364,7 +1433,7 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property,
// Check for nonatomic; note that nonatomic is effectively // Check for nonatomic; note that nonatomic is effectively
// meaningless for readonly properties, so don't diagnose if the // meaningless for readonly properties, so don't diagnose if the
// atomic property is 'readonly'. // atomic property is 'readonly'.
checkAtomicPropertyMismatch(*this, SuperProperty, Property); checkAtomicPropertyMismatch(*this, SuperProperty, Property, false);
if (Property->getSetterName() != SuperProperty->getSetterName()) { if (Property->getSetterName() != SuperProperty->getSetterName()) {
Diag(Property->getLocation(), diag::warn_property_attribute) Diag(Property->getLocation(), diag::warn_property_attribute)
<< Property->getDeclName() << "setter" << inheritedName; << Property->getDeclName() << "setter" << inheritedName;

View File

@ -1,9 +1,10 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-weak -fobjc-runtime-has-weak -emit-llvm %s -o - | FileCheck %s
// Checks metadata for properties in a few cases. // Checks metadata for properties in a few cases.
// Property from a class extension: // Property from a class extension:
__attribute__((objc_root_class))
@interface Foo @interface Foo
@end @end
@ -20,12 +21,17 @@
// CHECK: @"\01l_OBJC_$_PROP_LIST_Foo" = private global { i32, i32, [1 x %struct._prop_t] } // CHECK: @"\01l_OBJC_$_PROP_LIST_Foo" = private global { i32, i32, [1 x %struct._prop_t] }
// Readonly property in interface made readwrite in a category: // Readonly property in interface made readwrite in a category:
__attribute__((objc_root_class))
@interface FooRO @interface FooRO
@property (readonly) int evolvingprop; @property (readonly) int evolvingprop;
@property (nonatomic,readonly,getter=isBooleanProp) int booleanProp;
@property (nonatomic,readonly,weak) Foo *weakProp;
@end @end
@interface FooRO () @interface FooRO ()
@property int evolvingprop; @property int evolvingprop;
@property int booleanProp;
@property Foo *weakProp;
@end @end
@implementation FooRO @implementation FooRO
@ -34,6 +40,8 @@
// Metadata for _evolvingprop should be present, and PROP_LIST for FooRO should // Metadata for _evolvingprop should be present, and PROP_LIST for FooRO should
// still have only one entry, and the one entry should point to the version of // still have only one entry, and the one entry should point to the version of
// the property with a getter and setter. // the property with a getter and setter.
// CHECK: [[getter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [13 x i8] c"evolvingprop\00" // CHECK: [[evolvinggetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [13 x i8] c"evolvingprop\00"
// CHECK: [[setter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [18 x i8] c"Ti,V_evolvingprop\00", // CHECK: [[evolvingsetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [18 x i8] c"Ti,V_evolvingprop\00",
// CHECK: @"\01l_OBJC_$_PROP_LIST_FooRO" = private global { i32, i32, [1 x %struct._prop_t] }{{.*}}[[getter]]{{.*}}[[setter]] // CHECK: [[booleanmetadata:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [34 x i8] c"Ti,N,GisBooleanProp,V_booleanProp\00"
// CHECK: [[weakmetadata:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [23 x i8] c"T@\22Foo\22,W,N,V_weakProp\00"
// CHECK: @"\01l_OBJC_$_PROP_LIST_FooRO" = private global { i32, i32, [3 x %struct._prop_t] }{{.*}}[[evolvinggetter]]{{.*}}[[evolvingsetter]]{{.*}}[[booleanmetadata]]

View File

@ -31,3 +31,12 @@ typedef signed char BOOL;
@property (nonatomic, assign) BOOL allowReminders; @property (nonatomic, assign) BOOL allowReminders;
@property (nonatomic, assign) BOOL allowNonatomicProperty; // expected-warning {{'atomic' attribute on property 'allowNonatomicProperty' does not match the property inherited from 'EKProtocolCalendar'}} @property (nonatomic, assign) BOOL allowNonatomicProperty; // expected-warning {{'atomic' attribute on property 'allowNonatomicProperty' does not match the property inherited from 'EKProtocolCalendar'}}
@end @end
__attribute__((objc_root_class))
@interface A
@property (nonatomic, readonly, getter=isAvailable) int available; // expected-note{{property declared here}}
@end
@interface A ()
@property (nonatomic, assign, getter=wasAvailable) int available; // expected-warning{{getter name mismatch between property redeclaration ('wasAvailable') and its original declaration ('isAvailable')}}
@end

View File

@ -22,15 +22,15 @@
@end @end
@interface AtomicInheritanceSub2 : AtomicInheritanceSuper2 @interface AtomicInheritanceSub2 : AtomicInheritanceSuper2
@property (nonatomic, readwrite, retain) A *property; // FIXME: should be okay @property (nonatomic, readwrite, retain) A *property;
@end @end
@interface ReadonlyAtomic @interface ReadonlyAtomic
@property (readonly, nonatomic) A *property; // expected-note{{property declared here}} @property (readonly, nonatomic) A *property;
@end @end
@interface ReadonlyAtomic () @interface ReadonlyAtomic ()
@property (readwrite) A *property; // expected-warning{{'atomic' attribute on property 'property' does not match the property inherited from 'ReadonlyAtomic'}} @property (readwrite) A *property;
@end @end
// Readonly, atomic public redeclaration of property in subclass. // Readonly, atomic public redeclaration of property in subclass.