diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -2171,6 +2171,10 @@ data().ReferencedProtocols.set(List, Num, Locs, C); } + /// This is true iff the protocol is tagged with the `objc_static_protocol` + /// attribute. + bool isNonRuntimeProtocol() const; + ObjCProtocolDecl *lookupProtocolNamed(IdentifierInfo *PName); // Lookup a method. First, we search locally. If a method isn't diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2024,6 +2024,13 @@ let Documentation = [ObjCDirectMembersDocs]; } +def ObjCNonRuntimeProtocol : Attr { + let Spellings = [Clang<"objc_non_runtime_protocol">]; + let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCNonRuntimeProtocolDocs]; +} + def ObjCRuntimeName : Attr { let Spellings = [Clang<"objc_runtime_name">]; let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4491,6 +4491,21 @@ }]; } +def ObjCNonRuntimeProtocolDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +The ``objc_non_runtime_protocol`` attribute can be used to mark an Objective-C +to not generate runtime metadata. A non-runtime protocol is used only to +perform compile time checks on it's conformances. A standard protocol will emit +various chunks of metadata to enable dynamic runtime behaviors via, e.g., +``@protocol`` and ``objc_getProtocol``. These two tools require metadata +emitted into the binary that is loaded at runtime. With +``objc_non_runtime_protocol`` these chunks of metadata are removed. If you only +intend to use protocols to implement compile time behaviors then the metadata is +uneeded overhead. + }]; +} + def SelectAnyDocs : Documentation { let Category = DocCatDecl; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1034,6 +1034,8 @@ "string is ill-formed as UTF-8 and will become a null %0 when boxed">, InGroup; +def err_objc_non_runtime_protocol_in_protocol_expr : Error< + "cannot use a protocol declared 'objc_non_runtime_protocol' in a @protocol expression">; def err_objc_direct_on_protocol : Error< "'objc_direct' attribute cannot be applied to %select{methods|properties}0 " "declared in an Objective-C protocol">; @@ -1055,6 +1057,9 @@ def warn_objc_direct_property_ignored : Warning< "direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">, InGroup; +def warn_objc_non_runtime_protocol_ignored : Warning< + "non_runtime_protocol attribute on protocol %0 ignored (not implemented by this Objective-C runtime)">, + InGroup; def err_objc_direct_dynamic_property : Error< "direct property cannot be @dynamic">; diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1897,6 +1897,10 @@ return Result; } +bool ObjCProtocolDecl::isNonRuntimeProtocol() const { + return hasAttr(); +} + ObjCProtocolDecl *ObjCProtocolDecl::lookupProtocolNamed(IdentifierInfo *Name) { ObjCProtocolDecl *PDecl = this; diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -445,6 +445,29 @@ Method); } +static void +AppendFirstRuntimeProtocols(ObjCProtocolDecl *PD, + llvm::UniqueVector &PDs) { + if (!PD->isNonRuntimeProtocol()) { + PDs.insert(PD); + return; + } + + for (auto ParentPD : PD->protocols()) + AppendFirstRuntimeProtocols(ParentPD, PDs); +} + +llvm::UniqueVector +CGObjCRuntime::GetRuntimeProtocolList(ObjCProtocolDecl::protocol_iterator begin, + ObjCProtocolDecl::protocol_iterator end) { + llvm::UniqueVector PDs; + + for (; begin != end; ++begin) + AppendFirstRuntimeProtocols(*begin, PDs); + + return PDs; +} + /// Instead of '[[MyClass alloc] init]', try to generate /// 'objc_alloc_init(MyClass)'. This provides a code size improvement on the /// caller side, as well as the optimized objc_alloc. diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -1197,8 +1197,11 @@ } llvm::Constant *GenerateCategoryProtocolList(const ObjCCategoryDecl *OCD) override { - SmallVector Protocols; - for (const auto *PI : OCD->getReferencedProtocols()) + auto &ReferencedProtocols = OCD->getReferencedProtocols(); + auto RuntimeProtocols = GetRuntimeProtocolList(ReferencedProtocols.begin(), + ReferencedProtocols.end()); + SmallVector Protocols; + for (const auto *PI : RuntimeProtocols) Protocols.push_back( llvm::ConstantExpr::getBitCast(GenerateProtocolRef(PI), ProtocolPtrTy)); @@ -1381,7 +1384,9 @@ } SmallVector Protocols; - for (const auto *PI : PD->protocols()) + auto RuntimeProtocols = + GetRuntimeProtocolList(PD->protocol_begin(), PD->protocol_end()); + for (const auto *PI : RuntimeProtocols) Protocols.push_back( llvm::ConstantExpr::getBitCast(GenerateProtocolRef(PI), ProtocolPtrTy)); @@ -1920,8 +1925,10 @@ // struct objc_class *sibling_class classFields.addNullPointer(PtrTy); // struct objc_protocol_list *protocols; - SmallVector Protocols; - for (const auto *I : classDecl->protocols()) + auto RuntimeProtocols = GetRuntimeProtocolList(classDecl->protocol_begin(), + classDecl->protocol_end()); + SmallVector Protocols; + for (const auto *I : RuntimeProtocols) Protocols.push_back( llvm::ConstantExpr::getBitCast(GenerateProtocolRef(I), ProtocolPtrTy)); @@ -3088,6 +3095,9 @@ } void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { + if (PD->isNonRuntimeProtocol()) + return; + std::string ProtocolName = PD->getNameAsString(); // Use the protocol definition, if there is one. @@ -3240,8 +3250,10 @@ llvm::Constant *CGObjCGNU::GenerateCategoryProtocolList(const ObjCCategoryDecl *OCD) { + auto &RefPro = OCD->getReferencedProtocols(); + auto RuntimeProtos = GetRuntimeProtocolList(RefPro.begin(), RefPro.end()); SmallVector Protocols; - for (const auto *PD : OCD->getReferencedProtocols()) + for (const auto *PD : RuntimeProtos) Protocols.push_back(PD->getNameAsString()); return GenerateProtocolList(Protocols); } @@ -3527,8 +3539,11 @@ llvm::Constant *Properties = GeneratePropertyList(OID, ClassDecl); // Collect the names of referenced protocols + auto RefProtocols = ClassDecl->protocols(); + auto RuntimeProtocols = + GetRuntimeProtocolList(RefProtocols.begin(), RefProtocols.end()); SmallVector Protocols; - for (const auto *I : ClassDecl->protocols()) + for (const auto *I : RuntimeProtocols) Protocols.push_back(I->getNameAsString()); // Get the superclass pointer. diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -31,6 +31,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/UniqueVector.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/IntrinsicInst.h" @@ -3202,7 +3203,8 @@ ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end) { // Just return null for empty protocol lists - if (begin == end) + auto PDs = GetRuntimeProtocolList(begin, end); + if (PDs.empty()) return llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); ConstantInitBuilder builder(CGM); @@ -3215,9 +3217,9 @@ auto countSlot = values.addPlaceholder(); auto refsArray = values.beginArray(ObjCTypes.ProtocolPtrTy); - for (; begin != end; ++begin) { - refsArray.add(GetProtocolRef(*begin)); - } + for (auto Proto : PDs) + refsArray.add(GetProtocolRef(Proto)); + auto count = refsArray.size(); // This list is null terminated. @@ -6671,7 +6673,8 @@ // This routine is called for @protocol only. So, we must build definition // of protocol's meta-data (not a reference to it!) - // + assert(!PD->isNonRuntimeProtocol() && + "attempting to get a protocol ref to a static protocol."); llvm::Constant *Init = llvm::ConstantExpr::getBitCast(GetOrEmitProtocol(PD), ObjCTypes.getExternalProtocolPtrTy()); @@ -7028,6 +7031,8 @@ const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; + assert(!PD->isNonRuntimeProtocol() && + "attempting to GetOrEmit a non-runtime protocol"); if (!Entry) { // We use the initializer as a marker of whether this is a forward // reference or not. At module finalization we add the empty @@ -7171,10 +7176,19 @@ CGObjCNonFragileABIMac::EmitProtocolList(Twine Name, ObjCProtocolDecl::protocol_iterator begin, ObjCProtocolDecl::protocol_iterator end) { + // Just return null for empty protocol lists + auto Protocols = GetRuntimeProtocolList(begin, end); + if (Protocols.empty()) + return llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); + SmallVector ProtocolRefs; - // Just return null for empty protocol lists - if (begin == end) + for (auto PD : Protocols) + ProtocolRefs.push_back(GetProtocolRef(PD)); + + // If all of the protocols in the protocol list are objc_non_runtime_protocol + // just return null + if (ProtocolRefs.size() == 0) return llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); // FIXME: We shouldn't need to do this lookup here, should we? @@ -7191,8 +7205,8 @@ // A null-terminated array of protocols. auto array = values.beginArray(ObjCTypes.ProtocolnfABIPtrTy); - for (; begin != end; ++begin) - array.add(GetProtocolRef(*begin)); // Implemented??? + for (auto const &proto : ProtocolRefs) + array.add(proto); auto count = array.size(); array.addNullPointer(ObjCTypes.ProtocolnfABIPtrTy); diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h --- a/clang/lib/CodeGen/CGObjCRuntime.h +++ b/clang/lib/CodeGen/CGObjCRuntime.h @@ -20,6 +20,7 @@ #include "CGValue.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/IdentifierTable.h" // Selector +#include "llvm/ADT/UniqueVector.h" namespace llvm { class Constant; @@ -202,6 +203,13 @@ const CallArgList &CallArgs, const ObjCMethodDecl *Method = nullptr) = 0; + /// Walk the DAG of protocol references from a class, category or + /// protocol to find the list of protocols ended at either a runtime + /// protocol or a non-runtime protocol with no parents. + llvm::UniqueVector + GetRuntimeProtocolList(ObjCProtocolDecl::protocol_iterator begin, + ObjCProtocolDecl::protocol_iterator end); + /// Emit the code to return the named protocol as an object, as in a /// \@protocol expression. virtual llvm::Value *GenerateProtocolRef(CodeGenFunction &CGF, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2617,6 +2617,11 @@ D->addAttr(newAttr); } +static void handleObjCNonRuntimeProtocolAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + handleSimpleAttribute(S, D, AL); +} + static void handleObjCDirectAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // objc_direct cannot be set on methods declared in the context of a protocol if (isa(D->getDeclContext())) { @@ -7643,6 +7648,9 @@ case ParsedAttr::AT_ObjCDirect: handleObjCDirectAttr(S, D, AL); break; + case ParsedAttr::AT_ObjCNonRuntimeProtocol: + handleObjCNonRuntimeProtocolAttr(S, D, AL); + break; case ParsedAttr::AT_ObjCDirectMembers: handleObjCDirectMembersAttr(S, D, AL); handleSimpleAttribute(S, D, AL); diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1394,6 +1394,9 @@ Diag(ProtoLoc, diag::err_undeclared_protocol) << ProtocolId; return true; } + if (PDecl->isNonRuntimeProtocol()) + Diag(ProtoLoc, diag::err_objc_non_runtime_protocol_in_protocol_expr) + << PDecl; if (!PDecl->hasDefinition()) { Diag(ProtoLoc, diag::err_atprotocol_protocol) << PDecl; Diag(PDecl->getLocation(), diag::note_entity_declared_at) << PDecl; diff --git a/clang/test/CodeGenObjC/non-runtime-protocol.m b/clang/test/CodeGenObjC/non-runtime-protocol.m new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenObjC/non-runtime-protocol.m @@ -0,0 +1,118 @@ +// RUN: not %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -DPROTOEXPR -o - 2>&1 \ +// RUN: | FileCheck -check-prefix=PROTOEXPR %s + +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -o - \ +// RUN: | FileCheck -check-prefix=NONFRAGILE %s +// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %s -DINHERITANCE -o - \ +// RUN: | FileCheck -check-prefix=INHERITANCE %s + +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 %s -o - \ +// RUN: | FileCheck -check-prefix=FRAGILE %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-apple-darwin -fobjc-runtime=macosx-fragile-10.5 %s -DINHERITANCE -o - \ +// RUN: | FileCheck -check-prefix=FRAGILEINHERITANCE %s + +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu -fobjc-runtime=gnustep %s -o - \ +// RUN: | FileCheck -check-prefix=GNU %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu -fobjc-runtime=gnustep %s -DINHERITANCE -o - \ +// RUN: | FileCheck -check-prefix=GNUINHERITANCE %s +// +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu -fobjc-runtime=gnustep-2 %s -o - \ +// RUN: | FileCheck -check-prefix=GNU2 %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-linux-gnu -fobjc-runtime=gnustep-2 %s -DINHERITANCE -o - \ +// RUN: | FileCheck -check-prefix=GNU2INHERITANCE %s + +__attribute__((objc_root_class)) +@interface Root +@end +@implementation Root +@end + +#ifndef INHERITANCE +// Confirm that we're not emitting protocol information for the +// NONFRAGILE-NOT: OBJC_CLASS_NAME{{.*}}NonRuntimeProtocol +// NONFRAGILE-NOT: _OBJC_$_PROTOCOL_INSTANCE_METHODS_NonRuntimeProtocol +// NONFRAGILE-NOT: _OBJC_$_PROTOCOL_CLASS_METHODS_NonRuntimeProtocol +// NONFRAGILE-NOT: _OBJC_PROTOCOL_$_NonRuntimeProtocol +// NONFRAGILE-NOT: _OBJC_LABEL_PROTOCOL_$_NonRuntimeProtocol +// NONFRAGILE-NOT: _OBJC_CLASS_PROTOCOLS_$_NonRuntimeImplementer +// FRAGILE-NOT: OBJC_CLASS_NAME_.{{.*}}"Runtime\00" +// FRAGILE-NOT: OBJC_PROTOCOL_NonRuntime +// FRAGILE_NOT: OBJC_PROTOCOLS_NonRuntimeImplementer +// GNU-NOT: private unnamed_addr constant {{.*}} c"NonRuntimeProtocol\00" +// GNU-NOT: @.objc_protocol {{.*}} +// GNU2-NOT: private unnamed_addr constant {{.*}} c"NonRuntimeProtocol\00" +// GNU2-NOT: @.objc_protocol {{.*}} +__attribute__((objc_non_runtime_protocol)) +@protocol NonRuntimeProtocol +- (void)doThing; ++ (void)doClassThing; +@end +// NONFRAGILE: @"_OBJC_METACLASS_RO_$_NonRuntimeImplementer" {{.*}} %struct._objc_protocol_list* null +// NONFRAGILE: @"_OBJC_CLASS_RO_$_NonRuntimeImplementer" {{.*}} %struct._objc_protocol_list* null +@interface NonRuntimeImplementer : Root +- (void)doThing; ++ (void)doClassThing; +@end + +@implementation NonRuntimeImplementer +- (void)doThing { +} ++ (void)doClassThing { +} +@end + +void useNonRuntime(NonRuntimeImplementer *si) { + [si doThing]; + [NonRuntimeImplementer doClassThing]; + +#ifdef PROTOEXPR + // PROTOEXPR: cannot use a protocol declared 'objc_non_runtime_protocol' in a @protocol expression + Protocol *p = @protocol(NonRuntimeProtocol); +#endif +} +#endif + +#ifdef INHERITANCE +// Confirm that we only emit references to the non-runtime protocols and +// properly walk the DAG to find the right protocols. +// INHERITANCE: OBJC_PROTOCOL_$_R3{{.*}} +// INHERITANCE: OBJC_PROTOCOL_$_R2{{.*}} +// INHERITANCE: @"_OBJC_CLASS_PROTOCOLS_$_Implementer" {{.*}}_OBJC_PROTOCOL_$_R{{[23]}}{{.*}}_OBJC_PROTOCOL_$_R{{[23]}} + +// FRAGILEINHERITANCE: OBJC_PROTOCOL_R3 +// FRAGILEINHERITANCE: OBJC_PROTOCOL_R2 +// FRAGILEINHERITANCE: OBJC_CLASS_PROTOCOLS_Implementer{{.*}}OBJC_PROTOCOL_R3{{.*}}OBJC_PROTOCOL_R2 + +// GNUINHERITANCE-DAG: @[[Proto1:[0-9]]]{{.*}}c"R1\00" +// GNUINHERITANCE-DAG: [[P1Name:@.objc_protocol.[0-9]*]]{{.*}}@[[Proto1]] +// GNUINHERITANCE-DAG: @[[Proto2:[0-9]]]{{.*}}c"R2\00" +// GNUINHERITANCE-DAG: [[P2Name:@.objc_protocol.[0-9]+]]{{.*}}@[[Proto2]] +// GNUINHERITANCE-DAG: @[[Proto3:[0-9]]]{{.*}}c"R3\00" +// GNUINHERITANCE-DAG: [[P3Name:@.objc_protocol.[0-9]+]]{{.*}}@[[Proto3]] +// GNUINHERITANCE-DAG: @.objc_protocol_list{{.*}} +// GNUINHERITANCE: @.objc_protocol_list{{.*}}[[Proto3]]{{.*}}[[Proto2]] + +// GNU2INHERITANCE-DAG: @[[Proto1:[0-9]]]{{.*}}c"R1\00" +// GNU2INHERITANCE-DAG: _OBJC_PROTOCOL_R1{{.*}}@[[Proto1]] +// GNU2INHERITANCE-DAG: @[[Proto2:[0-9]]]{{.*}}c"R2\00" +// GNU2INHERITANCE-DAG: _OBJC_PROTOCOL_R2{{.*}}@[[Proto2]] +// GNU2INHERITANCE-DAG: @[[Proto3:[0-9]]]{{.*}}c"R3\00" +// GNU2INHERITANCE-DAG: _OBJC_PROTOCOL_R3{{.*}}@[[Proto3]] +// GNU2INHERITANCE: @.objc_protocol_list{{.*}}_OBJC_PROTOCOL_R3{{.*}}_OBJC_PROTOCOL_R2 +@protocol R1 +@end +@protocol R2 +@end +@protocol R3 +@end +__attribute__((objc_non_runtime_protocol)) @protocol N3 +@end +__attribute__((objc_non_runtime_protocol)) @protocol N1 +@end +__attribute__((objc_non_runtime_protocol)) @protocol N2 +@end +@interface Implementer : Root +@end +@implementation Implementer +@end +#endif diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -118,6 +118,7 @@ // CHECK-NEXT: ObjCExternallyRetained (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function, SubjectMatchRule_block, SubjectMatchRule_objc_method) // CHECK-NEXT: ObjCMethodFamily (SubjectMatchRule_objc_method) // CHECK-NEXT: ObjCNonLazyClass (SubjectMatchRule_objc_interface, SubjectMatchRule_objc_implementation) +// CHECK-NEXT: ObjCNonRuntimeProtocol (SubjectMatchRule_objc_protocol) // CHECK-NEXT: ObjCPreciseLifetime (SubjectMatchRule_variable) // CHECK-NEXT: ObjCRequiresPropertyDefs (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCRequiresSuper (SubjectMatchRule_objc_method)