[clang] 50be48b - [clang][ObjC] Allow different availability annotation on a method

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Wed May 19 12:14:04 PDT 2021


Author: Alex Lorenz
Date: 2021-05-19T12:13:57-07:00
New Revision: 50be48b0f3c884a87ddf19c7c51abcab035c1efb

URL: https://github.com/llvm/llvm-project/commit/50be48b0f3c884a87ddf19c7c51abcab035c1efb
DIFF: https://github.com/llvm/llvm-project/commit/50be48b0f3c884a87ddf19c7c51abcab035c1efb.diff

LOG: [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

Added: 
    clang/test/SemaObjC/override-opt-prop-availability.m

Modified: 
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/SemaObjC/attr-availability.m

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b6143d1d31789..9c459a95a6916 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3255,6 +3255,9 @@ class Sema final {
     /// 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.

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c4db147e7efde..90fae1d9c9286 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2608,7 +2608,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
     NewAttr = nullptr;
   else if ((isa<DeprecatedAttr>(Attr) || isa<UnavailableAttr>(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<UuidAttr>(Attr))
     NewAttr = S.mergeUuidAttr(D, *UA, UA->getGuid(), UA->getGuidDecl());
@@ -2956,6 +2957,7 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
       case AMK_Redeclaration:
       case AMK_Override:
       case AMK_ProtocolImplementation:
+      case AMK_OptionalProtocolImplementation:
         LocalAMK = AMK;
         break;
       }
@@ -3861,10 +3863,11 @@ void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod,
                                 ObjCMethodDecl *oldMethod) {
   // Merge the attributes, including deprecated/unavailable
   AvailabilityMergeKind MergeKind =
-    isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
-      ? AMK_ProtocolImplementation
-      : isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
-                                                       : AMK_Override;
+      isa<ObjCProtocolDecl>(oldMethod->getDeclContext())
+          ? (oldMethod->isOptional() ? AMK_OptionalProtocolImplementation
+                                     : AMK_ProtocolImplementation)
+          : isa<ObjCImplDecl>(newMethod->getDeclContext()) ? AMK_Redeclaration
+                                                           : AMK_Override;
 
   mergeDeclAttributes(newMethod, oldMethod, MergeKind);
 

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index c470598ef4f84..4bbbcf985cbd3 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2300,6 +2300,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
 
   case AMK_Override:
   case AMK_ProtocolImplementation:
+  case AMK_OptionalProtocolImplementation:
     OverrideOrImpl = true;
     break;
   }
@@ -2368,6 +2369,14 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(
                  diag::warn_mismatched_availability_override_unavail)
               << AvailabilityAttr::getPrettyPlatformName(Platform->getName())
               << (AMK == AMK_Override);
+          } else if (Which != 1 && AMK == AMK_OptionalProtocolImplementation) {
+            // Allow 
diff erent '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)

diff  --git a/clang/test/SemaObjC/attr-availability.m b/clang/test/SemaObjC/attr-availability.m
index eb25175c5a4a0..cb896150574ee 100644
--- a/clang/test/SemaObjC/attr-availability.m
+++ b/clang/test/SemaObjC/attr-availability.m
@@ -229,8 +229,7 @@ void use_myEnum() {
 // 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 @@ void testImplementsAvailabilityP2b(ImplementsAvailabilityP2b *obj) {
 
 __attribute__((objc_root_class))
 @interface ImplementsAvailabilityP2c <AvailabilityP2>
--(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 @@ @interface ImplementsAvailabilityP2d <AvailabilityP2>
 @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)) {

diff  --git a/clang/test/SemaObjC/override-opt-prop-availability.m b/clang/test/SemaObjC/override-opt-prop-availability.m
new file mode 100644
index 0000000000000..e18a9684a64b9
--- /dev/null
+++ b/clang/test/SemaObjC/override-opt-prop-availability.m
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios11 -fsyntax-only -verify %s
+
+ at protocol P
+
+ at property (nonatomic) int reqProp __attribute__((availability(ios, introduced=12.0))); // expected-note 2 {{is here}}
+
+
+
+ at optional
+ at property (nonatomic) int myProp __attribute__((availability(ios, introduced=12.0))); // expected-note {{has been marked as being introduced in}}
+
+ at optional
+ at property (nonatomic, readonly) int depProp __attribute__((availability(ios, introduced=8.0, deprecated=12.0))); // expected-note {{protocol method is here}}
+
+ at optional
+ at property (nonatomic) int obsProp __attribute__((availability(ios, introduced=8.0, obsoleted=12.0)));
+
+ at optional
+- (void) unavaibleInClass __attribute__((availability(ios, introduced=12.0))); // expected-note {{method is here}}
+
+ at end
+
+ at interface X <P>
+
+ at property (nonatomic) int myProp __attribute__((availability(ios, introduced=13.0))); // expected-note 2 {{has been marked as being introduced in}}
+
+ at property (nonatomic) int reqProp __attribute__((availability(ios, introduced=13.0))); // expected-warning 2 {{method introduced after the protocol method it implements on iOS}}
+
+ at 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}}
+
+ at 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}}
+
+ at 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<P> x) {
+  int i = x.myProp; // expected-warning {{'myProp' is only available on iOS 12.0 or newer}} expected-note {{enclose}}
+}


        


More information about the cfe-commits mailing list