Skip to content

Commit e35f077

Browse files
authored
Merge pull request #33349 from ellishg/master
[IRGen] Call objc_direct methods correctly
2 parents 540455a + 3aa081c commit e35f077

File tree

17 files changed

+387
-36
lines changed

17 files changed

+387
-36
lines changed

lib/AST/NameLookup.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "clang/AST/DeclObjC.h"
1718
#include "swift/AST/NameLookup.h"
1819
#include "swift/AST/ASTContext.h"
1920
#include "swift/AST/ASTVisitor.h"
@@ -1837,6 +1838,17 @@ AnyObjectLookupRequest::evaluate(Evaluator &evaluator, const DeclContext *dc,
18371838
if (!decl->isObjC())
18381839
continue;
18391840

1841+
// If the declaration is objc_direct, it cannot be called dynamically.
1842+
if (auto clangDecl = decl->getClangDecl()) {
1843+
if (auto objCMethod = dyn_cast<clang::ObjCMethodDecl>(clangDecl)) {
1844+
if (objCMethod->isDirectMethod())
1845+
continue;
1846+
} else if (auto objCProperty = dyn_cast<clang::ObjCPropertyDecl>(clangDecl)) {
1847+
if (objCProperty->isDirectProperty())
1848+
continue;
1849+
}
1850+
}
1851+
18401852
// If the declaration has an override, name lookup will also have
18411853
// found the overridden method. Skip this declaration, because we
18421854
// prefer the overridden method.
@@ -1848,7 +1860,6 @@ AnyObjectLookupRequest::evaluate(Evaluator &evaluator, const DeclContext *dc,
18481860

18491861
// If we didn't see this declaration before, and it's an acceptable
18501862
// result, add it to the list.
1851-
// declaration to the list.
18521863
if (knownDecls.insert(decl).second &&
18531864
isAcceptableLookupResult(dc, options, decl,
18541865
/*onlyCompleteObjectInits=*/false))

lib/ClangImporter/ImportDecl.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4579,7 +4579,8 @@ namespace {
45794579
/*genericParams=*/nullptr, bodyParams,
45804580
resultTy, async, throws, dc, decl);
45814581

4582-
result->setAccess(getOverridableAccessLevel(dc));
4582+
result->setAccess(decl->isDirectMethod() ? AccessLevel::Public
4583+
: getOverridableAccessLevel(dc));
45834584

45844585
// Optional methods in protocols.
45854586
if (decl->getImplementationControl() == clang::ObjCMethodDecl::Optional &&
@@ -5331,8 +5332,9 @@ namespace {
53315332
}
53325333

53335334
auto type = importedType.getType();
5334-
auto result = Impl.createDeclWithClangNode<VarDecl>(decl,
5335-
getOverridableAccessLevel(dc),
5335+
const auto access = decl->isDirectProperty() ? AccessLevel::Public
5336+
: getOverridableAccessLevel(dc);
5337+
auto result = Impl.createDeclWithClangNode<VarDecl>(decl, access,
53365338
/*IsStatic*/decl->isClassProperty(), VarDecl::Introducer::Var,
53375339
Impl.importSourceLoc(decl->getLocation()), name, dc);
53385340
result->setInterfaceType(type);
@@ -7023,7 +7025,13 @@ SwiftDeclConverter::importSubscript(Decl *decl,
70237025
bodyParams, decl->getLoc(),
70247026
elementTy, dc,
70257027
getter->getClangNode());
7026-
const auto access = getOverridableAccessLevel(dc);
7028+
7029+
bool IsObjCDirect = false;
7030+
if (auto objCDecl = dyn_cast<clang::ObjCMethodDecl>(getter->getClangDecl())) {
7031+
IsObjCDirect = objCDecl->isDirectMethod();
7032+
}
7033+
const auto access = IsObjCDirect ? AccessLevel::Public
7034+
: getOverridableAccessLevel(dc);
70277035
subscript->setAccess(access);
70287036
subscript->setSetterAccess(access);
70297037

@@ -7877,10 +7885,13 @@ void ClangImporter::Implementation::importAttributes(
78777885

78787886
if (auto method = dyn_cast<clang::ObjCMethodDecl>(ClangDecl)) {
78797887
if (method->isDirectMethod() && !AnyUnavailable) {
7880-
auto attr = AvailableAttr::createPlatformAgnostic(
7881-
C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift);
7882-
MappedDecl->getAttrs().add(attr);
7883-
AnyUnavailable = true;
7888+
assert(isa<AbstractFunctionDecl>(MappedDecl) &&
7889+
"objc_direct declarations are expected to be an AbstractFunctionDecl");
7890+
MappedDecl->getAttrs().add(new (C) FinalAttr(/*IsImplicit=*/true));
7891+
if (auto accessorDecl = dyn_cast<AccessorDecl>(MappedDecl)) {
7892+
auto attr = new (C) FinalAttr(/*isImplicit=*/true);
7893+
accessorDecl->getStorage()->getAttrs().add(attr);
7894+
}
78847895
}
78857896
}
78867897

lib/IRGen/GenDecl.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2860,12 +2860,18 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
28602860
// the insert-before point.
28612861
llvm::Constant *clangAddr = nullptr;
28622862
if (auto clangDecl = f->getClangDecl()) {
2863-
auto globalDecl = getClangGlobalDeclForFunction(clangDecl);
2864-
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);
2863+
// If we have an Objective-C Clang declaration, it must be a direct
2864+
// method and we want to generate the IR declaration ourselves.
2865+
if (auto objcDecl = dyn_cast<clang::ObjCMethodDecl>(clangDecl)) {
2866+
assert(objcDecl->isDirectMethod());
2867+
} else {
2868+
auto globalDecl = getClangGlobalDeclForFunction(clangDecl);
2869+
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);
28652870

2866-
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
2867-
clangAddr =
2868-
emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr);
2871+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
2872+
clangAddr =
2873+
emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr);
2874+
}
28692875
}
28702876
}
28712877

