Skip to content

Commit 1875783

Browse files
committed
Use more robust Swift type names for error domains
getErrorDomainStringForObjC() now includes the parent types in the error domain string. Its implementation does not support generic types or private discriminators, but those types can’t be PrintAsObjC’d anyway, so we should never see them.
1 parent 2eed537 commit 1875783

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

lib/AST/SwiftNameTranslation.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,29 @@ getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) {
5454

5555
std::string swift::objc_translation::
5656
getErrorDomainStringForObjC(const EnumDecl *ED) {
57+
// Should have already been diagnosed as diag::objc_enum_generic.
58+
assert(!ED->isGenericContext() && "Trying to bridge generic enum error to Obj-C");
59+
60+
// Clang decls have custom domains, but we shouldn't see them here anyway.
61+
assert(!ED->getClangDecl() && "clang decls shouldn't be re-exported");
62+
63+
SmallVector<const NominalTypeDecl *, 4> outerTypes;
64+
for (const NominalTypeDecl * D = ED;
65+
D != nullptr;
66+
D = D->getDeclContext()->getSelfNominalTypeDecl()) {
67+
// We don't currently PrintAsObjC any types whose parents are private or
68+
// fileprivate.
69+
assert(D->getFormalAccess() >= AccessLevel::Internal &&
70+
"We don't currently append private discriminators");
71+
outerTypes.push_back(D);
72+
}
73+
5774
std::string buffer = ED->getParentModule()->getNameStr();
58-
buffer += ".";
59-
buffer += ED->getNameStr();
75+
for (auto D : reversed(outerTypes)) {
76+
buffer += ".";
77+
buffer += D->getNameStr();
78+
}
79+
6080
return buffer;
6181
}
6282

test/stdlib/ErrorBridged.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,17 @@ ErrorBridgingTests.test("Error archetype identity") {
722722
=== nsError)
723723
}
724724

725+
// SR-9389
726+
class ParentA: NSObject {
727+
@objc(ParentAError) enum Error: Int, Swift.Error {
728+
case failed
729+
}
730+
}
731+
class ParentB: NSObject {
732+
@objc(ParentBError) enum Error: Int, Swift.Error {
733+
case failed
734+
}
735+
}
725736
private class NonPrintAsObjCClass: NSObject {
726737
@objc enum Error: Int, Swift.Error {
727738
case foo
@@ -731,7 +742,22 @@ private class NonPrintAsObjCClass: NSObject {
731742
case bar
732743
}
733744

734-
ErrorBridgingTests.test("@objc enum error domains") {
745+
ErrorBridgingTests.test("@objc error domains for nested types") {
746+
// Domain strings should correspond to Swift types, including parent types.
747+
expectEqual(ParentA.Error.failed._domain, "main.ParentA.Error")
748+
expectEqual(ParentB.Error.failed._domain, "main.ParentB.Error")
749+
750+
func makeNSError(like error: Error) -> NSError {
751+
return NSError(domain: error._domain, code: error._code)
752+
}
753+
754+
// NSErrors corresponding to Error types with the same name but nested in
755+
// different enclosing types should not be castable to the wrong error type.
756+
expectTrue(makeNSError(like: ParentA.Error.failed) is ParentA.Error)
757+
expectFalse(makeNSError(like: ParentA.Error.failed) is ParentB.Error)
758+
expectFalse(makeNSError(like: ParentB.Error.failed) is ParentA.Error)
759+
expectTrue(makeNSError(like: ParentB.Error.failed) is ParentB.Error)
760+
735761
// If an @objc enum error is not eligible for PrintAsObjC, we should treat it
736762
// as though it inherited the default implementation, which calls
737763
// String(reflecting:).

0 commit comments

Comments
 (0)