Index: clang/include/clang/AST/DeclObjC.h =================================================================== --- clang/include/clang/AST/DeclObjC.h +++ clang/include/clang/AST/DeclObjC.h @@ -408,7 +408,7 @@ /// \return the type for \c self and set \arg selfIsPseudoStrong and /// \arg selfIsConsumed accordingly. QualType getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID, - bool &selfIsPseudoStrong, bool &selfIsConsumed); + bool &selfIsPseudoStrong, bool &selfIsConsumed) const; ImplicitParamDecl * getSelfDecl() const { return SelfDecl; } void setSelfDecl(ImplicitParamDecl *SD) { SelfDecl = SD; } @@ -466,6 +466,9 @@ ObjCMethodDeclBits.HasSkippedBody = Skipped; } + /// True if the method is tagged as objc_direct + bool isDirectMethod() const; + /// Returns the property associated with this method's selector. /// /// Note that even if this particular method is not marked as a property @@ -747,13 +750,14 @@ /// property attribute rather than a type qualifier. OBJC_PR_nullability = 0x1000, OBJC_PR_null_resettable = 0x2000, - OBJC_PR_class = 0x4000 + OBJC_PR_class = 0x4000, + OBJC_PR_direct = 0x8000 // Adding a property should change NumPropertyAttrsBits }; enum { /// Number of bits fitting all the property attributes. - NumPropertyAttrsBits = 15 + NumPropertyAttrsBits = 16 }; enum SetterKind { Assign, Retain, Copy, Weak }; @@ -876,6 +880,7 @@ bool isInstanceProperty() const { return !isClassProperty(); } bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; } + bool isDirectProperty() const { return PropertyAttributes & OBJC_PR_direct; } ObjCPropertyQueryKind getQueryKind() const { return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class : Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1852,6 +1852,20 @@ let Documentation = [Undocumented]; } +def ObjCDirect : Attr { + let Spellings = [Clang<"objc_direct">]; + let Subjects = SubjectList<[ObjCMethod, ObjCCategory], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCDirectDocs]; +} + +def ObjCDirectMembers : Attr { + let Spellings = [Clang<"objc_direct_members">]; + let Subjects = SubjectList<[ObjCImpl], ErrorDiag>; + let LangOpts = [ObjC]; + let Documentation = [ObjCDirectMembersDocs]; +} + def ObjCRuntimeName : Attr { let Spellings = [Clang<"objc_runtime_name">]; let Subjects = SubjectList<[ObjCInterface, ObjCProtocol], ErrorDiag>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3897,6 +3897,66 @@ }]; } +def ObjCDirectDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute can be added on methods declaration and implementation, +categories, or extensions (for the latter two it applies the attribute +to all methods declared in the category or extension), + +This attribute can be added to an Objective-C method to mark it as being +called directly. It can also be used on Objective-C Categories and Extensions, +in which case it applies to all methods declared in such an ``@interface``. + +If an Objective-C property is declared with the `direct` property attribute, +then its getter and setter are declared to be direct unless +they are explicitly declared. + +A message send to a direct method calls the implementation directly, as if it +were a C function, rather than using ordinary Objective-C method dispatch. This +is substantially faster and potentially allows the implementation to be inlined, +but it also means the method cannot be overridden in subclasses or replaced +dynamically, as ordinary Objective-C methods can. + +Furthermore, a direct method is not listed in the class's method lists. This +substantially reduces the code-size overhead of the method but also means it +cannot be called dynamically using ordinary Objective-C method dispatch at all; +in particular, this means that it cannot override a superclass method or satisfy +a protocol requirement. + +Clang will diagnose attempts to override direct methods, override non-direct +methods with direct methods, implement a protocol requirement with a direct +method, or use a selector that is only known to be used for direct methods. + +Symbols for direct method implementations are implicitly given hidden +visibility, meaning that they can only be called within the same linkage unit. + +Although a direct method is called directly as if it were a C function, it still +obeys Objective-C semantics in other ways: + +- If the receiver is ``nil``, the call does nothing and returns the zero value + for the return type. + +- Calling a direct class method will cause the class to be initialized, + including calling the ``+initialize`` method if present. + +- The method still receives an implicit ``_cmd`` argument containing its selector. + +Note, the ``objc_direct_members`` can be used on ``@implementation`` blocks +to automatically mark methods direct, when they are not declared in any +``@interface`` or ``@protocol`` this class conforms to. + }]; +} + +def ObjCDirectMembersDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +This attribute can decorage classes ``@implementation`` blocks, +and causes any method that has not been declared in any ``@interface`` +or ``@protocol`` this class conforms to to be direct methods. + }]; +} + def SelectAnyDocs : Documentation { let Category = DocCatDecl; let Content = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -987,6 +987,16 @@ "string is ill-formed as UTF-8 and will become a null %0 when boxed">, InGroup; +def err_objc_direct_on_protocol : Error< + "objc_direct attribute cannot be applied to methods " + "declared in an Objective-C protocol">; +def err_objc_direct_missing_on_decl : Error< + "direct method implementation was previously declared not direct">; +def err_objc_direct_on_override : Error< + "method overrides or implementing protocol conformance cannot be direct">; +def err_objc_override_direct_method : Error< + "cannot override a method that is declared direct by a superclass">; + def warn_conflicting_overriding_ret_types : Warning< "conflicting return type in " "declaration of %0%diff{: $ vs $|}1,2">, @@ -1072,6 +1082,7 @@ "type of property %0 does not match type of accessor %1">; def note_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; +def note_direct_method_declared_at : Note<"direct method %0 declared here">; def note_property_attribute : Note<"property %0 is declared " "%select{deprecated|unavailable|partial}1 here">; def err_setter_type_void : Error<"type of setter must be void">; @@ -1307,6 +1318,8 @@ "several methods with selector %0 of mismatched types are found " "for the @selector expression">, InGroup, DefaultIgnore; +def err_direct_selector_expression: Error< + "@selector expression formed with direct selector %0">; def err_objc_kindof_nonobject : Error< "'__kindof' specifier cannot be applied to non-object type %0">; @@ -1320,6 +1333,8 @@ def warn_messaging_unqualified_id : Warning< "messaging unqualified id">, DefaultIgnore, InGroup>; +def err_messaging_unqualified_id_with_direct_method : Error< + "messaging unqualified id with a method that is possibly direct">; // C++ declarations def err_static_assert_expression_is_not_constant : Error< Index: clang/include/clang/Sema/DeclSpec.h =================================================================== --- clang/include/clang/Sema/DeclSpec.h +++ clang/include/clang/Sema/DeclSpec.h @@ -833,7 +833,8 @@ DQ_PR_unsafe_unretained = 0x800, DQ_PR_nullability = 0x1000, DQ_PR_null_resettable = 0x2000, - DQ_PR_class = 0x4000 + DQ_PR_class = 0x4000, + DQ_PR_direct = 0x8000, }; ObjCDeclSpec() @@ -903,7 +904,7 @@ unsigned objcDeclQualifier : 7; // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind - unsigned PropertyAttributes : 15; + unsigned PropertyAttributes : 16; unsigned Nullability : 2; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -8901,6 +8901,9 @@ RTC_Unknown }; + void CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden); + void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC); Index: clang/lib/AST/DeclObjC.cpp =================================================================== --- clang/lib/AST/DeclObjC.cpp +++ clang/lib/AST/DeclObjC.cpp @@ -823,6 +823,10 @@ Selector(), QualType(), nullptr, nullptr); } +bool ObjCMethodDecl::isDirectMethod() const { + return hasAttr(); +} + bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const { return getMethodFamily() == OMF_init && hasAttr(); @@ -1077,7 +1081,7 @@ QualType ObjCMethodDecl::getSelfType(ASTContext &Context, const ObjCInterfaceDecl *OID, bool &selfIsPseudoStrong, - bool &selfIsConsumed) { + bool &selfIsConsumed) const { QualType selfTy; selfIsPseudoStrong = false; selfIsConsumed = false; Index: clang/lib/AST/DeclPrinter.cpp =================================================================== --- clang/lib/AST/DeclPrinter.cpp +++ clang/lib/AST/DeclPrinter.cpp @@ -1469,6 +1469,11 @@ first = false; } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) { + Out << (first ? "" : ", ") << "direct"; + first = false; + } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_nonatomic) { Out << (first ? "" : ", ") << "nonatomic"; Index: clang/lib/AST/JSONNodeDumper.cpp =================================================================== --- clang/lib/AST/JSONNodeDumper.cpp +++ clang/lib/AST/JSONNodeDumper.cpp @@ -1013,6 +1013,7 @@ attributeOnlyIfTrue("unsafe_unretained", Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained); attributeOnlyIfTrue("class", Attrs & ObjCPropertyDecl::OBJC_PR_class); + attributeOnlyIfTrue("direct", Attrs & ObjCPropertyDecl::OBJC_PR_direct); attributeOnlyIfTrue("nullability", Attrs & ObjCPropertyDecl::OBJC_PR_nullability); attributeOnlyIfTrue("null_resettable", Index: clang/lib/AST/TextNodeDumper.cpp =================================================================== --- clang/lib/AST/TextNodeDumper.cpp +++ clang/lib/AST/TextNodeDumper.cpp @@ -1921,6 +1921,8 @@ OS << " unsafe_unretained"; if (Attrs & ObjCPropertyDecl::OBJC_PR_class) OS << " class"; + if (Attrs & ObjCPropertyDecl::OBJC_PR_direct) + OS << " direct"; if (Attrs & ObjCPropertyDecl::OBJC_PR_getter) dumpDeclRef(D->getGetterMethodDecl(), "getter"); if (Attrs & ObjCPropertyDecl::OBJC_PR_setter) Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -353,26 +353,13 @@ return nullptr; } -/// The ObjC runtime may provide entrypoints that are likely to be faster -/// than an ordinary message send of the appropriate selector. -/// -/// The entrypoints are guaranteed to be equivalent to just sending the -/// corresponding message. If the entrypoint is implemented naively as just a -/// message send, using it is a trade-off: it sacrifices a few cycles of -/// overhead to save a small amount of code. However, it's possible for -/// runtimes to detect and special-case classes that use "standard" -/// behavior; if that's dynamically a large proportion of all objects, using -/// the entrypoint will also be faster than using a message send. -/// -/// If the runtime does support a required entrypoint, then this method will -/// generate a call and return the resulting value. Otherwise it will return -/// None and the caller can generate a msgSend instead. -static Optional -tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType, - llvm::Value *Receiver, - const CallArgList& Args, Selector Sel, - const ObjCMethodDecl *method, - bool isClassMessage) { +Optional +CGObjCRuntime::tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, + QualType ResultType, + llvm::Value *Receiver, + const CallArgList& Args, Selector Sel, + const ObjCMethodDecl *method, + bool isClassMessage) { auto &CGM = CGF.CGM; if (!CGM.getCodeGenOpts().ObjCConvertMessagesToRuntimeCalls) return None; @@ -612,9 +599,10 @@ } else { // Call runtime methods directly if we can. if (Optional SpecializedResult = - tryGenerateSpecializedMessageSend(*this, ResultType, Receiver, Args, - E->getSelector(), method, - isClassMessage)) { + Runtime.tryGenerateSpecializedMessageSend(*this, ResultType, + Receiver, Args, + E->getSelector(), method, + isClassMessage)) { result = RValue::get(SpecializedResult.getValue()); } else { result = Runtime.GenerateMessageSend(*this, Return, ResultType, @@ -696,6 +684,14 @@ StartFunction(OMD, OMD->getReturnType(), Fn, FI, args, OMD->getLocation(), StartLoc); + if (OMD->isDirectMethod()) { + // This function is a direct call, it has to implement a nil check + // on entry. + // + // TODO: possibly have several entry points to elide the check + CGM.getObjCRuntime().GenerateDirectMethodPrologue(*this, Fn, OMD, CD); + } + // In ARC, certain methods get an extra cleanup. if (CGM.getLangOpts().ObjCAutoRefCount && OMD->isInstanceMethod() && Index: clang/lib/CodeGen/CGObjCGNU.cpp =================================================================== --- clang/lib/CodeGen/CGObjCGNU.cpp +++ clang/lib/CodeGen/CGObjCGNU.cpp @@ -606,6 +606,10 @@ llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) override; + void GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) override; void GenerateCategory(const ObjCCategoryImplDecl *CMD) override; void GenerateClass(const ObjCImplementationDecl *ClassDecl) override; void RegisterAlias(const ObjCCompatibleAliasDecl *OAD) override; @@ -3873,6 +3877,14 @@ return Method; } +void CGObjCGNU::GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) +{ + // GNU runtime doesn't support direct calls at this time +} + llvm::FunctionCallee CGObjCGNU::GetPropertyGetFunction() { return GetPropertyFn; } Index: clang/lib/CodeGen/CGObjCMac.cpp =================================================================== --- clang/lib/CodeGen/CGObjCMac.cpp +++ clang/lib/CodeGen/CGObjCMac.cpp @@ -874,6 +874,10 @@ /// this translation unit. llvm::DenseMap MethodDefinitions; + /// DirectMethodDefinitions - map of direct methods which have been defined in + /// this translation unit. + llvm::DenseMap DirectMethodDefinitions; + /// PropertyNames - uniqued method variable names. llvm::DenseMap PropertyNames; @@ -1065,7 +1069,7 @@ CodeGen::RValue EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, - llvm::Value *Sel, + Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, @@ -1092,6 +1096,14 @@ llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD=nullptr) override; + llvm::Function *GenerateDirectMethod(const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD); + + void GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) override; + void GenerateProtocol(const ObjCProtocolDecl *PD) override; /// GetOrEmitProtocol - Get the protocol object for the given @@ -1573,9 +1585,13 @@ // base of the ivar access is a parameter to an Objective C method. // However, because the parameters are not available in the current // interface, we cannot perform this check. + // + // Note that for direct methods, because objc_msgSend is skipped, + // and that the method may be inlined, this optimization actually + // can't be performed. if (const ObjCMethodDecl *MD = dyn_cast_or_null(CGF.CurFuncDecl)) - if (MD->isInstanceMethod()) + if (MD->isInstanceMethod() && !MD->isDirectMethod()) if (const ObjCInterfaceDecl *ID = MD->getClassInterface()) return IV->getContainingInterface()->isSuperClassOf(ID); return false; @@ -2103,8 +2119,7 @@ CGM.getTypes().ConvertType(CGF.getContext().getObjCClassType()); Target = CGF.Builder.CreateBitCast(Target, ClassTy); CGF.Builder.CreateStore(Target, CGF.Builder.CreateStructGEP(ObjCSuper, 1)); - return EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + return EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class, ObjCTypes); } @@ -2118,8 +2133,7 @@ const CallArgList &CallArgs, const ObjCInterfaceDecl *Class, const ObjCMethodDecl *Method) { - return EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + return EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method, Class, ObjCTypes); } @@ -2137,7 +2151,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType, - llvm::Value *Sel, + Selector Sel, llvm::Value *Arg0, QualType Arg0Ty, bool IsSuper, @@ -2145,11 +2159,22 @@ const ObjCMethodDecl *Method, const ObjCInterfaceDecl *ClassReceiver, const ObjCCommonTypesHelper &ObjCTypes) { + CodeGenTypes &Types = CGM.getTypes(); CallArgList ActualArgs; if (!IsSuper) Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy); ActualArgs.add(RValue::get(Arg0), Arg0Ty); - ActualArgs.add(RValue::get(Sel), CGF.getContext().getObjCSelType()); + if (Method && Method->isDirectMethod()) { + // Direct methods will synthesize the proper `_cmd` internally, + // so just don't bother with setting the `_cmd` argument. + assert(!IsSuper); + auto selTy = CGF.getContext().getObjCSelType(); + auto undefSel = llvm::UndefValue::get(Types.ConvertType(selTy)); + ActualArgs.add(RValue::get(undefSel), selTy); + } else { + ActualArgs.add(RValue::get(GetSelector(CGF, Sel)), + CGF.getContext().getObjCSelType()); + } ActualArgs.addFrom(CallArgs); // If we're calling a method, use the formal signature. @@ -2190,7 +2215,9 @@ bool RequiresNullCheck = false; llvm::FunctionCallee Fn = nullptr; - if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) { + if (Method && Method->isDirectMethod()) { + Fn = GenerateDirectMethod(Method, Method->getClassInterface()); + } else if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) { if (ReceiverCanBeNull) RequiresNullCheck = true; Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper) : ObjCTypes.getSendStretFn(IsSuper); @@ -3297,6 +3324,8 @@ values.addInt(ObjCTypes.IntTy, Properties.size()); auto propertiesArray = values.beginArray(ObjCTypes.PropertyTy); for (auto PD : Properties) { + if (PD->isDirectProperty()) + continue; auto property = propertiesArray.beginStruct(ObjCTypes.PropertyTy); property.add(GetPropertyName(PD->getIdentifier())); property.add(GetPropertyTypeString(PD, Container)); @@ -3372,7 +3401,8 @@ }; SmallVector Methods[NumMethodLists]; for (const auto *MD : OCD->methods()) { - Methods[unsigned(MD->isClassMethod())].push_back(MD); + if (!MD->isDirectMethod()) + Methods[unsigned(MD->isClassMethod())].push_back(MD); } Values.add(GetClassName(OCD->getName())); @@ -3554,13 +3584,16 @@ }; SmallVector Methods[NumMethodLists]; for (const auto *MD : ID->methods()) { - Methods[unsigned(MD->isClassMethod())].push_back(MD); + if (!MD->isDirectMethod()) + Methods[unsigned(MD->isClassMethod())].push_back(MD); } for (const auto *PID : ID->property_impls()) { if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize) { ObjCPropertyDecl *PD = PID->getPropertyDecl(); + if (PD->isDirectProperty()) + continue; if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) if (GetMethodDefinition(MD)) Methods[InstanceMethods].push_back(MD); @@ -3959,7 +3992,8 @@ values.addInt(ObjCTypes.IntTy, methods.size()); auto methodArray = values.beginArray(ObjCTypes.MethodTy); for (auto MD : methods) { - emitMethodConstant(methodArray, MD); + if (!MD->isDirectMethod()) + emitMethodConstant(methodArray, MD); } methodArray.finishAndAddTo(values); @@ -3970,22 +4004,136 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) { + llvm::Function *Method; + + if (OMD->isDirectMethod()) { + Method = GenerateDirectMethod(OMD, CD); + } else { + SmallString<256> Name; + GetNameForMethod(OMD, CD, Name); + + CodeGenTypes &Types = CGM.getTypes(); + llvm::FunctionType *MethodTy = + Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); + Method = + llvm::Function::Create(MethodTy, + llvm::GlobalValue::InternalLinkage, + Name.str(), + &CGM.getModule()); + } + + MethodDefinitions.insert(std::make_pair(OMD, Method)); + + return Method; +} + +llvm::Function *CGObjCCommonMac::GenerateDirectMethod(const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) { + auto I = DirectMethodDefinitions.find(OMD); + if (I != DirectMethodDefinitions.end()) + return I->second; + SmallString<256> Name; - GetNameForMethod(OMD, CD, Name); + GetNameForMethod(OMD, OMD->getClassInterface(), Name); CodeGenTypes &Types = CGM.getTypes(); llvm::FunctionType *MethodTy = Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD)); llvm::Function *Method = llvm::Function::Create(MethodTy, - llvm::GlobalValue::InternalLinkage, + llvm::GlobalValue::ExternalLinkage, Name.str(), &CGM.getModule()); - MethodDefinitions.insert(std::make_pair(OMD, Method)); + Method->setVisibility(llvm::Function::HiddenVisibility); + DirectMethodDefinitions.insert(std::make_pair(OMD, Method)); return Method; } +void CGObjCCommonMac::GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) +{ + auto &Builder = CGF.Builder; + bool ReceiverCanBeNull = true; + auto selfValue = &*Fn->arg_begin(); + + // Generate: + // + // /* for class methods only to force class lazy initialization */ + // self = [self self]; + // + // /* unless the receiver is never NULL */ + // if (self == nil) { + // return (ReturnType){ }; + // } + // + // _cmd = @selector(...) + // ... + + if (OMD->isClassMethod()) { + const ObjCInterfaceDecl *OID = cast(CD)->getClassInterface(); + Selector SelfSel = GetNullarySelector("self", CGM.getContext()); + auto ResultType = CGF.getContext().getObjCIdType(); + RValue result; + CallArgList Args; + + // attempt hard to generate objc_opt_self() which is as cheap as it can be + // to realize a class lazily + // + // TODO: make sure that when the inliner kicks in we do this once only + if (Optional SpecializedResult = + tryGenerateSpecializedMessageSend(CGF, ResultType, selfValue, Args, + SelfSel, nullptr, true)) { + result = RValue::get(SpecializedResult.getValue()); + } else { + result = GenerateMessageSend(CGF, ReturnValueSlot(), ResultType, + SelfSel, selfValue, Args, OID, nullptr); + } + Builder.CreateStore(result.getScalarVal(), + CGF.GetAddrOfLocalVar(OMD->getSelfDecl())); + + ReceiverCanBeNull = isWeakLinkedClass(OID); + } + + if (ReceiverCanBeNull) { + llvm::BasicBlock *SelfIsNilBlock = CGF.createBasicBlock("objc_direct_method.self_is_nil"); + llvm::BasicBlock *ContBlock = CGF.createBasicBlock("objc_direct_method.cont"); + + // if (self == nil) { + auto selfTy = cast(selfValue->getType()); + auto Zero = llvm::ConstantPointerNull::get(selfTy); + + llvm::MDBuilder MDHelper(CGM.getLLVMContext()); + Builder.CreateCondBr(Builder.CreateICmpEQ(selfValue, Zero), SelfIsNilBlock, + ContBlock, MDHelper.createBranchWeights(1, 1<<20)); + + CGF.EmitBlock(SelfIsNilBlock); + + // return (ReturnType){ }; + auto retTy = OMD->getReturnType(); + Builder.SetInsertPoint(SelfIsNilBlock); + if (retTy->isVoidType()) { + Builder.CreateRetVoid(); + } else { + CGF.EmitNullInitialization(CGF.ReturnValue, retTy); + CGF.EmitBranchThroughCleanup(CGF.ReturnBlock); + } + // } + + // rest of the body + CGF.EmitBlock(ContBlock); + Builder.SetInsertPoint(ContBlock); + } + + // only synthesize _cmd if it's referenced + if (OMD->getCmdDecl()->isUsed()) { + Builder.CreateStore(GetSelector(CGF, OMD), + CGF.GetAddrOfLocalVar(OMD->getCmdDecl())); + } +} + llvm::GlobalVariable *CGObjCCommonMac::CreateMetadataVar(Twine Name, ConstantStructBuilder &Init, StringRef Section, @@ -6228,15 +6376,18 @@ SmallVector methods; if (flags & NonFragileABI_Class_Meta) { for (const auto *MD : ID->class_methods()) - methods.push_back(MD); + if (!MD->isDirectMethod()) + methods.push_back(MD); } else { for (const auto *MD : ID->instance_methods()) - methods.push_back(MD); + if (!MD->isDirectMethod()) + methods.push_back(MD); for (const auto *PID : ID->property_impls()) { if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize){ ObjCPropertyDecl *PD = PID->getPropertyDecl(); - + if (PD->isDirectProperty()) + continue; if (auto MD = PD->getGetterMethodDecl()) if (GetMethodDefinition(MD)) methods.push_back(MD); @@ -6565,6 +6716,8 @@ SmallVector instanceMethods; SmallVector classMethods; for (const auto *MD : OCD->methods()) { + if (MD->isDirectMethod()) + continue; if (MD->isInstanceMethod()) { instanceMethods.push_back(MD); } else { @@ -7234,8 +7387,7 @@ ? EmitVTableMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method) - : EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + : EmitMessageSend(CGF, Return, ResultType, Sel, Receiver, CGF.getContext().getObjCIdType(), false, CallArgs, Method, Class, ObjCTypes); } @@ -7466,8 +7618,7 @@ ? EmitVTableMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, true, CallArgs, Method) - : EmitMessageSend(CGF, Return, ResultType, - EmitSelector(CGF, Sel), + : EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class, ObjCTypes); } @@ -7492,7 +7643,7 @@ llvm::ConstantExpr::getBitCast(GetMethodVarName(Sel), ObjCTypes.SelectorPtrTy); std::string SectionName = - GetSectionName("__objc_selrefs", "literal_pointers,no_dead_strip"); + GetSectionName("__objc_selrefs", "literal_pointers"); Entry = new llvm::GlobalVariable( CGM.getModule(), ObjCTypes.SelectorPtrTy, false, getLinkageTypeForObjCMetadata(CGM, SectionName), Casted, Index: clang/lib/CodeGen/CGObjCRuntime.h =================================================================== --- clang/lib/CodeGen/CGObjCRuntime.h +++ clang/lib/CodeGen/CGObjCRuntime.h @@ -169,6 +169,27 @@ const ObjCInterfaceDecl *Class = nullptr, const ObjCMethodDecl *Method = nullptr) = 0; + /// The ObjC runtime may provide entrypoints that are likely to be faster + /// than an ordinary message send of the appropriate selector. + /// + /// The entrypoints are guaranteed to be equivalent to just sending the + /// corresponding message. If the entrypoint is implemented naively as just a + /// message send, using it is a trade-off: it sacrifices a few cycles of + /// overhead to save a small amount of code. However, it's possible for + /// runtimes to detect and special-case classes that use "standard" + /// behavior; if that's dynamically a large proportion of all objects, using + /// the entrypoint will also be faster than using a message send. + /// + /// If the runtime does support a required entrypoint, then this method will + /// generate a call and return the resulting value. Otherwise it will return + /// None and the caller can generate a msgSend instead. + Optional + tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType, + llvm::Value *Receiver, + const CallArgList& Args, Selector Sel, + const ObjCMethodDecl *method, + bool isClassMessage); + /// Generate an Objective-C message send operation to the super /// class initiated in a method for Class and with the given Self /// object. @@ -205,6 +226,12 @@ virtual llvm::Function *GenerateMethod(const ObjCMethodDecl *OMD, const ObjCContainerDecl *CD) = 0; + /// Generates prologue for direct Objective-C Methods. + virtual void GenerateDirectMethodPrologue(CodeGenFunction &CGF, + llvm::Function *Fn, + const ObjCMethodDecl *OMD, + const ObjCContainerDecl *CD) = 0; + /// Return the runtime function for getting properties. virtual llvm::FunctionCallee GetPropertyGetFunction() = 0; Index: clang/lib/Parse/ParseObjc.cpp =================================================================== --- clang/lib/Parse/ParseObjc.cpp +++ clang/lib/Parse/ParseObjc.cpp @@ -822,6 +822,7 @@ /// property-attribute: /// getter '=' identifier /// setter '=' identifier ':' +/// direct /// readonly /// readwrite /// assign @@ -954,6 +955,8 @@ DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable); } else if (II->isStr("class")) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class); + } else if (II->isStr("direct")) { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_direct); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -2600,6 +2600,16 @@ D->addAttr(newAttr); } +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())) { + S.Diag(AL.getLoc(), diag::err_objc_direct_on_protocol); + return; + } + + handleSimpleAttribute(S, D, AL); +} + static void handleObjCMethodFamilyAttr(Sema &S, Decl *D, const ParsedAttr &AL) { const auto *M = cast(D); if (!AL.isArgIdent(0)) { @@ -6909,6 +6919,12 @@ case ParsedAttr::AT_ObjCRootClass: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCDirect: + handleObjCDirectAttr(S, D, AL); + break; + case ParsedAttr::AT_ObjCDirectMembers: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCNonLazyClass: handleSimpleAttribute(S, D, AL); break; Index: clang/lib/Sema/SemaDeclObjC.cpp =================================================================== --- clang/lib/Sema/SemaDeclObjC.cpp +++ clang/lib/Sema/SemaDeclObjC.cpp @@ -3233,6 +3233,9 @@ if (left->isHidden() || right->isHidden()) return false; + if (left->isDirectMethod() != right->isDirectMethod()) + return false; + if (getLangOpts().ObjCAutoRefCount && (left->hasAttr() != right->hasAttr() || @@ -3424,6 +3427,9 @@ if (!chosen->isInstanceMethod()) return false; + if (chosen->isDirectMethod() != other->isDirectMethod()) + return false; + Selector sel = chosen->getSelector(); if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") return false; @@ -4314,6 +4320,19 @@ }; } // end anonymous namespace + +void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method, + ObjCMethodDecl *overridden) +{ + if (const auto *attr = overridden->getAttr()) { + Diag(method->getLocation(), diag::err_objc_override_direct_method); + Diag(attr->getLocation(), diag::note_previous_declaration); + } else if (const auto *attr = method->getAttr()) { + Diag(attr->getLocation(), diag::err_objc_direct_on_override); + Diag(overridden->getLocation(), diag::note_previous_declaration); + } +} + void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod, ObjCInterfaceDecl *CurrentClass, ResultTypeCompatibilityKind RTC) { @@ -4332,8 +4351,8 @@ if (isa(overridden->getDeclContext()) || CurrentClass != overridden->getClassInterface() || overridden->isOverriding()) { + CheckObjCMethodDirectOverrides(ObjCMethod, overridden); hasOverriddenMethodsInBaseOrProtocol = true; - } else if (isa(ObjCMethod->getDeclContext())) { // OverrideSearch will return as "overridden" the same method in the // interface. For hasOverriddenMethodsInBaseOrProtocol, we need to @@ -4357,6 +4376,7 @@ for (ObjCMethodDecl *SuperOverridden : overrides) { if (isa(SuperOverridden->getDeclContext()) || CurrentClass != SuperOverridden->getClassInterface()) { + CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden); hasOverriddenMethodsInBaseOrProtocol = true; overridden->setOverriding(true); break; @@ -4464,6 +4484,12 @@ method->getLocation())); } + if (!method->isDirectMethod()) + if (const auto *attr = prevMethod->getAttr()) { + method->addAttr( + ObjCDirectAttr::CreateImplicit(S.Context, attr->getLocation())); + } + // Merge nullability of the result type. QualType newReturnType = mergeTypeNullabilityForRedecl( @@ -4672,6 +4698,12 @@ if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(), ObjCMethod->isInstanceMethod())) { mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD); + if (const auto *attr = ObjCMethod->getAttr()) { + if (!IMD->isDirectMethod()) { + Diag(attr->getLocation(), diag::err_objc_direct_missing_on_decl); + Diag(IMD->getLocation(), diag::note_previous_declaration); + } + } // Warn about defining -dealloc in a category. if (isa(ImpDecl) && IMD->isOverriding() && @@ -4679,6 +4711,9 @@ Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category) << ObjCMethod->getDeclName(); } + } else if (ImpDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); } // Warn if a method declared in a protocol to which a category or @@ -4698,6 +4733,10 @@ } } } else { + if (!ObjCMethod->isDirectMethod() && ClassDecl->hasAttr()) { + ObjCMethod->addAttr( + ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation())); + } cast(ClassDecl)->addDecl(ObjCMethod); } Index: clang/lib/Sema/SemaExprObjC.cpp =================================================================== --- clang/lib/Sema/SemaExprObjC.cpp +++ clang/lib/Sema/SemaExprObjC.cpp @@ -1118,6 +1118,17 @@ return BuildObjCEncodeExpression(AtLoc, TInfo, RParenLoc); } +static void HelperNoteMethodDeclaredAt(Sema &S, ObjCMethodDecl *Method) +{ + if (Method->isDirectMethod()) { + S.Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } else { + S.Diag(Method->getLocation(), diag::note_method_declared_at) + << Method->getDeclName(); + } +} + static bool HelperToDiagnoseMismatchedMethodsInGlobalPool(Sema &S, SourceLocation AtLoc, SourceLocation LParenLoc, @@ -1139,11 +1150,9 @@ S.Diag(AtLoc, diag::warn_multiple_selectors) << Method->getSelector() << FixItHint::CreateInsertion(LParenLoc, "(") << FixItHint::CreateInsertion(RParenLoc, ")"); - S.Diag(Method->getLocation(), diag::note_method_declared_at) - << Method->getDeclName(); + HelperNoteMethodDeclaredAt(S, Method); } - S.Diag(MatchingMethodDecl->getLocation(), diag::note_method_declared_at) - << MatchingMethodDecl->getDeclName(); + HelperNoteMethodDeclaredAt(S, MatchingMethodDecl); } } return Warned; @@ -1174,6 +1183,35 @@ } } +static void HelperToDiagnoseDirectSelectorsExpr(Sema &S, + SourceLocation AtLoc, + Selector Sel, + ObjCMethodList &MethList, + bool &onlyDirect) { + ObjCMethodList *M = &MethList; + for (M = M->getNext(); M; M=M->getNext()) { + ObjCMethodDecl *Method = M->getMethod(); + if (Method->getSelector() != Sel) + continue; + if (!Method->isDirectMethod()) + onlyDirect = false; + } +} + +static void DiagnoseDirectSelectorsExpr(Sema &S, SourceLocation AtLoc, + Selector Sel, bool &onlyDirect) { + for (Sema::GlobalMethodPool::iterator b = S.MethodPool.begin(), + e = S.MethodPool.end(); b != e; b++) { + // first, instance methods + ObjCMethodList &InstMethList = b->second.first; + HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, InstMethList, onlyDirect); + + // second, class methods + ObjCMethodList &ClsMethList = b->second.second; + HelperToDiagnoseDirectSelectorsExpr(S, AtLoc, Sel, ClsMethList, onlyDirect); + } +} + ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc, SourceLocation SelLoc, @@ -1196,9 +1234,18 @@ } else Diag(SelLoc, diag::warn_undeclared_selector) << Sel; - } else + } else { + bool onlyDirect = Method->isDirectMethod(); + DiagnoseDirectSelectorsExpr(*this, AtLoc, Sel, onlyDirect); DiagnoseMismatchedSelectors(*this, AtLoc, Method, LParenLoc, RParenLoc, WarnMultipleSelectors); + if (onlyDirect) { + Diag(AtLoc, diag::err_direct_selector_expression) + << Method->getSelector(); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } + } if (Method && Method->getImplementationControl() != ObjCMethodDecl::Optional && @@ -2771,9 +2818,6 @@ } } - if (ReceiverType->isObjCIdType() && !isImplicit) - Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); - // There's a somewhat weird interaction here where we assume that we // won't actually have a method unless we also don't need to do some // of the more detailed type-checking on the receiver. @@ -2975,6 +3019,17 @@ (Method && Method->getMethodFamily() == OMF_init) ? getEnclosingFunction() : nullptr; + if (ReceiverType->isObjCIdType() && !isImplicit) { + if (Method && Method->isDirectMethod()) { + Diag(Receiver->getExprLoc(), + diag::err_messaging_unqualified_id_with_direct_method); + Diag(Method->getLocation(), diag::note_direct_method_declared_at) + << Method->getDeclName(); + } else { + Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id); + } + } + if (DIFunctionScopeInfo && DIFunctionScopeInfo->ObjCIsDesignatedInit && (SuperLoc.isValid() || isSelfExpr(Receiver))) { Index: clang/lib/Sema/SemaObjCProperty.cpp =================================================================== --- clang/lib/Sema/SemaObjCProperty.cpp +++ clang/lib/Sema/SemaObjCProperty.cpp @@ -306,6 +306,8 @@ attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_atomic; if (Attributes & ObjCDeclSpec::DQ_PR_class) attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_class; + if (Attributes & ObjCDeclSpec::DQ_PR_direct) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_direct; return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten; } @@ -705,9 +707,12 @@ if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable); - if (Attributes & ObjCDeclSpec::DQ_PR_class) + if (Attributes & ObjCDeclSpec::DQ_PR_class) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_class); + if (Attributes & ObjCDeclSpec::DQ_PR_direct) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_direct); + return PDecl; } @@ -2410,6 +2415,9 @@ AddPropertyAttrs(*this, GetterMethod, property); + if (property->isDirectProperty()) + GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + if (property->hasAttr()) GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context, Loc)); @@ -2483,6 +2491,9 @@ AddPropertyAttrs(*this, SetterMethod, property); + if (property->isDirectProperty()) + SetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc)); + CD->addDecl(SetterMethod); if (const SectionAttr *SA = property->getAttr()) SetterMethod->addAttr(SectionAttr::CreateImplicit( Index: clang/test/CodeGenObjC/direct-method.m =================================================================== --- /dev/null +++ clang/test/CodeGenObjC/direct-method.m @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fobjc-runtime=macosx-fragile-10.5 -emit-llvm %s -o - | FileCheck %s + +struct my_complex_struct { + int a, b; +}; + +struct my_aggregate_struct { + int a, b; + char buf[128]; +}; + +__attribute__((objc_root_class)) +@interface Root +@end + +@implementation Root +- (int)getInt __attribute__((objc_direct)) +{ + return 42; +} +- (struct my_complex_struct)getComplex __attribute__((objc_direct)) +{ + struct my_complex_struct st = { .a = 42 }; + return st; +} +- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) +{ + struct my_aggregate_struct st = { .a = 42 }; + return st; +} +@end + +// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"( +// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"( +// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"( Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -104,6 +104,8 @@ // CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record) // CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method) +// CHECK-NEXT: ObjCDirect (SubjectMatchRule_objc_method, SubjectMatchRule_objc_category) +// CHECK-NEXT: ObjCDirectMembers (SubjectMatchRule_objc_implementation) // CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface) // CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol) // CHECK-NEXT: ObjCExternallyRetained (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function, SubjectMatchRule_block, SubjectMatchRule_objc_method) Index: clang/test/SemaObjC/method-direct.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/method-direct.m @@ -0,0 +1,120 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wselector-type-mismatch %s + +@protocol Proto +- (void)protoMethod; // expected-note {{previous declaration is here}} ++ (void)classProtoMethod; // expected-note {{previous declaration is here}} +@end + +@protocol ProtoDirectFail +- (void)protoMethod __attribute__((objc_direct)); // expected-error {{objc_direct attribute cannot be applied to methods declared in an Objective-C protocol}} ++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{objc_direct attribute cannot be applied to methods declared in an Objective-C protocol}} +@end + +__attribute__((objc_root_class)) +@interface Root +- (void)rootRegular; // expected-note {{previous declaration is here}} ++ (void)classRootRegular; // expected-note {{previous declaration is here}} +- (void)rootDirect __attribute__((objc_direct)); // expected-note {{previous declaration is here}}; ++ (void)classRootDirect __attribute__((objc_direct)); // expected-note {{previous declaration is here}}; +- (void)notDirectInIface; // expected-note {{previous declaration is here}} ++ (void)classNotDirectInIface; // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_direct)) +@interface Root () +- (void)rootExtensionDirect; // expected-note {{previous declaration is here}} ++ (void)classRootExtensionDirect; // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_direct)) +@interface Root (Direct) +- (void)rootCategoryDirect; // expected-note {{previous declaration is here}} ++ (void)classRootCategoryDirect; // expected-note {{previous declaration is here}} +@end + +@interface Root () +- (void)rootExtensionRegular; // expected-note {{previous declaration is here}} ++ (void)classRootExtensionRegular; // expected-note {{previous declaration is here}} +- (void)rootExtensionDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}} ++ (void)classRootExtensionDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}} +@end + +@interface Root (Direct2) +- (void)rootCategoryRegular; // expected-note {{previous declaration is here}} ++ (void)classRootCategoryRegular; // expected-note {{previous declaration is here}} +- (void)rootCategoryDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}} ++ (void)classRootCategoryDirect2 __attribute__((objc_direct)); // expected-note {{previous declaration is here}} +@end + +__attribute__((objc_root_class, objc_direct)) // expected-error {{'objc_direct' attribute only applies to Objective-C methods and Objective-C containers}} +@interface SubDirectFail : Root +- (instancetype)init; +@end + +@interface Sub : Root +/* invalid overrides with directs */ +- (void)rootRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} ++ (void)classRootRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} +- (void)protoMethod __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} ++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} +- (void)rootExtensionRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} ++ (void)classRootExtensionRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} +- (void)rootCategoryRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} ++ (void)classRootCategoryRegular __attribute__((objc_direct)); // expected-error {{method overrides or implementing protocol conformance cannot be direct}} + +/* invalid overrides of directs */ +- (void)rootDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootExtensionDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootExtensionDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootExtensionDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootExtensionDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootCategoryDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootCategoryDirect; // expected-error {{cannot override a method that is declared direct by a superclass}} +- (void)rootCategoryDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} ++ (void)classRootCategoryDirect2; // expected-error {{cannot override a method that is declared direct by a superclass}} +@end + +__attribute__((objc_direct_members)) +@implementation Root +- (void)rootRegular { } ++ (void)classRootRegular { } +- (void)rootDirect { } ++ (void)classRootDirect { } +- (void)rootExtensionDirect { } ++ (void)classRootExtensionDirect { } +- (void)rootExtensionRegular { } ++ (void)classRootExtensionRegular { } +- (void)rootExtensionDirect2 { } ++ (void)classRootExtensionDirect2 { } +- (void)notDirectInIface __attribute__((objc_direct)) // expected-error {{direct method implementation was previously declared not direct}} +{ +} ++ (void)classNotDirectInIface __attribute__((objc_direct)) // expected-error {{direct method implementation was previously declared not direct}} +{ +} +- (void)direct1 { } // expected-note {{direct method 'direct1' declared here}} +- (void)direct2 { } // expected-note {{direct method 'direct2' declared here}} +@end + +@interface Foo : Root +- (id)directMismatch1; // expected-note {{using}} +- (id)directMismatch2; // expected-note {{method 'directMismatch2' declared here}} +@end + +@interface Bar : Root +- (void)directMismatch1 __attribute__((objc_direct)); // expected-note {{also found}} +- (void)directMismatch2 __attribute__((objc_direct)); // expected-note {{method 'directMismatch2' declared here}} +@end + +extern void callMethod(id obj); +extern void useSel(SEL sel); + +void callMethod(id obj) +{ + [obj direct1]; // expected-error {{messaging unqualified id with a method that is possibly direct}} + [(Root *)obj direct1]; + [obj directMismatch1]; // expected-warning {{multiple methods named 'directMismatch1' found}} + useSel(@selector(direct2)); // expected-error {{@selector expression formed with direct selector 'direct2'}} + useSel(@selector(directMismatch2)); // expected-warning {{several methods with selector 'directMismatch2' of mismatched types are found for the @selector expression}} +}