lib/IRGen/GenObjC.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,14 @@ Callee irgen::getObjCMethodCallee(IRGenFunction &IGF,
661661
return Callee(std::move(info), fn, receiverValue, selectorValue);
662662
}
663663

664+
Callee irgen::getObjCDirectMethodCallee(CalleeInfo &&info, const FunctionPointer &fn,
665+
llvm::Value *selfValue) {
666+
// Direct calls to Objective-C methods have a selector value of `undef`.
667+
auto selectorType = fn.getFunctionType()->getParamType(1);
668+
auto selectorValue = llvm::UndefValue::get(selectorType);
669+
return Callee(std::move(info), fn, selfValue, selectorValue);
670+
}
671+
664672
/// Call [self allocWithZone: nil].
665673
llvm::Value *irgen::emitObjCAllocObjectCall(IRGenFunction &IGF,
666674
llvm::Value *self,

lib/IRGen/GenObjC.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ namespace irgen {
8888
Callee getObjCMethodCallee(IRGenFunction &IGF, const ObjCMethod &method,
8989
llvm::Value *selfValue, CalleeInfo &&info);
9090

91+
/// Prepare a callee for an Objective-C method with the `objc_direct` attribute.
92+
Callee getObjCDirectMethodCallee(CalleeInfo &&info, const FunctionPointer &fn,
93+
llvm::Value *selfValue);
94+
9195
/// Emit a partial application of an Objective-C method to its 'self'
9296
/// argument.
9397
void emitObjCPartialApplication(IRGenFunction &IGF,

lib/IRGen/IRGenSIL.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,10 @@ Callee LoweredValue::getCallee(IRGenFunction &IGF,
25732573
switch (kind) {
25742574
case Kind::FunctionPointer: {
25752575
auto &fn = getFunctionPointer();
2576+
if (calleeInfo.OrigFnType->getRepresentation() ==
2577+
SILFunctionTypeRepresentation::ObjCMethod) {
2578+
return getObjCDirectMethodCallee(std::move(calleeInfo), fn, selfValue);
2579+
}
25762580
return Callee(std::move(calleeInfo), fn, selfValue);
25772581
}
25782582

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/SIL/SILLinkage.h"
2222
#include "swift/SIL/SILLocation.h"
2323
#include "swift/SILOptimizer/Utils/SpecializationMangler.h"
24+
#include "clang/AST/ASTContext.h"
2425
#include "clang/AST/Attr.h"
2526
#include "clang/AST/Decl.h"
2627
#include "clang/AST/DeclCXX.h"
@@ -717,6 +718,16 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
717718
return SS.str();
718719
}
719720
return namedClangDecl->getName().str();
721+
} else if (auto objcDecl = dyn_cast<clang::ObjCMethodDecl>(clangDecl)) {
722+
if (objcDecl->isDirectMethod()) {
723+
std::string storage;
724+
llvm::raw_string_ostream SS(storage);
725+
clang::ASTContext &ctx = clangDecl->getASTContext();
726+
std::unique_ptr<clang::MangleContext> mangler(ctx.createMangleContext());
727+
mangler->mangleObjCMethodName(objcDecl, SS, /*includePrefixByte=*/true,
728+
/*includeCategoryNamespace=*/false);
729+
return SS.str();
730+
}
720731
}
721732
}
722733
}

lib/SILGen/SILGenApply.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/SIL/SILArgument.h"
4141
#include "clang/AST/DeclCXX.h"
4242
#include "llvm/Support/Compiler.h"
43+
#include "clang/AST/DeclObjC.h"
4344

4445
using namespace swift;
4546
using namespace Lowering;
@@ -1063,7 +1064,18 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
10631064
auto constant = SILDeclRef(afd).asForeign(requiresForeignEntryPoint(afd));
10641065

10651066
auto subs = e->getDeclRef().getSubstitutions();
1066-
setCallee(Callee::forClassMethod(SGF, constant, subs, e));
1067+
1068+
bool isObjCDirect = false;
1069+
if (auto objcDecl = dyn_cast_or_null<clang::ObjCMethodDecl>(
1070+
afd->getClangDecl())) {
1071+
isObjCDirect = objcDecl->isDirectMethod();
1072+
}
1073+
1074+
if (isObjCDirect) {
1075+
setCallee(Callee::forDirect(SGF, constant, subs, e));
1076+
} else {
1077+
setCallee(Callee::forClassMethod(SGF, constant, subs, e));
1078+
}
10671079
}
10681080

