Skip to content

Commit 50be48b

Browse files
committed
[clang][ObjC] Allow different availability annotation on a method
when implementing an optional protocol requirement When an Objective-C method implements an optional protocol requirement, allow the method to use a newer introduced or older obsoleted availability version than what's specified on the method in the protocol itself. This allows SDK adopters to adopt an optional method from a protocol later than when the method is introduced in the protocol. The users that call an optional method on an object that conforms to this protocol are supposed to check whether the object implements the method or not, so a lack of appropriate `if (@available)` check for a new OS version is not a cause of concern as there's already another runtime check that's required. Differential Revision: https://reviews.llvm.org/D102459
1 parent 2db182f commit 50be48b

File tree

5 files changed

+70
-9
lines changed

5 files changed

+70
-9
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3255,6 +3255,9 @@ class Sema final {
32553255
/// Merge availability attributes for an implementation of
32563256
/// a protocol requirement.
32573257
AMK_ProtocolImplementation,
3258+
/// Merge availability attributes for an implementation of
3259+
/// an optional protocol requirement.
3260+
AMK_OptionalProtocolImplementation
32583261
};
32593262

32603263
/// Describes the kind of priority given to an availability attribute.

clang/lib/Sema/SemaDecl.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,7 +2608,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26082608
NewAttr = nullptr;
26092609
else if ((isa<DeprecatedAttr>(Attr) || isa<UnavailableAttr>(Attr)) &&
26102610
(AMK == Sema::AMK_Override ||
2611-
AMK == Sema::AMK_ProtocolImplementation))
2611+
AMK == Sema::AMK_ProtocolImplementation ||
2612+
AMK == Sema::AMK_OptionalProtocolImplementation))
26122613
NewAttr = nullptr;
26132614
else if (const auto *UA = dyn_cast<UuidAttr>(Attr))
26142615
NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl());
@@ -2956,6 +2957,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
29562957
case AMK_Redeclaration:
29572958
case AMK_Override:
29582959
case AMK_ProtocolImplementation:
2960+
case AMK_OptionalProtocolImplementation:
29592961
LocalAMK = AMK;
29602962
break;
29612963
}
@@ -3861,10 +3863,11 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
38613863
ObjCMethodDecl *oldMethod) {
38623864
// Merge the attributes, including deprecated/unavailable
38633865
AvailabilityMergeKind MergeKind =
3864-
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
3865-
? AMK_ProtocolImplementation
3866-
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
3867-
: AMK_Override;
3866+
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
3867+
? (oldMethod->isOptional() ? AMK_OptionalProtocolImplementation
3868+
: AMK_ProtocolImplementation)
3869+
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
3870+
: AMK_Override;
38683871

38693872
mergeDeclAttributes(newMethod, oldMethod, MergeKind);
38703873

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
23002300

23012301
case AMK_Override:
23022302
case AMK_ProtocolImplementation:
2303+
case AMK_OptionalProtocolImplementation:
23032304
OverrideOrImpl = true;
23042305
break;
23052306
}
@@ -2368,6 +2369,14 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
23682369
diag::warn_mismatched_availability_override_unavail)
23692370
<< AvailabilityAttr::getPrettyPlatformName(Platform->getName())
23702371
<< (AMK == AMK_Override);
2372+
} else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) {
2373+
// Allow different 'introduced' / 'obsoleted' availability versions
2374+
// on a method that implements an optional protocol requirement. It
2375+
// makes less sense to allow this for 'deprecated' as the user can't
2376+
// see if the method is 'deprecated' as 'respondsToSelector' will
2377+
// still return true when the method is deprecated.
2378+
++i;
2379+
continue;
23712380
} else {
23722381
Diag(OldAA->getLocation(),
23732382
diag::warn_mismatched_availability_override)

clang/test/SemaObjC/attr-availability.m

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ void use_myEnum() {
229229
// inherited be implementations of those protocol methods.
230230
@protocol AvailabilityP2
231231
@optional
232-
-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}} \
233-
// expected-note 2{{protocol method is here}}
232+
-(void)methodA __attribute__((availability(macosx,introduced=10.1,deprecated=10.2))); // expected-note 4{{'methodA' has been explicitly marked deprecated here}}
234233
-(void)methodB __attribute__((unavailable)); // expected-note 4{{'methodB' has been explicitly marked unavailable here}}
235234
-(void)methodC;
236235
@end
@@ -279,7 +278,7 @@ void testImplementsAvailabilityP2b(ImplementsAvailabilityP2b *obj) {
279278

280279
__attribute__((objc_root_class))
281280
@interface ImplementsAvailabilityP2c <AvailabilityP2>
282-
-(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)}}
281+
-(void)methodA __attribute__((availability(macosx,introduced=10.2)));
283282
-(void)methodB __attribute__((unavailable));
284283
@end
285284

@@ -288,7 +287,7 @@ @interface ImplementsAvailabilityP2d <AvailabilityP2>
288287
@end
289288

290289
@implementation ImplementsAvailabilityP2d
291-
-(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)}}
290+
-(void)methodA __attribute__((availability(macosx,introduced=10.2)))
292291
{
293292
}
294293
-(void)methodB __attribute__((unavailable)) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %clang_cc1 -triple arm64-apple-ios11 -fsyntax-only -verify %s
2+
3+
@protocol P
4+
5+
@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=12.0))); // expected-note 2 {{is here}}
6+
7+
8+
9+
@optional
10+
@property (nonatomic) int myProp __attribute__((availability(ios, introduced=12.0))); // expected-note {{has been marked as being introduced in}}
11+
12+
@optional
13+
@property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=12.0))); // expected-note {{protocol method is here}}
14+
15+
@optional
16+
@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=12.0)));
17+
18+
@optional
19+
- (void) unavaibleInClass __attribute__((availability(ios, introduced=12.0))); // expected-note {{method is here}}
20+
21+
@end
22+
23+
@interface X <P>
24+
25+
@property (nonatomic) int myProp __attribute__((availability(ios, introduced=13.0))); // expected-note 2 {{has been marked as being introduced in}}
26+
27+
@property (nonatomic) int reqProp __attribute__((availability(ios, introduced=13.0))); // expected-warning 2 {{method introduced after the protocol method it implements on iOS}}
28+
29+
@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}}
30+
31+
@property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=10.0))); // expected-note {{been explicitly marked unavailable here}}
32+
33+
- (void) unavaibleInClass __attribute__((availability(ios, unavailable))); // expected-warning {{method cannot be unavailable on iOS when the protocol method it implements is available}}
34+
35+
@end
36+
37+
38+
void test(X *x) {
39+
int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 13.0 or newer}} expected-note {{enclose}}
40+
x.myProp = i; // expected-warning {{'setMyProp:' is only available on iOS 13.0 or newer}} expected-note {{enclose}}
41+
int i2 = x.depProp; // expected-warning {{'depProp' is deprecated: first deprecated in iOS 10.0}}
42+
int i3 = x.obsProp; // expected-error {{'obsProp' is unavailable: obsoleted in iOS 10.0}}
43+
}
44+
45+
void testProto(id<P> x) {
46+
int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 12.0 or newer}} expected-note {{enclose}}
47+
}

0 commit comments

Comments
 (0)