Skip to content

Commit 7ea8546

Browse files
committed
[ClangImporter] Fall back to Swift class names when resolving @Class
Christopher Rogers' (good) work in 49fd5ac caught places where the Swift compiler was allowing a @Class to resolve to a Swift class even if that class had a conflicting Objective-C name, or wasn't intended to be exposed to Objective-C at all. Unfortunately, this broke source compatibility in projects where people were relying on this. Restore that functionality, but only as a fallback; matching the Objective-C name is better than matching the Swift name. rdar://problem/56681046
1 parent 4d9e5bf commit 7ea8546

File tree

7 files changed

+96
-25
lines changed

7 files changed

+96
-25
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3729,6 +3729,28 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
37293729
/// FIXME: Other type declarations should also be okay?
37303730
}
37313731
}
3732+
3733+
// For source compatibility reasons, fall back to the Swift name.
3734+
//
3735+
// This is how people worked around not being able to import-as-member onto
3736+
// Swift types by their ObjC name before the above code to handle ObjCAttr
3737+
// was added.
3738+
if (name != nominal->getName())
3739+
clangName = exportName(nominal->getName());
3740+
3741+
lookupResult.clear();
3742+
lookupResult.setLookupName(clangName);
3743+
// FIXME: This loop is duplicated from above, but doesn't obviously factor
3744+
// out in a nice way.
3745+
if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) {
3746+
// FIXME: Filter based on access path? C++ access control?
3747+
for (auto clangDecl : lookupResult) {
3748+
if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl))
3749+
return EffectiveClangContext(objcClass);
3750+
3751+
/// FIXME: Other type declarations should also be okay?
3752+
}
3753+
}
37323754
}
37333755

37343756
return EffectiveClangContext();

