From ddd39efaedfdebb02425a4483f6ecd4a5d35866e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 10 Dec 2015 23:02:09 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@255309 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 9 +- include/clang/Basic/DiagnosticSemaKinds.td | 3 + include/clang/Sema/Sema.h | 6 +- lib/Parse/ParseObjc.cpp | 6 +- lib/Sema/SemaObjCProperty.cpp | 247 +++++++++++------- test/CodeGenObjC/property-list-in-extension.m | 16 +- test/SemaObjC/property-3.m | 9 + test/SemaObjC/property-atomic-redecl.m | 6 +- 8 files changed, 190 insertions(+), 112 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 833683fae2..f1e48a1296 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -2509,17 +2509,14 @@ public: void setPropertyAttributes(PropertyAttributeKind PRVal) { PropertyAttributes |= PRVal; } + void overwritePropertyAttributes(unsigned PRVal) { + PropertyAttributes = PRVal; + } PropertyAttributeKind getPropertyAttributesAsWritten() const { 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) { PropertyAttributesAsWritten = PRVal; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b00bd8ae5b..f1a8c279dc 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -930,6 +930,9 @@ def warn_missing_explicit_synthesis : Warning < def warn_property_getter_owning_mismatch : Warning< "property declared as returning non-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; def error_property_setter_ambiguous_use : Error< "synthesized properties %0 and %1 both claim setter %2 -" " use of this setter will cause unexpected behavior">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 44fd99cf95..1b14b13304 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3057,11 +3057,9 @@ public: 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); @@ -3075,7 +3073,6 @@ public: FieldDeclarator &FD, Selector GetterSel, Selector SetterSel, - const bool isAssign, const bool isReadWrite, const unsigned Attributes, const unsigned AttributesAsWritten, @@ -7333,7 +7330,6 @@ public: SourceLocation LParenLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, Selector GetterSel, Selector SetterSel, - bool *OverridingProperty, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC = nullptr); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 4df5802be6..63c01932c1 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -633,7 +633,6 @@ ObjCTypeParamList *Parser::parseObjCTypeParamList() { void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, Decl *CDecl) { SmallVector allMethods; - SmallVector allProperties; SmallVector allTUVariables; tok::ObjCKeywordKind MethodImplKind = tok::objc_not_keyword; @@ -789,12 +788,9 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, SetterSel = SelectorTable::constructSetterSelector( PP.getIdentifierTable(), PP.getSelectorTable(), FD.D.getIdentifier()); - bool isOverridingProperty = false; Decl *Property = Actions.ActOnProperty( getCurScope(), AtLoc, LParenLoc, FD, OCDS, GetterSel, SetterSel, - &isOverridingProperty, MethodImplKind); - if (!isOverridingProperty) - allProperties.push_back(Property); + MethodImplKind); FD.complete(Property); }; diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 0beb336092..f42daced86 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -153,13 +153,26 @@ static unsigned deducePropertyOwnershipFromType(Sema &S, QualType T) { 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) { - 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); + 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, @@ -168,7 +181,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, ObjCDeclSpec &ODS, Selector GetterSel, Selector SetterSel, - bool *isOverridingProperty, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC) { unsigned Attributes = ODS.getPropertyAttributes(); @@ -182,19 +194,6 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, // default is readwrite! !(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. ObjCContainerDecl *ClassDecl = cast(CurContext); ObjCPropertyDecl *Res = nullptr; @@ -202,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; } @@ -214,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) @@ -342,12 +340,15 @@ static bool LocPropertyAttribute( ASTContext &Context, const char *attrName, /// Check for a mismatch in the atomicity of the given properties. static void checkAtomicPropertyMismatch(Sema &S, ObjCPropertyDecl *OldProperty, - ObjCPropertyDecl *NewProperty) { + ObjCPropertyDecl *NewProperty, + bool PropagateAtomicity) { // If the atomicity of both matches, we're done. bool OldIsAtomic = - (OldProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0; + (OldProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) + == 0; bool NewIsAtomic = - (NewProperty->getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nonatomic) == 0; + (NewProperty->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) + == 0; if (OldIsAtomic == NewIsAtomic) return; // Determine whether the given property is readonly and implicitly @@ -355,18 +356,36 @@ static void checkAtomicPropertyMismatch(Sema &S, auto isImplicitlyReadonlyAtomic = [](ObjCPropertyDecl *Property) -> bool { // Is it readonly? 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? - if (Attrs & ObjCDeclSpec::DQ_PR_nonatomic) return false; + if (Attrs & ObjCPropertyDecl::OBJC_PR_nonatomic) return false; // Was 'atomic' specified directly? - if (Property->getPropertyAttributesAsWritten() & ObjCDeclSpec::DQ_PR_atomic) + 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)) || @@ -393,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) { @@ -411,7 +428,6 @@ Sema::HandlePropertyInClassExtension(Scope *S, // already declared. if (!CCPrimary) { Diag(CDecl->getLocation(), diag::err_continuation_class); - *isOverridingProperty = true; return nullptr; } @@ -427,11 +443,73 @@ Sema::HandlePropertyInClassExtension(Scope *S, 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() && + 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, - isAssign, isReadWrite, + isReadWrite, Attributes, AttributesAsWritten, T, TSI, MethodImplKind, DC); @@ -464,57 +542,10 @@ Sema::HandlePropertyInClassExtension(Scope *S, return nullptr; } } - - // 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(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 // declaration. - checkAtomicPropertyMismatch(*this, PIDecl, PDecl); - - *isOverridingProperty = true; + checkAtomicPropertyMismatch(*this, PIDecl, PDecl, true); // Make sure getter/setter are appropriately synthesized. ProcessPropertyDecl(PDecl); @@ -528,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, @@ -538,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()) { ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); @@ -551,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(); @@ -802,6 +846,31 @@ 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(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(Found)) + 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. @@ -1029,7 +1098,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // 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); @@ -1364,7 +1433,7 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, // 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); + checkAtomicPropertyMismatch(*this, SuperProperty, Property, false); if (Property->getSetterName() != SuperProperty->getSetterName()) { Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "setter" << inheritedName; diff --git a/test/CodeGenObjC/property-list-in-extension.m b/test/CodeGenObjC/property-list-in-extension.m index 18174fe6ba..878745e73e 100644 --- a/test/CodeGenObjC/property-list-in-extension.m +++ b/test/CodeGenObjC/property-list-in-extension.m @@ -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. // Property from a class extension: +__attribute__((objc_root_class)) @interface Foo @end @@ -20,12 +21,17 @@ // CHECK: @"\01l_OBJC_$_PROP_LIST_Foo" = private global { i32, i32, [1 x %struct._prop_t] } // Readonly property in interface made readwrite in a category: +__attribute__((objc_root_class)) @interface FooRO @property (readonly) int evolvingprop; +@property (nonatomic,readonly,getter=isBooleanProp) int booleanProp; +@property (nonatomic,readonly,weak) Foo *weakProp; @end @interface FooRO () @property int evolvingprop; +@property int booleanProp; +@property Foo *weakProp; @end @implementation FooRO @@ -34,6 +40,8 @@ // 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 // the property with a getter and setter. -// CHECK: [[getter:@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: @"\01l_OBJC_$_PROP_LIST_FooRO" = private global { i32, i32, [1 x %struct._prop_t] }{{.*}}[[getter]]{{.*}}[[setter]] +// CHECK: [[evolvinggetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [13 x i8] c"evolvingprop\00" +// CHECK: [[evolvingsetter:@OBJC_PROP_NAME_ATTR[^ ]+]] = private global [18 x i8] c"Ti,V_evolvingprop\00", +// 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]] diff --git a/test/SemaObjC/property-3.m b/test/SemaObjC/property-3.m index 3f82bcc3b7..8f2aa2d1ad 100644 --- a/test/SemaObjC/property-3.m +++ b/test/SemaObjC/property-3.m @@ -31,3 +31,12 @@ typedef signed char BOOL; @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'}} @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 diff --git a/test/SemaObjC/property-atomic-redecl.m b/test/SemaObjC/property-atomic-redecl.m index 8fd778048b..cb6d73a4ac 100644 --- a/test/SemaObjC/property-atomic-redecl.m +++ b/test/SemaObjC/property-atomic-redecl.m @@ -22,15 +22,15 @@ @end @interface AtomicInheritanceSub2 : AtomicInheritanceSuper2 -@property (nonatomic, readwrite, retain) A *property; // FIXME: should be okay +@property (nonatomic, readwrite, retain) A *property; @end @interface ReadonlyAtomic -@property (readonly, nonatomic) A *property; // expected-note{{property declared here}} +@property (readonly, nonatomic) A *property; @end @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 // Readonly, atomic public redeclaration of property in subclass. -- 2.40.0