Skip to content

Commit eedf0fc

Browse files
committed
Clang importer: teach importFullName to map init methods to initializers.
We're only using this in the Swift lookup tables at the moment.
1 parent 9ee502b commit eedf0fc

File tree

5 files changed

+77
-42
lines changed

5 files changed

+77
-42
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,8 +1655,10 @@ DeclName ClangImporter::Implementation::importFullName(
16551655

16561656
/// Whether the result is a function name.
16571657
bool isFunction = false;
1658+
bool isInitializer = false;
16581659
StringRef baseName;
16591660
SmallVector<StringRef, 4> argumentNames;
1661+
SmallString<16> selectorSplitScratch;
16601662
switch (D->getDeclName().getNameKind()) {
16611663
case clang::DeclarationName::CXXConstructorName:
16621664
case clang::DeclarationName::CXXConversionFunctionName:
@@ -1675,15 +1677,55 @@ DeclName ClangImporter::Implementation::importFullName(
16751677
case clang::DeclarationName::ObjCMultiArgSelector:
16761678
case clang::DeclarationName::ObjCOneArgSelector:
16771679
case clang::DeclarationName::ObjCZeroArgSelector: {
1680+
auto objcMethod = cast<clang::ObjCMethodDecl>(D);
1681+
isInitializer = isInitMethod(objcMethod);
1682+
16781683
// Map the Objective-C selector directly.
16791684
auto selector = D->getDeclName().getObjCSelector();
1680-
baseName = selector.getNameForSlot(0);
1685+
if (isInitializer)
1686+
baseName = "init";
1687+
else
1688+
baseName = selector.getNameForSlot(0);
16811689
for (unsigned index = 0, numArgs = selector.getNumArgs(); index != numArgs;
16821690
++index) {
1683-
if (index == 0)
1684-
argumentNames.push_back("");
1685-
else
1691+
if (index == 0) {
1692+
argumentNames.push_back(StringRef());
1693+
} else {
16861694
argumentNames.push_back(selector.getNameForSlot(index));
1695+
}
1696+
}
1697+
1698+
// For initializers, compute the first argument name.
1699+
if (isInitializer) {
1700+
// Skip over the 'init'.
1701+
auto argName = selector.getNameForSlot(0).substr(4);
1702+
1703+
// Drop "With" if present after the "init".
1704+
bool droppedWith = false;
1705+
if (argName.startswith("With")) {
1706+
argName = argName.substr(4);
1707+
droppedWith = true;
1708+
}
1709+
1710+
// Lowercase the remaining argument name.
1711+
argName = camel_case::toLowercaseWord(argName, selectorSplitScratch);
1712+
1713+
// If we dropped "with" and ended up with a reserved name,
1714+
// put "with" back.
1715+
if (droppedWith && isSwiftReservedName(argName)) {
1716+
selectorSplitScratch = "with";
1717+
selectorSplitScratch += selector.getNameForSlot(0).substr(8);
1718+
argName = selectorSplitScratch;
1719+
}
1720+
1721+
// Set the first argument name to be the name we computed. If
1722+
// there is no first argument, create one for this purpose.
1723+
if (argumentNames.empty() && !argName.empty()) {
1724+
// FIXME: Record what happened here for the caller?
1725+
argumentNames.push_back(argName);
1726+
} else {
1727+
argumentNames[0] = argName;
1728+
}
16871729
}
16881730

16891731
isFunction = true;
@@ -1825,17 +1867,15 @@ DeclName ClangImporter::Implementation::importFullName(
18251867
if (hasSwiftPrivate(D)) {
18261868
swiftPrivateScratch = "__";
18271869

1828-
if (baseName == "init") {
1870+
if (isInitializer) {
18291871
// For initializers, prepend "__" to the first argument name.
18301872
if (argumentNames.empty()) {
1831-
// swift_private cannot actually do anything here. Just drop the
1832-
// declaration.
1833-
// FIXME: Diagnose this?
1834-
return { };
1873+
// FIXME: Record that we did this.
1874+
argumentNames.push_back("__");
1875+
} else {
1876+
swiftPrivateScratch += argumentNames[0];
1877+
argumentNames[0] = swiftPrivateScratch;
18351878
}
1836-
1837-
swiftPrivateScratch += argumentNames[0];
1838-
argumentNames[0] = swiftPrivateScratch;
18391879
} else {
18401880
// For all other entities, prepend "__" to the base name.
18411881
swiftPrivateScratch += baseName;
@@ -2572,6 +2612,19 @@ ClangImporter::Implementation::isAccessibilityDecl(const clang::Decl *decl) {
25722612
return false;
25732613
}
25742614

2615+
bool ClangImporter::Implementation::isInitMethod(
2616+
const clang::ObjCMethodDecl *method) {
2617+
// init methods are always instance methods.
2618+
if (!method->isInstanceMethod()) return false;
2619+
2620+
// init methods must be classified as such by Clang.
2621+
if (method->getMethodFamily() != clang::OMF_init) return false;
2622+
2623+
// Swift restriction: init methods must start with the word "init".
2624+
auto selector = method->getSelector();
2625+
return camel_case::getFirstWord(selector.getNameForSlot(0)) == "init";
2626+
}
2627+
25752628
#pragma mark Name lookup
25762629
void ClangImporter::lookupValue(Identifier name, VisibleDeclConsumer &consumer){
25772630
auto &pp = Impl.Instance->getPreprocessor();

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2879,8 +2879,7 @@ namespace {
28792879
DeclContext *dc,
28802880
bool forceClassMethod = false) {
28812881
// If we have an init method, import it as an initializer.
2882-
if (decl->getMethodFamily() == clang::OMF_init &&
2883-
isReallyInitMethod(decl)) {
2882+
if (Impl.isInitMethod(decl)) {
28842883
// Cannot force initializers into class methods.
28852884
if (forceClassMethod)
28862885
return nullptr;
@@ -3069,27 +3068,6 @@ namespace {
30693068
return result;
30703069
}
30713070

3072-
private:
3073-
/// Check whether the given name starts with the given word.
3074-
static bool startsWithWord(StringRef name, StringRef word) {
3075-
if (name.size() < word.size()) return false;
3076-
return ((name.size() == word.size() || !islower(name[word.size()])) &&
3077-
name.startswith(word));
3078-
}
3079-
3080-
/// Determine whether the given Objective-C method, which Clang classifies
3081-
/// as an init method, is considered an init method in Swift.
3082-
static bool isReallyInitMethod(const clang::ObjCMethodDecl *method) {
3083-
if (!method->isInstanceMethod())
3084-
return false;
3085-
3086-
auto selector = method->getSelector();
3087-
auto first = selector.getIdentifierInfoForSlot(0);
3088-
if (!first) return false;
3089-
3090-
return startsWithWord(first->getName(), "init");
3091-
}
3092-
30933071
public:
30943072
/// \brief Given an imported method, try to import it as some kind of
30953073
/// special declaration, e.g., a constructor or subscript.
@@ -3241,9 +3219,7 @@ namespace {
32413219
Optional<CtorInitializerKind> kind,
32423220
bool required) {
32433221
// Only methods in the 'init' family can become constructors.
3244-
assert(objcMethod->getMethodFamily() == clang::OMF_init &&
3245-
"Not an init method");
3246-
assert(isReallyInitMethod(objcMethod) && "Not a real init method");
3222+
assert(Impl.isInitMethod(objcMethod) && "Not a real init method");
32473223

32483224
// Check whether we've already created the constructor.
32493225
auto known = Impl.Constructors.find({objcMethod, dc});
@@ -4552,8 +4528,7 @@ namespace {
45524528
continue;
45534529

45544530
// When mirroring an initializer, make it designated and required.
4555-
if (objcMethod->getMethodFamily() == clang::OMF_init &&
4556-
isReallyInitMethod(objcMethod)) {
4531+
if (Impl.isInitMethod(objcMethod)) {
45574532
// Import the constructor.
45584533
if (auto imported = importConstructor(
45594534
objcMethod, dc, /*implicit=*/true,

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
426426
/// imported as methods into Swift.
427427
bool isAccessibilityDecl(const clang::Decl *objCMethodOrProp);
428428

429+
/// Determine whether this method is an Objective-C "init" method
430+
/// that will be imported as a Swift initializer.
431+
bool isInitMethod(const clang::ObjCMethodDecl *method);
432+
429433
private:
430434
/// \brief Generation number that is used for crude versioning.
431435
///

test/IDE/Inputs/swift_name_objc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
SWIFT_NAME(SomeClass)
1717
@interface SNSomeClass : NSObject
1818
- (instancetype)initWithFloat:(float)f;
19+
- (instancetype)initWithDefault;
1920
- (void)instanceMethodWithX:(float)x y:(float)y z:(float)z;
2021
+ (instancetype)someClassWithDouble:(double)d;
2122

test/IDE/dump_swift_lookup_tables_objc.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// CHECK-NEXT: doubleProperty --> doubleProperty{{$}}
1515
// CHECK-NEXT: extensionMethodWithX --> extensionMethodWithX(_:y:)
1616
// CHECK-NEXT: floatProperty --> floatProperty{{$}}
17-
// CHECK-NEXT: initWithFloat --> initWithFloat(_:)
17+
// CHECK-NEXT: init --> init(float:), init(withDefault:)
1818
// CHECK-NEXT: instanceMethodWithX --> instanceMethodWithX(_:y:z:)
1919
// CHECK-NEXT: protoInstanceMethodWithX --> protoInstanceMethodWithX(_:y:)
2020
// CHECK-NEXT: setAccessibilityFloat --> setAccessibilityFloat(_:)
@@ -43,8 +43,10 @@
4343
// CHECK-NEXT: SNSomeClass: -[SNSomeClass extensionMethodWithX:y:]
4444
// CHECK-NEXT: floatProperty:
4545
// CHECK-NEXT: SNSomeClass: SNSomeClass.floatProperty
46-
// CHECK-NEXT: initWithFloat(_:):
46+
// CHECK-NEXT: init(float:):
4747
// CHECK-NEXT: SNSomeClass: -[SNSomeClass initWithFloat:]
48+
// CHECK-NEXT: init(withDefault:):
49+
// CHECK-NEXT: SNSomeClass: -[SNSomeClass initWithDefault]
4850
// CHECK-NEXT: instanceMethodWithX(_:y:z:):
4951
// CHECK-NEXT: SNSomeClass: -[SNSomeClass instanceMethodWithX:y:z:]
5052
// CHECK-NEXT: protoInstanceMethodWithX(_:y:):

0 commit comments

Comments
 (0)