r202948 - Adjust logic for 'objc_protocol_requires_explicit_implementation' for inherited protocols and protocols already conformed in the class hierarchy.

Ted Kremenek kremenek at apple.com
Wed Mar 5 00:13:09 PST 2014


Author: kremenek
Date: Wed Mar  5 02:13:08 2014
New Revision: 202948

URL: http://llvm.org/viewvc/llvm-project?rev=202948&view=rev
Log:
Adjust logic for 'objc_protocol_requires_explicit_implementation' for inherited protocols and protocols already conformed in the class hierarchy.

Per more discussion, 'objc_protocol_requires_explicit_implementation' is
refinement that it mainly adds that requirement that a protocol must be
explicitly satisfied at the moment the first class in the class hierarchy
conforms to it.  Any subclasses that also conform to that protocol,
either directly or via conforming to a protocol that inherits that protocol,
do not need to re-implement that protocol.

Doing this requires first doing a pass on the super class hierarchy,
gathering the set of protocols conformed to by the super classes,
and then culling those out when determining conformance.  This
two-pass algorithm could be generalized for all protocol checking,
and could possibly be a performance win in some cases.  For now
we restrict this change to protocols with this attribute to isolate
the change in logic (especially as the design continues to evolve).

This change needs to be adjusted for properties as well; this
only impacts methods right now.

