MadCoder updated this revision to Diff 229183.
MadCoder marked 2 inline comments as done.
MadCoder added a comment.
Beefed up the tests, addressed the `selfValue` related issue.
Also rolled back to the `ObjCDeclInterfaceDecl` cast in GenerateDirectMethod as
it turns out that all callers pass this type rather than an `ObjCImplDecl`
however it's never nil and always of that type so make this an assert instead.
Did a pass of clang-format to clean up style
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D69991/new/
https://reviews.llvm.org/D69991
Files:
clang/include/clang/AST/DeclObjC.h
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/ObjCRuntime.h
clang/include/clang/Sema/DeclSpec.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/DeclObjC.cpp
clang/lib/AST/DeclPrinter.cpp
clang/lib/AST/JSONNodeDumper.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/CodeGen/CGObjC.cpp
clang/lib/CodeGen/CGObjCGNU.cpp
clang/lib/CodeGen/CGObjCMac.cpp
clang/lib/CodeGen/CGObjCRuntime.h
clang/lib/Parse/ParseObjc.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaDeclObjC.cpp
clang/lib/Sema/SemaExprObjC.cpp
clang/lib/Sema/SemaObjCProperty.cpp
clang/test/CodeGenObjC/direct-method.m
clang/test/Misc/pragma-attribute-supported-attributes-list.test
clang/test/SemaObjC/method-direct.m
Index: clang/test/SemaObjC/method-direct.m
===================================================================
--- /dev/null
+++ clang/test/SemaObjC/method-direct.m
@@ -0,0 +1,139 @@
+// 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
+@property(nonatomic, direct) int protoProperty; // expected-error {{'objc_direct' attribute cannot be applied to properties declared in an Objective-C protocol}}
+- (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
+@property(nonatomic, direct) int directOrNot; // expected-note {{previous declaration is here}}
+- (int)directOrNot;
+- (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_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}}
+
+/* allowed override because definition wasn't marked explicitly */
+- (int)directOrNot;
+
+/* invalid overrides of directs */
+- (void)setDirectOrNot:(int)value; // expected-error {{cannot override a method that is declared direct by a superclass}}
+- (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}}
+}
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)
+// 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)
Index: clang/test/CodeGenObjC/direct-method.m
===================================================================
--- /dev/null
+++ clang/test/CodeGenObjC/direct-method.m
@@ -0,0 +1,154 @@
+// 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: %self.addr = alloca %0*,
+ // CHECK-NEXT: %_cmd.addr = alloca i8*,
+ // CHECK-NEXT: store %0* %self, %0** %self.addr,
+ // CHECK-NEXT: store i8* %_cmd, i8** %_cmd.addr,
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** %self.addr,
+ // 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: %self.addr = alloca i8*,
+ // CHECK-NEXT: %_cmd.addr = alloca i8*,
+ // CHECK-NEXT: store i8* %self, i8** %self.addr,
+ // CHECK-NEXT: store i8* %_cmd, i8** %_cmd.addr,
+
+ // [self self]
+ // CHECK-NEXT: [[SELF:%.*]] = load i8*, i8** %self.addr,
+ // CHECK-NEXT: [[SELFSEL:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_
+ // CHECK-NEXT: [[SELF0:%.*]] = call {{.*}} @objc_msgSend
+ // CHECK-NEXT: store i8* [[SELF0]], i8** %self.addr,
+
+ // 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: %self.addr = alloca %0*,
+ // CHECK-NEXT: %_cmd.addr = alloca i8*,
+ // CHECK-NEXT: store %0* %self, %0** %self.addr,
+ // CHECK-NEXT: store i8* %_cmd, i8** %_cmd.addr,
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** %self.addr,
+ // 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: %self.addr = alloca %0*,
+ // CHECK-NEXT: %_cmd.addr = alloca i8*,
+ // CHECK-NEXT: store %0* %self, %0** %self.addr,
+ // CHECK-NEXT: store i8* %_cmd, i8** %_cmd.addr,
+
+ // self nil-check
+ // CHECK-NEXT: [[SELF:%.*]] = load %0*, %0** %self.addr,
+ // 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
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,20 @@
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) {
+ 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;
}
@@ -2410,6 +2423,9 @@
AddPropertyAttrs(*this, GetterMethod, property);
+ if (property->isDirectProperty())
+ GetterMethod->addAttr(ObjCDirectAttr::CreateImplicit(Context, Loc));
+
if (property->hasAttr<NSReturnsNotRetainedAttr>())
GetterMethod->addAttr(NSReturnsNotRetainedAttr::CreateImplicit(Context,
Loc));
@@ -2483,6 +2499,9 @@
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(
Index: clang/lib/Sema/SemaExprObjC.cpp
===================================================================
--- clang/lib/Sema/SemaExprObjC.cpp
+++ clang/lib/Sema/SemaExprObjC.cpp
@@ -1118,6 +1118,16 @@
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 +1149,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 +1182,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 +1233,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 +2817,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 +3018,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/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<NSReturnsRetainedAttr>()
!= right->hasAttr<NSReturnsRetainedAttr>() ||
@@ -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,18 @@
};
} // 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) {
@@ -4332,8 +4350,8 @@
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
@@ -4357,6 +4375,7 @@
for (ObjCMethodDecl *SuperOverridden : overrides) {
if (isa<ObjCProtocolDecl>(SuperOverridden->getDeclContext()) ||
CurrentClass != SuperOverridden->getClassInterface()) {
+ CheckObjCMethodDirectOverrides(ObjCMethod, SuperOverridden);
hasOverriddenMethodsInBaseOrProtocol = true;
overridden->setOverriding(true);
break;
@@ -4464,6 +4483,12 @@
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(
@@ -4672,6 +4697,12 @@
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() &&
@@ -4679,6 +4710,9 @@
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
@@ -4698,6 +4732,11 @@
}
}
} else {
+ if (!ObjCMethod->isDirectMethod() &&
+ ClassDecl->hasAttr<ObjCDirectMembersAttr>()) {
+ ObjCMethod->addAttr(
+ ObjCDirectAttr::CreateImplicit(Context, ObjCMethod->getLocation()));
+ }
cast<DeclContext>(ClassDecl)->addDecl(ObjCMethod);
}
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2600,6 +2600,29 @@
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)) {
@@ -6909,6 +6932,13 @@
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;
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/CodeGen/CGObjCRuntime.h
===================================================================
--- clang/lib/CodeGen/CGObjCRuntime.h
+++ clang/lib/CodeGen/CGObjCRuntime.h
@@ -169,6 +169,21 @@
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 @@
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/CodeGen/CGObjCMac.cpp
===================================================================
--- clang/lib/CodeGen/CGObjCMac.cpp
+++ clang/lib/CodeGen/CGObjCMac.cpp
@@ -874,6 +874,10 @@
/// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF,
ReturnValueSlot Return,
QualType ResultType,
- llvm::Value *Sel,
+ Selector Sel,
llvm::Value *Arg0,
QualType Arg0Ty,
bool IsSuper,
@@ -2145,11 +2158,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 +2214,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 +3323,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 +3400,8 @@
};
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,13 +3583,16 @@
};
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) {
ObjCPropertyDecl *PD = PID->getPropertyDecl();
+ if (PD->isDirectProperty())
+ continue;
if (ObjCMethodDecl *MD = PD->getGetterMethodDecl())
if (GetMethodDefinition(MD))
Methods[InstanceMethods].push_back(MD);
@@ -3959,7 +3991,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,6 +4003,34 @@
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);
@@ -3977,15 +4038,94 @@
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;
+
+ // 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
+ result = GeneratePossiblySpecializedMessageSend(
+ CGF, ReturnValueSlot(), ResultType, SelfSel, selfValue, Args, OID,
+ nullptr, true);
+ Builder.CreateStore(result.getScalarVal(), selfAddr);
+
+ 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()) {
+ 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 +6368,18 @@
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);
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 +6708,8 @@
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 {
@@ -7234,8 +7379,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 +7610,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 +7635,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/CGObjCGNU.cpp
===================================================================
--- clang/lib/CodeGen/CGObjCGNU.cpp
+++ clang/lib/CodeGen/CGObjCGNU.cpp
@@ -606,6 +606,9 @@
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 +3876,13 @@
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/CGObjC.cpp
===================================================================
--- clang/lib/CodeGen/CGObjC.cpp
+++ clang/lib/CodeGen/CGObjC.cpp
@@ -430,6 +430,20 @@
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 @@
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 @@
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 @@
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/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/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/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/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<ObjCDirectAttr>();
+}
+
bool ObjCMethodDecl::isThisDeclarationADesignatedInitializer() const {
return getMethodFamily() == OMF_init &&
hasAttr<ObjCDesignatedInitializerAttr>();
@@ -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/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/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/Basic/ObjCRuntime.h
===================================================================
--- clang/include/clang/Basic/ObjCRuntime.h
+++ clang/include/clang/Basic/ObjCRuntime.h
@@ -446,6 +446,20 @@
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.
///
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -987,6 +987,22 @@
"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">,
@@ -1072,6 +1088,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 +1324,8 @@
"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">;
@@ -1320,6 +1339,8 @@
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">;
// C++ declarations
def err_static_assert_expression_is_not_constant : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -3897,6 +3897,69 @@
}];
}
+def ObjCDirectDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+This attribute can be added on methods declaration and implementation,
+to mark it as being called directly.
+
+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`` attribute can be used on ``@implementation``
+and ``@interface`` blocks defining Categories or Extensions to mark methods
+as being direct calls in bulk.
+ }];
+}
+
+def ObjCDirectMembersDocs : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+This attribute can decorate classes ``@interface`` Categories and Extensions,
+or ``@implementation`` blocks. This attribute isn't allowed on the principal
+class ``@interface`` or on ``@protocol`` definitions.
+
+For Categories and Extensions, all methods and properties declared in this
+``@interface`` are implicitly marked with the ``objc_direct`` attribute.
+
+For ``@implementation`` blocks, then methods that are not declared in any
+``@interface`` (Principal interface, Category or Extension) of this class
+or any of its superclass, as well as methods implementing an ``@protocol``
+conformance, are implicitly marked with the ``objc_direct`` attribute.
+ }];
+}
+
def SelectAnyDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
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], 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>;
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 :
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits