-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[IRGen] Call objc_direct methods correctly #33349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,27 @@ | ||
// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_direct.h -verify-ignore-unknown | ||
// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/../Inputs/objc_direct.h | ||
|
||
// REQUIRES: objc_interop | ||
|
||
func callThingsOnBar(_ x: Bar) { | ||
let _ = x.directProperty // expected-error {{getter for 'directProperty' is unavailable in Swift}} | ||
let _ = x.directProperty2 // expected-error {{getter for 'directProperty2' is unavailable in Swift}} | ||
var something = Bar() as AnyObject | ||
|
||
x.directMethod() // expected-error {{'directMethod()' is unavailable in Swift}} | ||
x.directMethod2() // expected-error {{'directMethod2()' is unavailable in Swift}} | ||
something.directProperty = 123 // expected-error {{value of type 'AnyObject' has no member 'directProperty'}} | ||
let _ = something.directProperty // expected-error {{value of type 'AnyObject' has no member 'directProperty'}} | ||
|
||
Bar.directClassMethod() // expected-error {{'directClassMethod()' is unavailable in Swift}} | ||
Bar.directClassMethod2() // expected-error {{'directClassMethod2()' is unavailable in Swift}} | ||
something.directProperty2 = 456 // expected-error {{value of type 'AnyObject' has no member 'directProperty2'}} | ||
let _ = something.directProperty2 // expected-error {{value of type 'AnyObject' has no member 'directProperty2'}} | ||
|
||
let _ = something.directMethod() // expected-error {{value of type 'AnyObject' has no member 'directMethod'}} | ||
|
||
let _ = something.directMethod2() // expected-error {{value of type 'AnyObject' has no member 'directMethod2'}} | ||
|
||
class Foo { | ||
// Test that we can use a method with the same name as some `objc_direct` method. | ||
@objc func directProtocolMethod() -> String { | ||
"This is not a direct method." | ||
} | ||
} | ||
|
||
var otherthing = Foo() as AnyObject | ||
|
||
// We expect no error. | ||
let _ = otherthing.directProtocolMethod() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// RUN: %target-swift-emit-ir -import-objc-header %S/../Inputs/objc_direct.h -o - %s | %FileCheck %s | ||
|
||
// REQUIRES: objc_interop | ||
|
||
func markUsed<T>(_ t: T) {} | ||
|
||
protocol BarProtocol { | ||
func directProtocolMethod() -> String! | ||
} | ||
|
||
extension Bar: BarProtocol {} | ||
|
||
let bar = Bar() | ||
|
||
bar.directProperty = 123 | ||
// CHECK: call void @"\01-[Bar setDirectProperty:]"({{.*}}, i8* undef, i32 {{.*}}) | ||
|
||
markUsed(bar.directProperty) | ||
// CHECK: call i32 @"\01-[Bar directProperty]"({{.*}}, i8* undef) | ||
|
||
bar.directProperty2 = 456 | ||
// CHECK: call void @"\01-[Bar setDirectProperty2:]"({{.*}}, i8* undef, i32 {{.*}}) | ||
|
||
markUsed(bar.directProperty2) | ||
// CHECK: call i32 @"\01-[Bar directProperty2]"({{.*}}, i8* undef) | ||
|
||
bar[0] = 789 | ||
// CHECK: call void @"\01-[Bar setObject:atIndexedSubscript:]"({{.*}}, i8* undef, i32 789, i32 0) | ||
|
||
markUsed(bar[0]) | ||
// CHECK: call i32 @"\01-[Bar objectAtIndexedSubscript:]"({{.*}}, i8* undef, i32 0) | ||
|
||
markUsed(bar.directMethod()) | ||
// CHECK: call {{.*}} @"\01-[Bar directMethod]"({{.*}}, i8* undef) | ||
|
||
markUsed(bar.directMethod2()) | ||
// CHECK: call {{.*}} @"\01-[Bar directMethod2]"({{.*}}, i8* undef) | ||
|
||
markUsed(Bar.directClassMethod()) | ||
// NOTE: The class must be realized before calling objc_direct class methods, even if | ||
// Swift avoids explicit class realization before calling regular class methods. | ||
// CHECK: [[R0:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Bar" | ||
// CHECK: [[R1:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[R0]]) | ||
// CHECK: [[R2:%.*]] = bitcast %objc_class* [[R1]] to i8* | ||
// CHECK: call {{.*}} @"\01+[Bar directClassMethod]"(i8* [[R2]], i8* undef) | ||
|
||
markUsed(Bar.directClassMethod2()) | ||
// CHECK: [[R3:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Bar" | ||
// CHECK: [[R4:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[R3]]) | ||
// CHECK: [[R5:%.*]] = bitcast %objc_class* [[R4]] to i8* | ||
// CHECK: call {{.*}} @"\01+[Bar directClassMethod2]"(i8* [[R5]], i8* undef) | ||
|
||
markUsed(bar.directProtocolMethod()) | ||
// CHECK: call {{.*}} @"\01-[Bar directProtocolMethod]"({{.*}}, i8* undef) | ||
|
||
// CHECK-DAG: declare i32 @"\01-[Bar directProperty]" | ||
// CHECK-DAG: declare void @"\01-[Bar setDirectProperty:]" | ||
// CHECK-DAG: declare i32 @"\01-[Bar directProperty2]" | ||
// CHECK-DAG: declare void @"\01-[Bar setDirectProperty2:]" | ||
// CHECK-DAG: declare void @"\01-[Bar setObject:atIndexedSubscript:]" | ||
// CHECK-DAG: declare i32 @"\01-[Bar objectAtIndexedSubscript:]" | ||
// CHECK-DAG: declare {{.*}} @"\01-[Bar directMethod]" | ||
// CHECK-DAG: declare {{.*}} @"\01-[Bar directMethod2]" | ||
// CHECK-DAG: declare {{.*}} @"\01+[Bar directClassMethod]" | ||
// CHECK-DAG: declare {{.*}} @"\01+[Bar directClassMethod2]" | ||
// CHECK-DAG: declare {{.*}} @"\01-[Bar directProtocolMethod]" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ @interface MyCls1(ext_in_objc) | |
// CHECK: [[@LINE-1]]:12 | class/Swift | MyCls1 | [[MyCls1_USR]] | | ||
// CHECK: [[@LINE-2]]:19 | extension/ObjC | ext_in_objc | c:@M@cross_language@objc(cy)MyCls1@ext_in_objc | | ||
-(void)someMethFromObjC; | ||
// CHECK: [[@LINE-1]]:8 | instance-method/ObjC | someMethFromObjC | [[someMethFromObjC_USR:.*]] | -[ext_in_objc someMethFromObjC] | ||
// CHECK: [[@LINE-1]]:8 | instance-method/ObjC | someMethFromObjC | [[someMethFromObjC_USR:.*]] | -[MyCls1(ext_in_objc) someMethFromObjC] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rjmccall I want to draw your attention to this test I fixed that breaks when using swiftlang/llvm-project#2028. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks |
||
@end | ||
|
||
void test1() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#import <Foundation/Foundation.h> | ||
|
||
@interface Bar : NSObject | ||
@property(direct) int directProperty; | ||
- (int)objectAtIndexedSubscript:(int)i __attribute__((objc_direct)); | ||
- (void)setObject:(int)obj atIndexedSubscript:(int)i __attribute__((objc_direct)); | ||
- (NSString *)directMethod __attribute__((objc_direct)); | ||
+ (NSString *)directClassMethod __attribute__((objc_direct)); | ||
- (NSString *)directProtocolMethod __attribute__((objc_direct)); | ||
@end | ||
|
||
__attribute__((objc_direct_members)) | ||
@interface Bar(CategoryName) | ||
@property int directProperty2; | ||
- (NSString *)directMethod2; | ||
+ (NSString *)directClassMethod2; | ||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an interesting question whether suppressing this in lookup is the right thing to do, as opposed to diagnosing the attempt during type-checking. I've asked @DougGregor and @brentdax to weigh in here. Naively I'd think we should diagnose during type-checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is effectively a choice between emitting a generic and slightly incorrect "no method found"-type diagnostic, and emitting a tailored "direct methods cannot be called through AnyObject dispatch"-type diagnostic.
I think it'd be better to emit a tailored diagnostic, but it could be left out if that would be very difficult, or it could be emitted based on a separate lookup performed while diagnosing the error. Please make sure you include a test case where one class has a direct method and another has a normal method by the same name—we'll want to make sure that the direct method doesn't stop you from using the normal one through AnyObject dispatch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do agree that this is a very unhelpful error message for developers who might be surprised to see a "no method found"-type message after adding an
objc_direct
attribute. I'm not very familiar with this part of the codebase, where would we put the lookup to diagnose the error?And thanks for the test case, I'll add that.