summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/DeclObjC.h11
-rw-r--r--clang/include/clang/Basic/Attr.td14
-rw-r--r--clang/include/clang/Basic/AttrDocs.td98
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td25
-rw-r--r--clang/include/clang/Basic/ObjCRuntime.h14
-rw-r--r--clang/include/clang/Sema/DeclSpec.h5
-rw-r--r--clang/include/clang/Sema/Sema.h3
-rw-r--r--clang/lib/AST/DeclObjC.cpp6
-rw-r--r--clang/lib/AST/DeclPrinter.cpp5
-rw-r--r--clang/lib/AST/JSONNodeDumper.cpp1
-rw-r--r--clang/lib/AST/TextNodeDumper.cpp2
-rw-r--r--clang/lib/CodeGen/CGObjC.cpp43
-rw-r--r--clang/lib/CodeGen/CGObjCGNU.cpp10
-rw-r--r--clang/lib/CodeGen/CGObjCMac.cpp202
-rw-r--r--clang/lib/CodeGen/CGObjCRuntime.h21
-rw-r--r--clang/lib/Parse/ParseObjc.cpp3
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp30
-rw-r--r--clang/lib/Sema/SemaDeclObjC.cpp41
-rw-r--r--clang/lib/Sema/SemaExprObjC.cpp67
-rw-r--r--clang/lib/Sema/SemaObjCProperty.cpp22
-rw-r--r--clang/test/CodeGenObjC/direct-method.m175
-rw-r--r--clang/test/Misc/pragma-attribute-supported-attributes-list.test2
-rw-r--r--clang/test/SemaObjC/method-direct-properties.m126
-rw-r--r--clang/test/SemaObjC/method-direct.m148
24 files changed, 1024 insertions, 50 deletions
diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h
index e5d3ebfadc0..b98aef6b499 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -410,7 +410,7 @@ public:
/// \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; }
@@ -476,6 +476,9 @@ public:
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
@@ -757,13 +760,14 @@ public:
/// 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 };
@@ -886,6 +890,7 @@ public:
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 :
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c3849c14698..3e55104f763 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1861,6 +1861,20 @@ def ObjCDesignatedInitializer : Attr {
let Documentation = [Undocumented];
}
+def ObjCDirect : Attr {
+ let Spellings = [Clang<"objc_direct">];
+ let Subjects = SubjectList<[ObjCMethod], ErrorDiag>;
+ let LangOpts = [ObjC];
+ let Documentation = [ObjCDirectDocs];
+}
+
+def ObjCDirectMembers : Attr {
+ let Spellings = [Clang<"objc_direct_members">];
+ let Subjects = SubjectList<[ObjCImpl, ObjCCategory], ErrorDiag>;
+ let LangOpts = [ObjC];
+ let Documentation = [ObjCDirectMembersDocs];
+}
+
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
index 2b06a28d366..f35199f81c3 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3912,6 +3912,104 @@ overheads associated with defining and calling such a method.
}];
}
+def ObjCDirectDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+The ``objc_direct`` attribute can be used to mark an Objective-C method as
+being *direct*. A direct method is treated statically like an ordinary method,
+but dynamically it behaves more like a C function. This lowers some of the costs
+associated with the method but also sacrifices some of the ordinary capabilities
+of Objective-C methods.
+
+A message send of 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.
+
+Because a direct method cannot be overridden, it is an error to perform
+a ``super`` message send of one.
+
+Although a message send of a direct method causes the method to be called
+directly as if it were a C function, it still obeys Objective-C semantics in other
+ways:
+
+- If the receiver is ``nil``, the message send does nothing and returns the zero value
+ for the return type.
+
+- A message send of a direct class method will cause the class to be initialized,
+ including calling the ``+initialize`` method if present.
+
+- The implicit ``_cmd`` parameter containing the method's selector is still defined.
+ In order to minimize code-size costs, the implementation will not emit a reference
+ to the selector if the parameter is unused within the method.
+
+Symbols for direct method implementations are implicitly given hidden
+visibility, meaning that they can only be called within the same linkage unit.
+
+It is an error to do any of the following:
+
+- declare a direct method in a protocol,
+- declare an override of a direct method with a method in a subclass,
+- declare an override of a non-direct method with a direct method in a subclass,
+- declare a method with different directness in different class interfaces, or
+- implement a non-direct method (as declared in any class interface) with a direct method.
+
+If any of these rules would be violated if every method defined in an
+``@implementation`` within a single linkage unit were declared in an
+appropriate class interface, the program is ill-formed with no diagnostic
+required. If a violation of this rule is not diagnosed, behavior remains
+well-defined; this paragraph is simply reserving the right to diagnose such
+conflicts in the future, not to treat them as undefined behavior.
+
+Additionally, Clang will warn about any ``@selector`` expression that
+names a selector that is only known to be used for direct methods.
+
+For the purpose of these rules, a "class interface" includes a class's primary
+``@interface`` block, its class extensions, its categories, its declared protocols,
+and all the class interfaces of its superclasses.
+
+An Objective-C property can be declared with the ``direct`` property
+attribute. If a direct property declaration causes an implicit declaration of
+a getter or setter method (that is, if the given method is not explicitly
+declared elsewhere), the method is declared to be direct.
+
+Some programmers may wish to make many methods direct at once. In order
+to simplify this, the ``objc_direct_members`` attribute is provided; see its
+documentation for more information.
+ }];
+}
+
+def ObjCDirectMembersDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+The ``objc_direct_members`` attribute can be placed on an Objective-C
+``@interface`` or ``@implementation`` to mark that methods declared
+therein should be considered direct by default. See the documentation
+for ``objc_direct`` for more information about direct methods.
+
+When ``objc_direct_members`` is placed on an ``@interface`` block, every
+method in the block is considered to be declared as direct. This includes any
+implicit method declarations introduced by property declarations. If the method
+redeclares a non-direct method, the declaration is ill-formed, exactly as if the
+method was annotated with the ``objc_direct`` attribute. ``objc_direct_members``
+cannot be placed on the primary interface of a class, only on category or class
+extension interfaces.
+
+When ``objc_direct_members`` is placed on an ``@implementation`` block,
+methods defined in the block are considered to be declared as direct unless
+they have been previously declared as non-direct in any interface of the class.
+This includes the implicit method definitions introduced by synthesized
+properties, including auto-synthesized properties.
+ }];
+}
+
def SelectAnyDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 49ad7c7cc46..78ff18b8798 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -988,6 +988,22 @@ def warn_objc_boxing_invalid_utf8_string : Warning<
"string is ill-formed as UTF-8 and will become a null %0 when boxed">,
InGroup<ObjCBoxing>;
+def err_objc_direct_on_protocol : Error<
+ "'objc_direct' attribute cannot be applied to %select{methods|properties}0 "
+ "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<
+ "methods that %select{override superclass methods|implement protocol requirements}0 cannot be direct">;
+def err_objc_override_direct_method : Error<
+ "cannot override a method that is declared direct by a superclass">;
+def warn_objc_direct_ignored : Warning<
+ "%0 attribute isn't implemented by this Objective-C runtime">,
+ InGroup<IgnoredAttributes>;
+def warn_objc_direct_property_ignored : Warning<
+ "direct attribute on property %0 ignored (not implemented by this Objective-C runtime)">,
+ InGroup<IgnoredAttributes>;
+
def warn_conflicting_overriding_ret_types : Warning<
"conflicting return type in "
"declaration of %0%diff{: $ vs $|}1,2">,
@@ -1073,6 +1089,7 @@ def warn_accessor_property_type_mismatch : Warning<
"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">;
@@ -1308,6 +1325,8 @@ def warn_multiple_selectors: Warning<
"several methods with selector %0 of mismatched types are found "
"for the @selector expression">,
InGroup<SelectorTypeMismatch>, 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">;
@@ -1321,6 +1340,12 @@ def err_objc_method_unsupported_param_ret_type : Error<
def warn_messaging_unqualified_id : Warning<
"messaging unqualified id">, DefaultIgnore,
InGroup<DiagGroup<"objc-messaging-id">>;
+def err_messaging_unqualified_id_with_direct_method : Error<
+ "messaging unqualified id with a method that is possibly direct">;
+def err_messaging_super_with_direct_method : Error<
+ "messaging super with a direct method">;
+def err_messaging_class_with_direct_method : Error<
+ "messaging a Class with a method that is possibly direct">;
// C++ declarations
def err_static_assert_expression_is_not_constant : Error<
diff --git a/clang/include/clang/Basic/ObjCRuntime.h b/clang/include/clang/Basic/ObjCRuntime.h
index 5329b38c207..1c4a69269de 100644
--- a/clang/include/clang/Basic/ObjCRuntime.h
+++ b/clang/include/clang/Basic/ObjCRuntime.h
@@ -446,6 +446,20 @@ public:
llvm_unreachable("bad kind");
}
+ /// Does this runtime supports direct dispatch
+ bool allowsDirectDispatch() const {
+ switch (getKind()) {
+ case FragileMacOSX: return false;
+ case MacOSX: return true;
+ case iOS: return true;
+ case WatchOS: return true;
+ case GCC: return false;
+ case GNUstep: return false;
+ case ObjFW: return false;
+ }
+ llvm_unreachable("bad kind");
+ }
+
/// Try to parse an Objective-C runtime specification from the given
/// string.
///
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index b417f89c0e5..e3ead60bb43 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -833,7 +833,8 @@ public:
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 @@ private:
unsigned objcDeclQualifier : 7;
// NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind
- unsigned PropertyAttributes : 15;
+ unsigned PropertyAttributes : 16;
unsigned Nullability : 2;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a3f1a07c3f5..220b6c17835 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8901,6 +8901,9 @@ public:
RTC_Unknown
};
+ void CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
+ ObjCMethodDecl *overridden);
+
void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCInterfaceDecl *CurrentClass,
ResultTypeCompatibilityKind RTC);
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index 26017e4193d..fed76aecdaa 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -823,6 +823,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
Selector(), QualType(), nullptr, nullptr);
}
+bool ObjCMethodDecl::isDirectMethod() const {
+ return hasAttr<ObjCDirectAttr>();
+}
+
bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
return getMethodFamily() == OMF_init &&
hasAttr<ObjCDesignatedInitializerAttr>();
@@ -1077,7 +1081,7 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const {
QualType ObjCMethodDecl::getSelfType(ASTContext &Context,
const ObjCInterfaceDecl *OID,
bool &selfIsPseudoStrong,
- bool &selfIsConsumed) {
+ bool &selfIsConsumed) const {
QualType selfTy;
selfIsPseudoStrong = false;
selfIsConsumed = false;
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index a11c8760465..e59792710e7 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -1461,6 +1461,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) {
first = false;
}
+ if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_direct) {
+ Out << (first ? "" : ", ") << "direct";
+ first = false;
+ }
+
if (PDecl->getPropertyAttributes() &
ObjCPropertyDecl::OBJC_PR_nonatomic) {
Out << (first ? "" : ", ") << "nonatomic";
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index de5a9128e3c..6d5912e5a07 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1017,6 +1017,7 @@ void JSONNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
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",
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 63a6510324f..987910da24a 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1921,6 +1921,8 @@ void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
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)
diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp
index c509149af3f..984fa599a99 100644
--- a/clang/lib/CodeGen/CGObjC.cpp
+++ b/clang/lib/CodeGen/CGObjC.cpp
@@ -430,6 +430,20 @@ tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
return None;
}
+CodeGen::RValue CGObjCRuntime::GeneratePossiblySpecializedMessageSend(
+ CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType,
+ Selector Sel, llvm::Value *Receiver, const CallArgList &Args,
+ const ObjCInterfaceDecl *OID, const ObjCMethodDecl *Method,
+ bool isClassMessage) {
+ if (Optional<llvm::Value *> SpecializedResult =
+ tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,
+ Sel, Method, isClassMessage)) {
+ return RValue::get(SpecializedResult.getValue());
+ }
+ return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID,
+ Method);
+}
+
/// 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.
@@ -611,16 +625,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E,
method);
} else {
// Call runtime methods directly if we can.
- if (Optional<llvm::Value *> SpecializedResult =
- tryGenerateSpecializedMessageSend(*this, ResultType, Receiver, Args,
- E->getSelector(), method,
- isClassMessage)) {
- result = RValue::get(SpecializedResult.getValue());
- } else {
- result = Runtime.GenerateMessageSend(*this, Return, ResultType,
- E->getSelector(), Receiver, Args,
- OID, method);
- }
+ result = Runtime.GeneratePossiblySpecializedMessageSend(
+ *this, Return, ResultType, E->getSelector(), Receiver, Args, OID,
+ method, isClassMessage);
}
// For delegate init calls in ARC, implicitly store the result of
@@ -683,7 +690,13 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD,
llvm::Function *Fn = CGM.getObjCRuntime().GenerateMethod(OMD, CD);
const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD);
- CGM.SetInternalFunctionAttributes(OMD, Fn, FI);
+ if (OMD->isDirectMethod()) {
+ Fn->setVisibility(llvm::Function::HiddenVisibility);
+ CGM.SetLLVMFunctionAttributes(OMD, FI, Fn);
+ CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn);
+ } else {
+ CGM.SetInternalFunctionAttributes(OMD, Fn, FI);
+ }
args.push_back(OMD->getSelfDecl());
args.push_back(OMD->getCmdDecl());
@@ -696,6 +709,14 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD,
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() &&
diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index c2702374858..12454bb5cb0 100644
--- a/clang/lib/CodeGen/CGObjCGNU.cpp
+++ b/clang/lib/CodeGen/CGObjCGNU.cpp
@@ -606,6 +606,9 @@ public:
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;
@@ -3871,6 +3874,13 @@ llvm::Function *CGObjCGNU::GenerateMethod(const ObjCMethodDecl *OMD,
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;
}
diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp
index d08a26f538b..775e3406da7 100644
--- a/clang/lib/CodeGen/CGObjCMac.cpp
+++ b/clang/lib/CodeGen/CGObjCMac.cpp
@@ -874,6 +874,10 @@ protected:
/// this translation unit.
llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> MethodDefinitions;
+ /// DirectMethodDefinitions - map of direct methods which have been defined in
+ /// this translation unit.
+ llvm::DenseMap<const ObjCMethodDecl*, llvm::Function*> DirectMethodDefinitions;
+
/// PropertyNames - uniqued method variable names.
llvm::DenseMap<IdentifierInfo*, llvm::GlobalVariable*> PropertyNames;
@@ -1065,7 +1069,7 @@ protected:
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,13 @@ public:
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 +1584,13 @@ private:
// 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<ObjCMethodDecl>(CGF.CurFuncDecl))
- if (MD->isInstanceMethod())
+ if (MD->isInstanceMethod() && !MD->isDirectMethod())
if (const ObjCInterfaceDecl *ID = MD->getClassInterface())
return IV->getContainingInterface()->isSuperClassOf(ID);
return false;
@@ -2103,10 +2118,9 @@ CGObjCMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF,
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),
- ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
- true, CallArgs, Method, Class, ObjCTypes);
+ return EmitMessageSend(CGF, Return, ResultType, Sel, ObjCSuper.getPointer(),
+ ObjCTypes.SuperPtrCTy, true, CallArgs, Method, Class,
+ ObjCTypes);
}
/// Generate code for a message send expression.
@@ -2118,10 +2132,9 @@ CodeGen::RValue CGObjCMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF,
const CallArgList &CallArgs,
const ObjCInterfaceDecl *Class,
const ObjCMethodDecl *Method) {
- return EmitMessageSend(CGF, Return, ResultType,
- EmitSelector(CGF, Sel),
- Receiver, CGF.getContext().getObjCIdType(),
- false, CallArgs, Method, Class, ObjCTypes);
+ return EmitMessageSend(CGF, Return, ResultType, Sel, Receiver,
+ CGF.getContext().getObjCIdType(), false, CallArgs,
+ Method, Class, ObjCTypes);
}
static bool isWeakLinkedClass(const ObjCInterfaceDecl *ID) {
@@ -2137,7 +2150,7 @@ CodeGen::RValue
CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
- llvm::Value *Sel,
+ Selector Sel,
llvm::Value *Arg0,
QualType Arg0Ty,
bool IsSuper,
@@ -2145,11 +2158,24 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
const ObjCMethodDecl *Method,
const ObjCInterfaceDecl *ClassReceiver,
const ObjCCommonTypesHelper &ObjCTypes) {
+ CodeGenTypes &Types = CGM.getTypes();
+ auto selTy = CGF.getContext().getObjCSelType();
+ llvm::Value *SelValue;
+
+ if (Method && Method->isDirectMethod()) {
+ // Direct methods will synthesize the proper `_cmd` internally,
+ // so just don't bother with setting the `_cmd` argument.
+ assert(!IsSuper);
+ SelValue = llvm::UndefValue::get(Types.ConvertType(selTy));
+ } else {
+ SelValue = GetSelector(CGF, Sel);
+ }
+
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());
+ ActualArgs.add(RValue::get(SelValue), selTy);
ActualArgs.addFrom(CallArgs);
// If we're calling a method, use the formal signature.
@@ -2190,7 +2216,9 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
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 +3325,8 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name,
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 +3402,8 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
};
SmallVector<const ObjCMethodDecl *, 16> 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,11 +3585,14 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) {
};
SmallVector<const ObjCMethodDecl *, 16> 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) {
+ if (PID->getPropertyDecl()->isDirectProperty())
+ continue;
if (ObjCMethodDecl *MD = PID->getGetterMethodDecl())
if (GetMethodDefinition(MD))
Methods[InstanceMethods].push_back(MD);
@@ -3957,7 +3991,8 @@ llvm::Constant *CGObjCMac::emitMethodList(Twine name, MethodListType MLT,
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);
@@ -3968,6 +4003,34 @@ llvm::Constant *CGObjCMac::emitMethodList(Twine name, MethodListType MLT,
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);
@@ -3975,15 +4038,98 @@ llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD,
llvm::FunctionType *MethodTy =
Types.GetFunctionType(Types.arrangeObjCMethodDeclaration(OMD));
llvm::Function *Method =
- llvm::Function::Create(MethodTy,
- llvm::GlobalValue::InternalLinkage,
- Name.str(),
- &CGM.getModule());
- MethodDefinitions.insert(std::make_pair(OMD, Method));
+ llvm::Function::Create(MethodTy, llvm::GlobalValue::ExternalLinkage,
+ Name.str(), &CGM.getModule());
+ 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 selfAddr = CGF.GetAddrOfLocalVar(OMD->getSelfDecl());
+ auto selfValue = Builder.CreateLoad(selfAddr);
+
+ // 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<ObjCInterfaceDecl>(CD);
+ assert(OID &&
+ "GenerateDirectMethod() should be called with the Class Interface");
+ Selector SelfSel = GetNullarySelector("self", CGM.getContext());
+ auto ResultType = CGF.getContext().getObjCIdType();
+ RValue result;
+ CallArgList Args;
+
+ // TODO: If this method is inlined, the caller might know that `self` is
+ // already initialized; for example, it might be an ordinary Objective-C
+ // method which always receives an initialized `self`, or it might have just
+ // forced initialization on its own.
+ //
+ // We should find a way to eliminate this unnecessary initialization in such
+ // cases in LLVM.
+ result = GeneratePossiblySpecializedMessageSend(
+ CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID,
+ nullptr, true);
+ Builder.CreateStore(result.getScalarVal(), selfAddr);
+
+ // Nullable `Class` expressions cannot be messaged with a direct method
+ // so the only reason why the receive can be null would be because
+ // of weak linking.
+ 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<llvm::PointerType>(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()) {
+ 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,
@@ -6226,10 +6372,12 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer(
SmallVector<const ObjCMethodDecl*, 16> 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);
}
values.add(emitMethodList(ID->getObjCRuntimeNameAsString(),
@@ -6550,6 +6698,8 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
SmallVector<const ObjCMethodDecl *, 16> instanceMethods;
SmallVector<const ObjCMethodDecl *, 8> classMethods;
for (const auto *MD : OCD->methods()) {
+ if (MD->isDirectMethod())
+ continue;
if (MD->isInstanceMethod()) {
instanceMethods.push_back(MD);
} else {
@@ -7218,8 +7368,7 @@ CGObjCNonFragileABIMac::GenerateMessageSend(CodeGen::CodeGenFunction &CGF,
? 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);
}
@@ -7450,8 +7599,7 @@ CGObjCNonFragileABIMac::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF,
? 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);
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.h b/clang/lib/CodeGen/CGObjCRuntime.h
index 471816cb598..f0b3525cfde 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.h
+++ b/clang/lib/CodeGen/CGObjCRuntime.h
@@ -169,6 +169,21 @@ public:
const ObjCInterfaceDecl *Class = nullptr,
const ObjCMethodDecl *Method = nullptr) = 0;
+ /// Generate an Objective-C message send operation.
+ ///
+ /// This variant allows for the call to be substituted with an optimized
+ /// variant.
+ CodeGen::RValue
+ GeneratePossiblySpecializedMessageSend(CodeGenFunction &CGF,
+ ReturnValueSlot Return,
+ QualType ResultType,
+ Selector Sel,
+ llvm::Value *Receiver,
+ const CallArgList& Args,
+ const ObjCInterfaceDecl *OID,
+ 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 +220,12 @@ public:
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;
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index 42d6221a733..efcef6d3b12 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -822,6 +822,7 @@ static void diagnoseRedundantPropertyNullability(Parser &P,
/// property-attribute:
/// getter '=' identifier
/// setter '=' identifier ':'
+/// direct
/// readonly
/// readwrite
/// assign
@@ -954,6 +955,8 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) {
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);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 5728df428a3..d16913e2d0e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2600,6 +2600,29 @@ static void handleVisibilityAttr(Sema &S, Decl *D, const ParsedAttr &AL,
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<ObjCProtocolDecl>(D->getDeclContext())) {
+ S.Diag(AL.getLoc(), diag::err_objc_direct_on_protocol) << false;
+ return;
+ }
+
+ if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) {
+ handleSimpleAttribute<ObjCDirectAttr>(S, D, AL);
+ } else {
+ S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL;
+ }
+}
+
+static void handleObjCDirectMembersAttr(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ if (S.getLangOpts().ObjCRuntime.allowsDirectDispatch()) {
+ handleSimpleAttribute<ObjCDirectMembersAttr>(S, D, AL);
+ } else {
+ S.Diag(AL.getLoc(), diag::warn_objc_direct_ignored) << AL;
+ }
+}
+
static void handleObjCMethodFamilyAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
const auto *M = cast<ObjCMethodDecl>(D);
if (!AL.isArgIdent(0)) {
@@ -6944,6 +6967,13 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_ObjCRootClass:
handleSimpleAttribute<ObjCRootClassAttr>(S, D, AL);
break;
+ case ParsedAttr::AT_ObjCDirect:
+ handleObjCDirectAttr(S, D, AL);
+ break;
+ case ParsedAttr::AT_ObjCDirectMembers:
+ handleObjCDirectMembersAttr(S, D, AL);
+ handleSimpleAttribute<ObjCDirectMembersAttr>(S, D, AL);
+ break;
case ParsedAttr::AT_ObjCNonLazyClass:
handleSimpleAttribute<ObjCNonLazyClassAttr>(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index 30a1b95d424..e84dc47a1ee 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -3239,6 +3239,9 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left,
if (left->isHidden() || right->isHidden())
return false;
+ if (left->isDirectMethod() != right->isDirectMethod())
+ return false;
+
if (getLangOpts().ObjCAutoRefCount &&
(left->hasAttr<NSReturnsRetainedAttr>()
!= right->hasAttr<NSReturnsRetainedAttr>() ||
@@ -3430,6 +3433,9 @@ static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen,
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;
@@ -4339,6 +4345,18 @@ private:
};
} // end anonymous namespace
+void Sema::CheckObjCMethodDirectOverrides(ObjCMethodDecl *method,
+ ObjCMethodDecl *overridden) {
+ if (const auto *attr = overridden->getAttr<ObjCDirectAttr>()) {
+ Diag(method->getLocation(), diag::err_objc_override_direct_method);
+ Diag(attr->getLocation(), diag::note_previous_declaration);
+ } else if (const auto *attr = method->getAttr<ObjCDirectAttr>()) {
+ Diag(attr->getLocation(), diag::err_objc_direct_on_override)
+ << isa<ObjCProtocolDecl>(overridden->getDeclContext());
+ Diag(overridden->getLocation(), diag::note_previous_declaration);
+ }
+}
+
void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCInterfaceDecl *CurrentClass,
ResultTypeCompatibilityKind RTC) {
@@ -4357,8 +4375,8 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
if (isa<ObjCProtocolDecl>(overridden->getDeclContext()) ||
CurrentClass != overridden->getClassInterface() ||
overridden->isOverriding()) {
+ CheckObjCMethodDirectOverrides(ObjCMethod, overridden);
hasOverriddenMethodsInBaseOrProtocol = true;
-
} else if (isa<ObjCImplDecl>(ObjCMethod->getDeclContext())) {
// OverrideSearch will return as "overridden" the same method in the
// interface. For hasOverriddenMethodsInBaseOrProtocol, we need to
@@ -4382,6 +4400,7 @@ void Sema::CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
for (ObjCMethodDecl *SuperOverridden : overrides) {
if (isa<ObjCProtocolDecl>(SuperOverridden->getDeclContext()) ||
CurrentClass != SuperOverridden->getClassInterface()) {
+ CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden);
hasOverriddenMethodsInBaseOrProtocol = true;
overridden->setOverriding(true);
break;
@@ -4489,6 +4508,12 @@ static void mergeInterfaceMethodToImpl(Sema &S,
method->getLocation()));
}
+ if (!method->isDirectMethod())
+ if (const auto *attr = prevMethod->getAttr<ObjCDirectAttr>()) {
+ method->addAttr(
+ ObjCDirectAttr::CreateImplicit(S.Context, attr->getLocation()));
+ }
+
// Merge nullability of the result type.
QualType newReturnType
= mergeTypeNullabilityForRedecl(
@@ -4719,6 +4744,12 @@ Decl *Sema::ActOnMethodDeclaration(
if (auto *IMD = IDecl->lookupMethod(ObjCMethod->getSelector(),
ObjCMethod->isInstanceMethod())) {
mergeInterfaceMethodToImpl(*this, ObjCMethod, IMD);
+ if (const auto *attr = ObjCMethod->getAttr<ObjCDirectAttr>()) {
+ 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<ObjCCategoryImplDecl>(ImpDecl) && IMD->isOverriding() &&
@@ -4726,6 +4757,9 @@ Decl *Sema::ActOnMethodDeclaration(
Diag(ObjCMethod->getLocation(), diag::warn_dealloc_in_category)
<< ObjCMethod->getDeclName();
}
+ } else if (ImpDecl->hasAttr<ObjCDirectMembersAttr>()) {
+ ObjCMethod->addAttr(
+ ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
}
// Warn if a method declared in a protocol to which a category or
@@ -4745,6 +4779,11 @@ Decl *Sema::ActOnMethodDeclaration(
}
}
} else {
+ if (!ObjCMethod->isDirectMethod() &&
+ ClassDecl->hasAttr<ObjCDirectMembersAttr>()) {
+ ObjCMethod->addAttr(
+ ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
+ }
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
}
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index 8db4e0ba74a..687348c90a8 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1170,6 +1170,35 @@ static void DiagnoseMismatchedSelectors(Sema &S, SourceLocation AtLoc,
}
}
+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,
@@ -1192,9 +1221,18 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel,
} 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 &&
@@ -2767,9 +2805,6 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
}
}
- 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.
@@ -2971,6 +3006,30 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver,
(Method && Method->getMethodFamily() == OMF_init)
? getEnclosingFunction() : nullptr;
+ if (Method && Method->isDirectMethod()) {
+ if (ReceiverType->isObjCIdType() && !isImplicit) {
+ Diag(Receiver->getExprLoc(),
+ diag::err_messaging_unqualified_id_with_direct_method);
+ Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+ << Method->getDeclName();
+ }
+
+ if (ReceiverType->isObjCClassType() && !isImplicit) {
+ Diag(Receiver->getExprLoc(),
+ diag::err_messaging_class_with_direct_method);
+ Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+ << Method->getDeclName();
+ }
+
+ if (SuperLoc.isValid()) {
+ Diag(SuperLoc, diag::err_messaging_super_with_direct_method);
+ Diag(Method->getLocation(), diag::note_direct_method_declared_at)
+ << Method->getDeclName();
+ }
+ } else if (ReceiverType->isObjCIdType() && !isImplicit) {
+ Diag(Receiver->getExprLoc(), diag::warn_messaging_unqualified_id);
+ }
+
if (DIFunctionScopeInfo &&
DIFunctionScopeInfo->ObjCIsDesignatedInit &&
(SuperLoc.isValid() || isSelfExpr(Receiver))) {
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index 558a754a445..87f7baaf568 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -306,6 +306,8 @@ makePropertyAttributesAsWritten(unsigned Attributes) {
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,21 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S,
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) ||
+ CDecl->hasAttr<ObjCDirectMembersAttr>()) {
+ if (isa<ObjCProtocolDecl>(CDecl)) {
+ Diag(PDecl->getLocation(), diag::err_objc_direct_on_protocol) << true;
+ } else if (getLangOpts().ObjCRuntime.allowsDirectDispatch()) {
+ PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_direct);
+ } else {
+ Diag(PDecl->getLocation(), diag::warn_objc_direct_property_ignored)
+ << PDecl->getDeclName();
+ }
+ }
+
return PDecl;
}
@@ -2460,6 +2474,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) {
AddPropertyAttrs(*this, GetterMethod, property);
+ if (property->isDirectProperty())
+ GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc));
+
if (property->hasAttr<NSReturnsNotRetainedAttr>())
GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context,
Loc));
@@ -2534,6 +2551,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) {
AddPropertyAttrs(*this, SetterMethod, property);
+ if (property->isDirectProperty())
+ SetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc));
+
CD->addDecl(SetterMethod);
if (const SectionAttr *SA = property->getAttr<SectionAttr>())
SetterMethod->addAttr(SectionAttr::CreateImplicit(
diff --git a/clang/test/CodeGenObjC/direct-method.m b/clang/test/CodeGenObjC/direct-method.m
new file mode 100644
index 00000000000..dd9b670b3e4
--- /dev/null
+++ b/clang/test/CodeGenObjC/direct-method.m
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -emit-llvm -fobjc-arc -triple x86_64-apple-darwin10 %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
+// CHECK-LABEL: define hidden i32 @"\01-[Root getInt]"(
+- (int)getInt __attribute__((objc_direct)) {
+ // loading parameters
+ // CHECK-LABEL: entry:
+ // CHECK-NEXT: [[RETVAL:%.*]] = alloca
+ // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
+ // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
+ // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
+ // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
+ // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
+ // CHECK-NEXT: br i1 [[NILCHECK]],
+
+ // setting return value to nil
+ // CHECK-LABEL: objc_direct_method.self_is_nil:
+ // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
+ // CHECK-NEXT: br label
+
+ // set value
+ // CHECK-LABEL: objc_direct_method.cont:
+ // CHECK: store{{.*}}[[RETVAL]],
+ // CHECK-NEXT: br label
+
+ // return
+ // CHECK-LABEL: return:
+ // CHECK: {{%.*}} = load{{.*}}[[RETVAL]],
+ // CHECK-NEXT: ret
+ return 42;
+}
+
+// CHECK-LABEL: define hidden i32 @"\01+[Root classGetInt]"(
++ (int)classGetInt __attribute__((objc_direct)) {
+ // loading parameters
+ // CHECK-LABEL: entry:
+ // CHECK-NEXT: [[SELFADDR:%.*]] = alloca i8*,
+ // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
+ // CHECK-NEXT: store i8* %{{.*}}, i8** [[SELFADDR]],
+ // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
+
+ // [self self]
+ // CHECK-NEXT: [[SELF:%.*]] = load i8*, i8** [[SELFADDR]],
+ // CHECK-NEXT: [[SELFSEL:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[SELF0:%.*]] = call {{.*}} @objc_msgSend
+ // CHECK-NEXT: store i8* [[SELF0]], i8** [[SELFADDR]],
+
+ // return
+ // CHECK-NEXT: ret
+ return 42;
+}
+
+// CHECK-LABEL: define hidden i64 @"\01-[Root getComplex]"(
+- (struct my_complex_struct)getComplex __attribute__((objc_direct)) {
+ // loading parameters
+ // CHECK-LABEL: entry:
+ // CHECK-NEXT: [[RETVAL:%.*]] = alloca
+ // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
+ // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
+ // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
+ // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
+ // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
+ // CHECK-NEXT: br i1 [[NILCHECK]],
+
+ // setting return value to nil
+ // CHECK-LABEL: objc_direct_method.self_is_nil:
+ // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
+ // CHECK-NEXT: br label
+
+ // set value
+ // CHECK-LABEL: objc_direct_method.cont:
+ // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK-NEXT: call void @llvm.memcpy{{[^(]*}}({{[^,]*}}[[RET1]],
+ // CHECK-NEXT: br label
+
+ // return
+ // CHECK-LABEL: return:
+ // CHECK: [[RET2:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK-NEXT: {{%.*}} = load{{.*}}[[RET2]],
+ // CHECK-NEXT: ret
+ struct my_complex_struct st = {.a = 42};
+ return st;
+}
+
+// CHECK-LABEL: define hidden i64 @"\01+[Root classGetComplex]"(
++ (struct my_complex_struct)classGetComplex __attribute__((objc_direct)) {
+ struct my_complex_struct st = {.a = 42};
+ return st;
+ // CHECK: ret i64
+}
+
+// CHECK-LABEL: define hidden void @"\01-[Root getAggregate]"(
+- (struct my_aggregate_struct)getAggregate __attribute__((objc_direct)) {
+ // CHECK: %struct.my_aggregate_struct* noalias sret [[RETVAL:%[^,]*]],
+
+ // loading parameters
+ // CHECK-LABEL: entry:
+ // CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*,
+ // CHECK-NEXT: [[_CMDADDR:%.*]] = alloca i8*,
+ // CHECK-NEXT: store %0* %{{.*}}, %0** [[SELFADDR]],
+ // CHECK-NEXT: store i8* %{{.*}}, i8** [[_CMDADDR]],
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** [[SELFADDR]],
+ // CHECK-NEXT: [[NILCHECK:%.*]] = icmp eq %0* [[SELF]], null
+ // CHECK-NEXT: br i1 [[NILCHECK]],
+
+ // setting return value to nil
+ // CHECK-LABEL: objc_direct_method.self_is_nil:
+ // CHECK: [[RET0:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK-NEXT: call void @llvm.memset{{[^(]*}}({{[^,]*}}[[RET0]], i8 0,
+ // CHECK-NEXT: br label
+
+ // set value
+ // CHECK-LABEL: objc_direct_method.cont:
+ // CHECK: [[RET1:%.*]] = bitcast{{.*}}[[RETVAL]]
+ // CHECK: br label
+
+ // return
+ // CHECK-LABEL: return:
+ // CHECK: ret void
+ struct my_aggregate_struct st = {.a = 42};
+ return st;
+}
+
+// CHECK-LABEL: define hidden void @"\01+[Root classGetAggregate]"(
++ (struct my_aggregate_struct)classGetAggregate __attribute__((objc_direct)) {
+ struct my_aggregate_struct st = {.a = 42};
+ return st;
+ // CHECK: ret void
+}
+
+@end
+
+@interface Foo : Root {
+ id __strong _cause_cxx_destruct;
+}
+@property(nonatomic, readonly, direct) int getDirect_setDynamic;
+@property(nonatomic, readonly) int getDynamic_setDirect;
+@end
+
+@interface Foo ()
+@property(nonatomic, readwrite) int getDirect_setDynamic;
+@property(nonatomic, readwrite, direct) int getDynamic_setDirect;
+@end
+
+__attribute__((objc_direct_members))
+@implementation Foo
+// CHECK-LABEL: define hidden i32 @"\01-[Foo getDirect_setDynamic]"(
+// CHECK-LABEL: define internal void @"\01-[Foo setGetDirect_setDynamic:]"(
+// CHECK-LABEL: define internal i32 @"\01-[Foo getDynamic_setDirect]"(
+// CHECK-LABEL: define hidden void @"\01-[Foo setGetDynamic_setDirect:]"(
+// CHECK-LABEL: define internal void @"\01-[Foo .cxx_destruct]"(
+@end
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 877f6ee6f3f..2effc52eb98 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -105,6 +105,8 @@
// CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record)
// CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method)
+// CHECK-NEXT: ObjCDirect (SubjectMatchRule_objc_method)
+// CHECK-NEXT: ObjCDirectMembers (SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_category)
// 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)
diff --git a/clang/test/SemaObjC/method-direct-properties.m b/clang/test/SemaObjC/method-direct-properties.m
new file mode 100644
index 00000000000..26d13010551
--- /dev/null
+++ b/clang/test/SemaObjC/method-direct-properties.m
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wselector-type-mismatch %s
+
+@protocol ProtoDirectFail
+@property(nonatomic, direct) int protoProperty; // expected-error {{'objc_direct' attribute cannot be applied to properties declared in an Objective-C protocol}}
+@end
+
+__attribute__((objc_root_class))
+@interface Root
+@property(nonatomic, direct) int propertyWithNonDirectGetter; // expected-note {{previous declaration is here}}
+- (int)propertyWithNonDirectGetter;
+- (int)propertyWithNonDirectGetter2;
+- (int)propertyWithNonDirectGetterInParent;
+- (int)propertyWithNonDirectGetterInParent2;
+
+@property(nonatomic, readonly, direct) int getDirect_setDynamic; // expected-note {{previous declaration is here}}
+@property(nonatomic, readonly, direct) int getDirect_setDirect; // expected-note {{previous declaration is here}}
+@property(nonatomic, readonly, direct) int getDirect_setDirectMembers; // expected-note {{previous declaration is here}}
+
+@property(nonatomic, readonly) int getDynamic_setDirect;
+@property(nonatomic, readonly) int getDynamic_setDirectMembers;
+
+@property(nonatomic, readonly) int dynamicProperty;
+@property(nonatomic, readonly) int synthDynamicProperty;
+
+@property(nonatomic, readonly, direct) int directProperty; // expected-note {{previous declaration is here}}
+@property(nonatomic, readonly, direct) int synthDirectProperty; // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_direct_members))
+@interface
+Root()
+@property(nonatomic) int propertyWithNonDirectGetter2; // expected-note {{previous declaration is here}}
+
+@property(nonatomic, readwrite) int getDirect_setDirectMembers; // expected-note {{previous declaration is here}}
+@property(nonatomic, readwrite) int getDynamic_setDirectMembers; // expected-note {{previous declaration is here}}
+@end
+
+@interface Root ()
+@property(nonatomic, readwrite) int getDirect_setDynamic;
+@property(nonatomic, readwrite, direct) int getDirect_setDirect; // expected-note {{previous declaration is here}}
+
+@property(nonatomic, readwrite, direct) int getDynamic_setDirect; // expected-note {{previous declaration is here}}
+@end
+
+@interface Sub : Root
+@property(nonatomic, direct) int propertyWithNonDirectGetterInParent; // expected-note {{previous declaration is here}}
+
+- (int)propertyWithNonDirectGetter; // no error: legal override
+- (int)propertyWithNonDirectGetter2; // no error: legal override
+- (int)propertyWithNonDirectGetterInParent; // no error: legal override
+- (int)propertyWithNonDirectGetterInParent2; // no error: legal override
+
+@end
+
+__attribute__((objc_direct_members))
+@interface Sub ()
+@property(nonatomic) int propertyWithNonDirectGetterInParent2; // expected-note {{previous declaration is here}}
+@end
+
+// make sure that the `directness` of methods stuck,
+// by observing errors trying to override the setter
+@interface SubWitness : Sub
+
+- (int)setPropertyWithNonDirectGetter:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setPropertyWithNonDirectGetter2:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setPropertyWithNonDirectGetterInParent:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setPropertyWithNonDirectGetterInParent2:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+
+- (int)getDirect_setDynamic; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setGetDirect_setDynamic:(int)value;
+- (int)getDirect_setDirect; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setGetDirect_setDirect:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)getDirect_setDirectMembers; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)setGetDirect_setDirectMembers:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+
+- (int)getDynamic_setDirect;
+- (int)setGetDynamic_setDirect:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (int)getDynamic_setDirectMembers;
+- (int)setGetDynamic_setDirectMembers:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+@end
+
+__attribute__((objc_direct_members))
+@implementation Root
+- (int)propertyWithNonDirectGetter {
+ return 42;
+}
+- (int)propertyWithNonDirectGetter2 {
+ return 42;
+}
+- (int)propertyWithNonDirectGetterInParent {
+ return 42;
+}
+- (int)propertyWithNonDirectGetterInParent2 {
+ return 42;
+}
+
+- (int)dynamicProperty {
+ return 42;
+}
+- (int)directProperty {
+ return 42;
+}
+@end
+
+@implementation Sub
+- (int)propertyWithNonDirectGetter {
+ return 42;
+}
+- (int)propertyWithNonDirectGetter2 {
+ return 42;
+}
+
+- (int)dynamicProperty {
+ return 42;
+}
+- (int)synthDynamicProperty {
+ return 42;
+}
+
+- (int)directProperty { // expected-error {{cannot override a method that is declared direct by a superclass}}
+ return 42;
+}
+- (int)synthDirectProperty { // expected-error {{cannot override a method that is declared direct by a superclass}}
+ return 42;
+}
+@end
diff --git a/clang/test/SemaObjC/method-direct.m b/clang/test/SemaObjC/method-direct.m
new file mode 100644
index 00000000000..4829a67cd8a
--- /dev/null
+++ b/clang/test/SemaObjC/method-direct.m
@@ -0,0 +1,148 @@
+// 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)otherRootDirect __attribute__((objc_direct)); // expected-note {{direct method 'otherRootDirect' declared here}}
++ (void)otherClassRootDirect __attribute__((objc_direct)); // expected-note {{direct method 'otherClassRootDirect' declared here}}
+- (void)notDirectInIface; // expected-note {{previous declaration is here}}
++ (void)classNotDirectInIface; // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_direct_members))
+@interface Root ()
+- (void)rootExtensionDirect; // expected-note {{previous declaration is here}}
++ (void)classRootExtensionDirect; // expected-note {{previous declaration is here}}
+@end
+
+__attribute__((objc_direct_members))
+@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_members)) // expected-error {{'objc_direct_members' attribute only applies to Objective-C implementation declarations and Objective-C containers}}
+@interface SubDirectFail : Root
+- (instancetype)init;
+@end
+
+@interface Sub : Root <Proto>
+/* invalid overrides with directs */
+- (void)rootRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}}
++ (void)classRootRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}}
+- (void)protoMethod __attribute__((objc_direct)); // expected-error {{methods that implement protocol requirements cannot be direct}}
++ (void)classProtoMethod __attribute__((objc_direct)); // expected-error {{methods that implement protocol requirements cannot be direct}}
+- (void)rootExtensionRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}}
++ (void)classRootExtensionRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}}
+- (void)rootCategoryRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods cannot be direct}}
++ (void)classRootCategoryRegular __attribute__((objc_direct)); // expected-error {{methods that override superclass methods 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)otherRootDirect {
+}
++ (void)otherClassRootDirect {
+}
+- (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
+
+@interface ValidSub : Root
+@end
+
+@implementation ValidSub
+- (void)someValidSubMethod {
+ [super otherRootDirect]; // expected-error {{messaging super with a direct method}}
+}
+@end
+
+extern void callMethod(id obj, Class cls);
+extern void useSel(SEL sel);
+
+void callMethod(id obj, Class cls) {
+ [Root otherClassRootDirect];
+ [cls otherClassRootDirect]; // expected-error {{messaging a Class with a method that is possibly direct}}
+ [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}}
+}
OpenPOWER on IntegriCloud