Modified:
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/test/SemaObjC/protocols-suppress-conformance.m

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=202948&r1=202947&r2=202948&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Wed Mar  5 02:13:08 2014
@@ -1632,6 +1632,26 @@ void Sema::WarnExactTypedMethods(ObjCMet
 /// we used an immutable set to keep the table then it wouldn't add significant
 /// memory cost and it would be handy for lookups.
 
+typedef llvm::DenseSet<IdentifierInfo*> ProtocolNameSet;
+typedef llvm::OwningPtr<ProtocolNameSet> LazyProtocolNameSet;
+
+/// Recursively populates a set with all conformed protocols in a class
+/// hierarchy that have the 'objc_protocol_requires_explicit_implementation'
+/// attribute.
+static void findProtocolsWithExplicitImpls(const ObjCInterfaceDecl *Super,
+                                           ProtocolNameSet &PNS) {
+  if (!Super)
+    return;
+
+  for (ObjCInterfaceDecl::all_protocol_iterator
+        I = Super->all_referenced_protocol_begin(),
+        E = Super->all_referenced_protocol_end(); I != E; ++I) {
+    const ObjCProtocolDecl *PDecl = *I;
+    if (PDecl->hasAttr<ObjCExplicitProtocolImplAttr>())
+      PNS.insert(PDecl->getIdentifier());
+  }
+}
+
 /// CheckProtocolMethodDefs - This routine checks unimplemented methods
 /// Declared in protocol, and those referenced by it.
 static void CheckProtocolMethodDefs(Sema &S,
@@ -1641,7 +1661,7 @@ static void CheckProtocolMethodDefs(Sema
                                     const Sema::SelectorSet &InsMap,
                                     const Sema::SelectorSet &ClsMap,
                                     ObjCContainerDecl *CDecl,
-                                    bool isExplicitProtocol = true) {
+                                    LazyProtocolNameSet &ProtocolsExplictImpl) {
   ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl);
   ObjCInterfaceDecl *IDecl = C ? C->getClassInterface() 
                                : dyn_cast<ObjCInterfaceDecl>(CDecl);
@@ -1649,6 +1669,32 @@ static void CheckProtocolMethodDefs(Sema
   
   ObjCInterfaceDecl *Super = IDecl->getSuperClass();
   ObjCInterfaceDecl *NSIDecl = 0;
+
+  // If this protocol is marked 'objc_protocol_requires_explicit_implementation'
+  // then we should check if any class in the super class hierarchy also
+  // conforms to this protocol, either directly or via protocol inheritance.
+  // If so, we can skip checking this protocol completely because we
+  // know that a parent class already satisfies this protocol.
+  //
+  // Note: we could generalize this logic for all protocols, and merely
+  // add the limit on looking at the super class chain for just
+  // specially marked protocols.  This may be a good optimization.  This
+  // change is restricted to 'objc_protocol_requires_explicit_implementation'
+  // protocols for now for controlled evaluation.
+  if (PDecl->hasAttr<ObjCExplicitProtocolImplAttr>()) {
+    if (!ProtocolsExplictImpl.isValid()) {
+      ProtocolsExplictImpl.reset(new ProtocolNameSet);
+      findProtocolsWithExplicitImpls(Super, *ProtocolsExplictImpl);
+    }
+    if (ProtocolsExplictImpl->find(PDecl->getIdentifier()) !=
+        ProtocolsExplictImpl->end())
+      return;
+
+    // If no super class conforms to the protocol, we should not search
+    // for methods in the super class to implicitly satisfy the protocol.
+    Super = NULL;
+  }
+
   if (S.getLangOpts().ObjCRuntime.isNeXTFamily()) {
     // check to see if class implements forwardInvocation method and objects
     // of this class are derived from 'NSProxy' so that to forward requests
@@ -1674,8 +1720,6 @@ static void CheckProtocolMethodDefs(Sema
   // the method was implemented by a base class or an inherited
   // protocol. This lookup is slow, but occurs rarely in correct code
   // and otherwise would terminate in a warning.
-  if (isExplicitProtocol && PDecl->hasAttr<ObjCExplicitProtocolImplAttr>())
-    Super = NULL;
 
   // check unimplemented instance methods.
   if (!NSIDecl)
@@ -1744,7 +1788,7 @@ static void CheckProtocolMethodDefs(Sema
   for (ObjCProtocolDecl::protocol_iterator PI = PDecl->protocol_begin(),
        E = PDecl->protocol_end(); PI != E; ++PI)
     CheckProtocolMethodDefs(S, ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap,
-                            CDecl, /* isExplicitProtocl */ false);
+                            CDecl, ProtocolsExplictImpl);
 }
 
 /// MatchAllMethodDeclarations - Check methods declared in interface
@@ -1958,12 +2002,15 @@ void Sema::ImplMethodsVsClassMethods(Sco
   // Check and see if class methods in class interface have been
   // implemented in the implementation class.
 
+  LazyProtocolNameSet ExplicitImplProtocols;
+
   if (ObjCInterfaceDecl *I = dyn_cast<ObjCInterfaceDecl> (CDecl)) {
     for (ObjCInterfaceDecl::all_protocol_iterator
           PI = I->all_referenced_protocol_begin(),
           E = I->all_referenced_protocol_end(); PI != E; ++PI)
       CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), *PI,
-                              IncompleteImpl, InsMap, ClsMap, I);
+                              IncompleteImpl, InsMap, ClsMap, I,
+                              ExplicitImplProtocols);
     // Check class extensions (unnamed categories)
     for (ObjCInterfaceDecl::visible_extensions_iterator
            Ext = I->visible_extensions_begin(),
@@ -1978,7 +2025,8 @@ void Sema::ImplMethodsVsClassMethods(Sco
       for (ObjCCategoryDecl::protocol_iterator PI = C->protocol_begin(),
            E = C->protocol_end(); PI != E; ++PI)
         CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), *PI,
-                                IncompleteImpl, InsMap, ClsMap, CDecl);
+                                IncompleteImpl, InsMap, ClsMap, CDecl,
+                                ExplicitImplProtocols);
       DiagnoseUnimplementedProperties(S, IMPDecl, CDecl,
                                       /* SynthesizeProperties */ false);
     } 

Modified: cfe/trunk/test/SemaObjC/protocols-suppress-conformance.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/protocols-suppress-conformance.m?rev=202948&r1=202947&r2=202948&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/protocols-suppress-conformance.m (original)
+++ cfe/trunk/test/SemaObjC/protocols-suppress-conformance.m Wed Mar  5 02:13:08 2014
@@ -4,7 +4,7 @@
 // to be explicitly implemented in the adopting class.
 __attribute__((objc_protocol_requires_explicit_implementation))
 @protocol Protocol
-- (void) theBestOfTimes; // expected-note 2 {{method 'theBestOfTimes' declared here}}
+- (void) theBestOfTimes; // expected-note {{method 'theBestOfTimes' declared here}}
 @property (readonly) id theWorstOfTimes; // expected-note {{property declared here}}
 @end
 
@@ -16,12 +16,11 @@ __attribute__((objc_protocol_requires_ex
 @property (readonly) id theWorstOfTimes;
 @end
 
-// This class subclasses ClassA (which adopts 'Protocol'),
-// but does not provide the needed implementation.
+// This class subclasses ClassA (which also adopts 'Protocol').
 @interface ClassB : ClassA <Protocol>
 @end
 
- at implementation ClassB // expected-warning {{method 'theBestOfTimes' in protocol 'Protocol' not implemented}} expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation}}
+ at implementation ClassB // expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation}}
 @end
 
 @interface ClassB_Good : ClassA <Protocol>
@@ -171,4 +170,39 @@ __attribute__((objc_protocol_requires_ex
 __attribute__((objc_protocol_requires_explicit_implementation))  // expected-error{{attribute 'objc_protocol_requires_explicit_implementation' can only be applied to @protocol definitions, not forward declarations}}
 @protocol NotDefined;
 
+// Another complete hierarchy.
+ __attribute__((objc_protocol_requires_explicit_implementation))
+ at protocol Ex2FooBar
+- (void)methodA;
+ at end
+
+ __attribute__((objc_protocol_requires_explicit_implementation))
+ at protocol Ex2ProtocolA
+- (void)methodB; // expected-note {{method 'methodB' declared here}}
+ at end
+
+ __attribute__((objc_protocol_requires_explicit_implementation))
+ at protocol Ex2ProtocolB <Ex2ProtocolA>
+- (void)methodA; // expected-note {{method 'methodA' declared here}}
+ at end
+
+// NOT required
+ at protocol Ex2ProtocolC <Ex2ProtocolA>
+- (void)methodB;
+- (void)methodA;
+ at end
+
+ at interface Ex2ClassA <Ex2ProtocolC, Ex2FooBar>
+ at end
+ at implementation Ex2ClassA
+- (void)methodB {}
+- (void)methodA {}
+ at end
+
+ at interface Ex2ClassB : Ex2ClassA <Ex2ProtocolB>
+ at end
+
+ at implementation Ex2ClassB // expected-warning {{method 'methodB' in protocol 'Ex2ProtocolA' not implemented}}\
+                          // expected-warning {{method 'methodA' in protocol 'Ex2ProtocolB' not implemented}}
+ at end
 





More information about the cfe-commits mailing list