Skip to content

Commit efb102a

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 (cherry picked from commit 50be48b)
1 parent ab6d133 commit efb102a

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
@@ -3135,6 +3135,9 @@ class Sema final {
31353135
/// Merge availability attributes for an implementation of
31363136
/// a protocol requirement.
31373137
AMK_ProtocolImplementation,
3138+
/// Merge availability attributes for an implementation of
3139+
/// an optional protocol requirement.
3140+
AMK_OptionalProtocolImplementation
31383141
};
31393142

31403143
/// 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
@@ -2602,7 +2602,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26022602
NewAttr = nullptr;
26032603
else if ((isa<DeprecatedAttr>(Attr) || isa<UnavailableAttr>(Attr)) &&
26042604
(AMK == Sema::AMK_Override ||
2605-
AMK == Sema::AMK_ProtocolImplementation))
2605+
AMK == Sema::AMK_ProtocolImplementation ||
2606+
AMK == Sema::AMK_OptionalProtocolImplementation))
26062607
NewAttr = nullptr;
26072608
else if (isa<SwiftPrivateAttr>(Attr) && AMK == Sema::AMK_Override)
26082609
NewAttr = nullptr;
@@ -2951,6 +2952,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
29512952
case AMK_Redeclaration:
29522953
case AMK_Override:
29532954
case AMK_ProtocolImplementation:
2955+
case AMK_OptionalProtocolImplementation:
29542956
LocalAMK = AMK;
29552957
break;
29562958
}
@@ -3852,10 +3854,11 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
38523854
ObjCMethodDecl *oldMethod) {
38533855
// Merge the attributes, including deprecated/unavailable
38543856
AvailabilityMergeKind MergeKind =
3855-
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
3856-
? AMK_ProtocolImplementation
3857-
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
3858-
: AMK_Override;
3857+
isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
3858+
? (oldMethod->isOptional() ? AMK_OptionalProtocolImplementation
3859+
: AMK_ProtocolImplementation)
3860+
: isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
3861+
: AMK_Override;
38593862

38603863
mergeDeclAttributes(newMethod, oldMethod, MergeKind);
38613864

clang/lib/Sema/SemaDeclAttr.cpp

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

23592359
case AMK_Override:
23602360
case AMK_ProtocolImplementation:
2361+
case AMK_OptionalProtocolImplementation:
23612362
OverrideOrImpl = true;
23622363
break;
23632364
}
@@ -2426,6 +2427,14 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
24262427
diag::warn_mismatched_availability_override_unavail)
24272428
<< AvailabilityAttr::getPrettyPlatformName(Platform->getName())
24282429
<< (AMK == AMK_Override);
2430+
} else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) {
2431+
// Allow different 'introduced' / 'obsoleted' availability versions
2432+
// on a method that implements an optional protocol requirement. It
2433+
// makes less sense to allow this for 'deprecated' as the user can't
2434+
// see if the method is 'deprecated' as 'respondsToSelector' will
2435+
// still return true when the method is deprecated.
2436+
++i;
2437+
continue;
24292438
} else {
24302439
Diag(OldAA->getLocation(),
24312440
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)