Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3255,6 +3255,9 @@ /// Merge availability attributes for an implementation of /// a protocol requirement. AMK_ProtocolImplementation, + /// Merge availability attributes for an implementation of + /// an optional protocol requirement. + AMK_OptionalProtocolImplementation }; /// Describes the kind of priority given to an availability attribute. Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2608,7 +2608,8 @@ NewAttr = nullptr; else if ((isa(Attr) || isa(Attr)) && (AMK == Sema::AMK_Override || - AMK == Sema::AMK_ProtocolImplementation)) + AMK == Sema::AMK_ProtocolImplementation || + AMK == Sema::AMK_OptionalProtocolImplementation)) NewAttr = nullptr; else if (const auto *UA = dyn_cast(Attr)) NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl()); @@ -2956,6 +2957,7 @@ case AMK_Redeclaration: case AMK_Override: case AMK_ProtocolImplementation: + case AMK_OptionalProtocolImplementation: LocalAMK = AMK; break; } @@ -3861,10 +3863,11 @@ ObjCMethodDecl *oldMethod) { // Merge the attributes, including deprecated/unavailable AvailabilityMergeKind MergeKind = - isa(oldMethod->getDeclContext()) - ? AMK_ProtocolImplementation - : isa(newMethod->getDeclContext()) ? AMK_Redeclaration - : AMK_Override; + isa(oldMethod->getDeclContext()) + ? (oldMethod->isOptional() ? AMK_OptionalProtocolImplementation + : AMK_ProtocolImplementation) + : isa(newMethod->getDeclContext()) ? AMK_Redeclaration + : AMK_Override; mergeDeclAttributes(newMethod, oldMethod, MergeKind); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2300,6 +2300,7 @@ case AMK_Override: case AMK_ProtocolImplementation: + case AMK_OptionalProtocolImplementation: OverrideOrImpl = true; break; } @@ -2368,6 +2369,14 @@ diag::warn_mismatched_availability_override_unavail) << AvailabilityAttr::getPrettyPlatformName(Platform->getName()) << (AMK == AMK_Override); + } else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) { + // Allow different 'introduced' / 'obsoleted' availability versions + // on a method that implements an optional protocol requirement. It + // makes less sense to allow this for 'deprecated' as the user can't + // see if the method is 'deprecated' as 'respondsToSelector' will + // still return true when the method is deprecated. + ++i; + continue; } else { Diag(OldAA->getLocation(), diag::warn_mismatched_availability_override) Index: clang/test/SemaObjC/attr-availability.m =================================================================== --- clang/test/SemaObjC/attr-availability.m +++ clang/test/SemaObjC/attr-availability.m @@ -229,8 +229,7 @@ // inherited be implementations of those protocol methods. @protocol AvailabilityP2 @optional --(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}} \ -// expected-note 2{{protocol method is here}} +-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}} -(void)methodB __attribute__((unavailable)); // expected-note 4{{'methodB' has been explicitly marked unavailable here}} -(void)methodC; @end @@ -279,7 +278,7 @@ __attribute__((objc_root_class)) @interface ImplementsAvailabilityP2c --(void)methodA __attribute__((availability(macosx,introduced=10.2))); // expected-warning{{method introduced after the protocol method it implements on macOS (10.2 vs. 10.1)}} +-(void)methodA __attribute__((availability(macosx,introduced=10.2))); -(void)methodB __attribute__((unavailable)); @end @@ -288,7 +287,7 @@ @end @implementation ImplementsAvailabilityP2d --(void)methodA __attribute__((availability(macosx,introduced=10.2))) // expected-warning{{method introduced after the protocol method it implements on macOS (10.2 vs. 10.1)}} +-(void)methodA __attribute__((availability(macosx,introduced=10.2))) { } -(void)methodB __attribute__((unavailable)) { Index: clang/test/SemaObjC/override-opt-prop-availability.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/override-opt-prop-availability.m @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios11 -fsyntax-only -verify %s + +@protocol P + +@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=12.0))); // expected-note 2 {{is here}} + + + +@optional +@property (nonatomic) int myProp __attribute__((availability(ios, introduced=12.0))); // expected-note {{has been marked as being introduced in}} + +@optional +@property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=12.0))); // expected-note {{protocol method is here}} + +@optional +@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=12.0))); + +@optional +- (void) unavaibleInClass __attribute__((availability(ios, introduced=12.0))); // expected-note {{method is here}} + +@end + +@interface X

+ +@property (nonatomic) int myProp __attribute__((availability(ios, introduced=13.0))); // expected-note 2 {{has been marked as being introduced in}} + +@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=13.0))); // expected-warning 2 {{method introduced after the protocol method it implements on iOS}} + +@property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=10.0))); // expected-warning {{method deprecated before the protocol method it implements on iOS (12.0 vs. 10.0)}} expected-note {{been explicitly marked deprecated here}} + +@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=10.0))); // expected-note {{been explicitly marked unavailable here}} + +- (void) unavaibleInClass __attribute__((availability(ios, unavailable))); // expected-warning {{method cannot be unavailable on iOS when the protocol method it implements is available}} + +@end + + +void test(X *x) { + int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 13.0 or newer}} expected-note {{enclose}} + x.myProp = i; // expected-warning {{'setMyProp:' is only available on iOS 13.0 or newer}} expected-note {{enclose}} + int i2 = x.depProp; // expected-warning {{'depProp' is deprecated: first deprecated in iOS 10.0}} + int i3 = x.obsProp; // expected-error {{'obsProp' is unavailable: obsoleted in iOS 10.0}} +} + +void testProto(id

x) { + int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 12.0 or newer}} expected-note {{enclose}} +}