lib/ClangImporter/ImportDecl.cpp

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4537,54 +4537,77 @@ namespace {
45374537
const auto &languageVersion =
45384538
Impl.SwiftContext.LangOpts.EffectiveLanguageVersion;
45394539

4540-
auto isMatch = [&](const T *singleResult, bool baseNameMatches) -> bool {
4540+
auto isMatch = [&](const T *singleResult, bool baseNameMatches,
4541+
bool allowObjCMismatch) -> bool {
45414542
const DeclAttributes &attrs = singleResult->getAttrs();
45424543

45434544
// Skip versioned variants.
45444545
if (attrs.isUnavailableInSwiftVersion(languageVersion))
45454546
return false;
45464547

4547-
// Skip if type not exposed to Objective-C.
4548-
// If the base name doesn't match, then a matching
4549-
// custom name in an @objc attribute is required.
4550-
if (baseNameMatches && !singleResult->isObjC())
4551-
return false;
4552-
4553-
// If Clang decl has a custom Swift name, then we know that
4554-
// `name` is the base name we're looking for.
4555-
if (hasKnownSwiftName)
4556-
return baseNameMatches;
4548+
// If Clang decl has a custom Swift name, then we know that the name we
4549+
// did direct lookup for is correct.
4550+
// 'allowObjCMismatch' shouldn't exist, but we need it for source
4551+
// compatibility where a previous version of the compiler didn't check
4552+
// @objc-ness at all.
4553+
if (hasKnownSwiftName || allowObjCMismatch) {
4554+
assert(baseNameMatches);
4555+
return allowObjCMismatch || singleResult->isObjC();
4556+
}
45574557

45584558
// Skip if a different name is used for Objective-C.
45594559
if (auto objcAttr = attrs.getAttribute<ObjCAttr>())
45604560
if (auto objcName = objcAttr->getName())
45614561
return objcName->getSimpleName() == name;
45624562

4563-
return baseNameMatches;
4563+
return baseNameMatches && singleResult->isObjC();
45644564
};
45654565

45664566
// First look at Swift types with the same name.
4567-
SmallVector<ValueDecl *, 4> results;
4568-
overlay->lookupValue(name, NLKind::QualifiedLookup, results);
4567+
SmallVector<ValueDecl *, 4> swiftDeclsByName;
4568+
overlay->lookupValue(name, NLKind::QualifiedLookup, swiftDeclsByName);
45694569
T *found = nullptr;
4570-
for (auto result : results) {
4570+
for (auto result : swiftDeclsByName) {
45714571
if (auto singleResult = dyn_cast<T>(result)) {
4572-
if (isMatch(singleResult, /*baseNameMatches=*/true)) {
4572+
if (isMatch(singleResult, /*baseNameMatches=*/true,
4573+
/*allowObjCMismatch=*/false)) {
45734574
if (found)
45744575
return nullptr;
45754576
found = singleResult;
45764577
}
45774578
}
45784579
}
45794580

4580-
if (!found && !hasKnownSwiftName) {
4581+
if (!found && hasKnownSwiftName)
4582+
return nullptr;
4583+
4584+
if (!found) {
45814585
// Try harder to find a match looking at just custom Objective-C names.
4582-
SmallVector<Decl *, 64> results;
4583-
overlay->getTopLevelDecls(results);
4584-
for (auto result : results) {
4586+
SmallVector<Decl *, 64> allTopLevelDecls;
4587+
overlay->getTopLevelDecls(allTopLevelDecls);
4588+
for (auto result : allTopLevelDecls) {
45854589
if (auto singleResult = dyn_cast<T>(result)) {
45864590
// The base name _could_ match but it's irrelevant here.
4587-
if (isMatch(singleResult, /*baseNameMatches=*/false)) {
4591+
if (isMatch(singleResult, /*baseNameMatches=*/false,
4592+
/*allowObjCMismatch=*/false)) {
4593+
if (found)
4594+
return nullptr;
4595+
found = singleResult;
4596+
}
4597+
}
4598+
}
4599+
}
4600+
4601+
if (!found) {
4602+
// Go back to the first list and find classes with matching Swift names
4603+
// *even if the ObjC name doesn't match.*
4604+
// This shouldn't be allowed but we need it for source compatibility;
4605+
// people used `@class SwiftNameOfClass` as a workaround for not
4606+
// having the previous loop, and it "worked".
4607+
for (auto result : swiftDeclsByName) {
4608+
if (auto singleResult = dyn_cast<T>(result)) {
4609+
if (isMatch(singleResult, /*baseNameMatches=*/true,
4610+
/*allowObjCMismatch=*/true)) {
45884611
if (found)
45894612
return nullptr;
45904613
found = singleResult;
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
@class Outer;
2-
3-
struct Nested {
4-
int value;
2+
struct NestedInOuter {
3+
int a;
54
} __attribute((swift_name("Outer.Nested")));
5+
6+
@class OuterByObjCName_ObjC;
7+
struct NestedInOuterByObjCName {
8+
int b;
9+
} __attribute((swift_name("OuterByObjCName_ObjC.Nested")));
10+
11+
@class OuterBySwiftName_Swift;
12+
struct NestedInOuterBySwiftName {
13+
int c;
14+
} __attribute((swift_name("OuterBySwiftName_Swift.Nested")));

test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.framework/Headers/Mixed.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ void consumeObjCForwardNativeTypeHasDifferentCustomNameClass(ObjCForwardNativeTy
124124
@class ForwardNativeTypeIsNonObjCClass;
125125
void consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass *_Nonnull obj);
126126

127+
@class ForwardNativeTypeIsUnambiguouslyNonObjCClass;
128+
void consumeForwardNativeTypeIsUnambiguouslyNonObjCClass(ForwardNativeTypeIsUnambiguouslyNonObjCClass *_Nonnull obj);
129+
127130
SWIFT_CLASS("BOGUS")
128131
@interface BogusClass
129132
@end

test/ClangImporter/MixedSource/Inputs/mixed-framework/Mixed.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,7 @@ public class ForwardNativeTypeIsNonObjCClass {
9898
public class SwiftForwardNativeTypeIsNonObjCClass {
9999
public init() {}
100100
}
101+
102+
public class ForwardNativeTypeIsUnambiguouslyNonObjCClass {
103+
public init() {}
104+
}

test/ClangImporter/MixedSource/import-as-member-swift.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@
22

33
@objc internal class Outer {}
44

5-
_ = Outer.Nested()
5+
@objc(OuterByObjCName_ObjC)
6+
internal class OuterByObjCName_Swift {}
7+
8+
@objc(OuterBySwiftName_ObjC)
9+
internal class OuterBySwiftName_Swift {}
10+
11+
_ = Outer.Nested(a: 1)
12+
_ = OuterByObjCName_Swift.Nested(b: 2)
13+
_ = OuterBySwiftName_Swift.Nested(c: 3)

test/ClangImporter/MixedSource/import-mixed-framework.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,5 @@ consumeObjCForwardNativeTypeHasDifferentCustomNameClass(SwiftForwardNativeTypeHa
6060

6161
consumeForwardNativeTypeIsNonObjCClass(SwiftForwardNativeTypeIsNonObjCClass())
6262
consumeForwardNativeTypeIsNonObjCClass(ForwardNativeTypeIsNonObjCClass()) // expected-error {{cannot convert value of type 'ForwardNativeTypeIsNonObjCClass' to expected argument type 'SwiftForwardNativeTypeIsNonObjCClass'}}
63+
64+
consumeForwardNativeTypeIsUnambiguouslyNonObjCClass(ForwardNativeTypeIsUnambiguouslyNonObjCClass())

0 commit comments

Comments
 (0)