10691081
//
@@ -5134,8 +5146,14 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &SGF,
51345146
}
51355147
}
51365148

5149+
bool isObjCDirect = false;
5150+
if (auto objcDecl = dyn_cast_or_null<clang::ObjCMethodDecl>(
5151+
decl->getClangDecl())) {
5152+
isObjCDirect = objcDecl->isDirectMethod();
5153+
}
5154+
51375155
// Dispatch in a struct/enum or to a final method is always direct.
5138-
if (!isClassDispatch)
5156+
if (!isClassDispatch || isObjCDirect)
51395157
return Callee::forDirect(SGF, constant, subs, loc);
51405158

51415159
// Otherwise, if we have a non-final class dispatch to a normal method,

lib/Sema/LookupVisibleDecls.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "TypeChecker.h"
19+
#include "clang/AST/DeclObjC.h"
1920
#include "swift/AST/ASTContext.h"
2021
#include "swift/AST/GenericSignature.h"
2122
#include "swift/AST/GenericSignatureBuilder.h"
@@ -314,6 +315,17 @@ static void doDynamicLookup(VisibleDeclConsumer &Consumer,
314315
if (!D->isObjC())
315316
return;
316317

318+
// If the declaration is objc_direct, it cannot be called dynamically.
319+
if (auto clangDecl = D->getClangDecl()) {
320+
if (auto objCMethod = dyn_cast<clang::ObjCMethodDecl>(clangDecl)) {
321+
if (objCMethod->isDirectMethod())
322+
return;
323+
} else if (auto objCProperty = dyn_cast<clang::ObjCPropertyDecl>(clangDecl)) {
324+
if (objCProperty->isDirectProperty())
325+
return;
326+
}
327+
}
328+
317329
if (D->isRecursiveValidation())
318330
return;
319331

test/ClangImporter/Inputs/objc_direct.h

Lines changed: 0 additions & 12 deletions
This file was deleted.

test/ClangImporter/objc_direct.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
1-
// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/Inputs/objc_direct.h -verify-ignore-unknown
1+
// RUN: %target-swift-frontend-verify -typecheck %s -enable-objc-interop -import-objc-header %S/../Inputs/objc_direct.h
22

33
// REQUIRES: objc_interop
44

5-
func callThingsOnBar(_ x: Bar) {
6-
let _ = x.directProperty // expected-error {{getter for 'directProperty' is unavailable in Swift}}
7-
let _ = x.directProperty2 // expected-error {{getter for 'directProperty2' is unavailable in Swift}}
5+
var something = Bar() as AnyObject
86

9-
x.directMethod() // expected-error {{'directMethod()' is unavailable in Swift}}
10-
x.directMethod2() // expected-error {{'directMethod2()' is unavailable in Swift}}
7+
something.directProperty = 123 // expected-error {{value of type 'AnyObject' has no member 'directProperty'}}
8+
let _ = something.directProperty // expected-error {{value of type 'AnyObject' has no member 'directProperty'}}
119

12-
Bar.directClassMethod() // expected-error {{'directClassMethod()' is unavailable in Swift}}
13-
Bar.directClassMethod2() // expected-error {{'directClassMethod2()' is unavailable in Swift}}
10+
something.directProperty2 = 456 // expected-error {{value of type 'AnyObject' has no member 'directProperty2'}}
11+
let _ = something.directProperty2 // expected-error {{value of type 'AnyObject' has no member 'directProperty2'}}
12+
13+
let _ = something.directMethod() // expected-error {{value of type 'AnyObject' has no member 'directMethod'}}
14+
15+
let _ = something.directMethod2() // expected-error {{value of type 'AnyObject' has no member 'directMethod2'}}
16+
17+
class Foo {
18+
// Test that we can use a method with the same name as some `objc_direct` method.
19+
@objc func directProtocolMethod() -> String {
20+
"This is not a direct method."
21+
}
1422
}
23+
24+
var otherthing = Foo() as AnyObject
25+
26+
// We expect no error.
27+
let _ = otherthing.directProtocolMethod()

test/IRGen/objc_direct.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %target-swift-emit-ir -import-objc-header %S/../Inputs/objc_direct.h -o - %s | %FileCheck %s
2+
3+
// REQUIRES: objc_interop
4+
5+
func markUsed<T>(_ t: T) {}
6+
7+
protocol BarProtocol {
8+
func directProtocolMethod() -> String!
9+
}
10+
11+
extension Bar: BarProtocol {}
12+
13+
let bar = Bar()
14+
15+
bar.directProperty = 123
16+
// CHECK: call void @"\01-[Bar setDirectProperty:]"({{.*}}, i8* undef, i32 {{.*}})
17+
18+
markUsed(bar.directProperty)
19+
// CHECK: call i32 @"\01-[Bar directProperty]"({{.*}}, i8* undef)
20+
21+
bar.directProperty2 = 456
22+
// CHECK: call void @"\01-[Bar setDirectProperty2:]"({{.*}}, i8* undef, i32 {{.*}})
23+
24+
markUsed(bar.directProperty2)
25+
// CHECK: call i32 @"\01-[Bar directProperty2]"({{.*}}, i8* undef)
26+
27+
bar[0] = 789
28+
// CHECK: call void @"\01-[Bar setObject:atIndexedSubscript:]"({{.*}}, i8* undef, i32 789, i32 0)
29+
30+
markUsed(bar[0])
31+
// CHECK: call i32 @"\01-[Bar objectAtIndexedSubscript:]"({{.*}}, i8* undef, i32 0)
32+
33+
markUsed(bar.directMethod())
34+
// CHECK: call {{.*}} @"\01-[Bar directMethod]"({{.*}}, i8* undef)
35+
36+
markUsed(bar.directMethod2())
37+
// CHECK: call {{.*}} @"\01-[Bar directMethod2]"({{.*}}, i8* undef)
38+
39+
markUsed(Bar.directClassMethod())
40+
// NOTE: The class must be realized before calling objc_direct class methods, even if
41+
// Swift avoids explicit class realization before calling regular class methods.
42+
// CHECK: [[R0:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Bar"
43+
// CHECK: [[R1:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[R0]])
44+
// CHECK: [[R2:%.*]] = bitcast %objc_class* [[R1]] to i8*
45+
// CHECK: call {{.*}} @"\01+[Bar directClassMethod]"(i8* [[R2]], i8* undef)
46+
47+
markUsed(Bar.directClassMethod2())
48+
// CHECK: [[R3:%.*]] = load %objc_class*, %objc_class** @"OBJC_CLASS_REF_$_Bar"
49+
// CHECK: [[R4:%.*]] = call %objc_class* @swift_getInitializedObjCClass(%objc_class* [[R3]])
50+
// CHECK: [[R5:%.*]] = bitcast %objc_class* [[R4]] to i8*
51+
// CHECK: call {{.*}} @"\01+[Bar directClassMethod2]"(i8* [[R5]], i8* undef)
52+
53+
markUsed(bar.directProtocolMethod())
54+
// CHECK: call {{.*}} @"\01-[Bar directProtocolMethod]"({{.*}}, i8* undef)
55+
56+
// CHECK-DAG: declare i32 @"\01-[Bar directProperty]"
57+
// CHECK-DAG: declare void @"\01-[Bar setDirectProperty:]"
58+
// CHECK-DAG: declare i32 @"\01-[Bar directProperty2]"
59+
// CHECK-DAG: declare void @"\01-[Bar setDirectProperty2:]"
60+
// CHECK-DAG: declare void @"\01-[Bar setObject:atIndexedSubscript:]"
61+
// CHECK-DAG: declare i32 @"\01-[Bar objectAtIndexedSubscript:]"
62+
// CHECK-DAG: declare {{.*}} @"\01-[Bar directMethod]"
63+
// CHECK-DAG: declare {{.*}} @"\01-[Bar directMethod2]"
64+
// CHECK-DAG: declare {{.*}} @"\01+[Bar directClassMethod]"
65+
// CHECK-DAG: declare {{.*}} @"\01+[Bar directClassMethod2]"
66+
// CHECK-DAG: declare {{.*}} @"\01-[Bar directProtocolMethod]"

test/Inputs/objc_direct.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface Bar : NSObject
4+
@property(direct) int directProperty;
5+
- (int)objectAtIndexedSubscript:(int)i __attribute__((objc_direct));
6+
- (void)setObject:(int)obj atIndexedSubscript:(int)i __attribute__((objc_direct));
7+
- (NSString *)directMethod __attribute__((objc_direct));
8+
+ (NSString *)directClassMethod __attribute__((objc_direct));
9+
- (NSString *)directProtocolMethod __attribute__((objc_direct));
10+
@end
11+
12+
__attribute__((objc_direct_members))
13+
@interface Bar(CategoryName)
14+
@property int directProperty2;
15+
- (NSString *)directMethod2;
16+
+ (NSString *)directClassMethod2;
17+
@end

0 commit comments

Comments
 (0)