Only generate domains for PrintAsObjC-able types

Otherwise we generate a call to String(reflecting:), which correctly handles many things we may not be able to (like private types), and which matches the default implementation of Error._domain.
This commit is contained in:
Brent Royal-Gordon
2018-12-21 15:45:21 -08:00
parent 1a6ca82d6b
commit 2eed5374db
4 changed files with 68 additions and 6 deletions

View File

@@ -47,6 +47,7 @@ public:
bool isPublic() const { return !Value.getPointer(); }
bool isPrivate() const { return Value.getInt(); }
bool isFileScope() const;
bool isInternal() const;
/// Returns true if this is a child scope of the specified other access scope.
///

View File

@@ -839,6 +839,11 @@ bool AccessScope::isFileScope() const {
return DC && isa<FileUnit>(DC);
}
bool AccessScope::isInternal() const {
auto DC = getDeclContext();
return DC && isa<ModuleDecl>(DC);
}
AccessLevel AccessScope::accessLevelForDiagnostics() const {
if (isPublic())
return AccessLevel::Public;

View File

@@ -32,6 +32,33 @@ static void deriveBodyBridgedNSError_enum_nsErrorDomain(
// enum SomeEnum {
// @derived
// static var _nsErrorDomain: String {
// return _typeName(self, qualified: true)
// }
// }
auto M = domainDecl->getParentModule();
auto &C = M->getASTContext();
auto self = domainDecl->getImplicitSelfDecl();
auto selfRef = new (C) DeclRefExpr(self, DeclNameLoc(), /*implicit*/ true);
auto stringType = TypeExpr::createForDecl(SourceLoc(), C.getStringDecl(),
domainDecl, /*implicit*/ true);
auto initReflectingCall =
CallExpr::createImplicit(C, stringType,
{ selfRef }, { C.getIdentifier("reflecting") });
auto ret =
new (C) ReturnStmt(SourceLoc(), initReflectingCall, /*implicit*/ true);
auto body = BraceStmt::create(C, SourceLoc(), ASTNode(ret), SourceLoc());
domainDecl->setBody(body);
}
static void deriveBodyBridgedNSError_printAsObjCEnum_nsErrorDomain(
AbstractFunctionDecl *domainDecl, void *) {
// enum SomeEnum {
// @derived
// static var _nsErrorDomain: String {
// return "ModuleName.SomeEnum"
// }
// }
@@ -52,11 +79,12 @@ static void deriveBodyBridgedNSError_enum_nsErrorDomain(
}
static ValueDecl *
deriveBridgedNSError_enum_nsErrorDomain(DerivedConformance &derived) {
deriveBridgedNSError_enum_nsErrorDomain(DerivedConformance &derived,
void (*synthesizer)(AbstractFunctionDecl *, void*)) {
// enum SomeEnum {
// @derived
// static var _nsErrorDomain: String {
// return "ModuleName.SomeEnum"
// ...
// }
// }
@@ -74,7 +102,7 @@ deriveBridgedNSError_enum_nsErrorDomain(DerivedConformance &derived) {
// Define the getter.
auto getterDecl = derived.addGetterToReadOnlyDerivedProperty(
derived.TC, propDecl, stringTy);
getterDecl->setBodySynthesizer(&deriveBodyBridgedNSError_enum_nsErrorDomain);
getterDecl->setBodySynthesizer(synthesizer);
derived.addMembersToConformanceContext({getterDecl, propDecl, pbDecl});
@@ -85,8 +113,17 @@ ValueDecl *DerivedConformance::deriveBridgedNSError(ValueDecl *requirement) {
if (!isa<EnumDecl>(Nominal))
return nullptr;
if (requirement->getBaseName() == TC.Context.Id_nsErrorDomain)
return deriveBridgedNSError_enum_nsErrorDomain(*this);
if (requirement->getBaseName() == TC.Context.Id_nsErrorDomain) {
auto synthesizer = deriveBodyBridgedNSError_enum_nsErrorDomain;
auto scope = Nominal->getFormalAccessScope(Nominal->getModuleScopeContext());
if (scope.isPublic() || scope.isInternal())
// PrintAsObjC may print this domain, so we should make sure we use the
// same string it will.
synthesizer = deriveBodyBridgedNSError_printAsObjCEnum_nsErrorDomain;
return deriveBridgedNSError_enum_nsErrorDomain(*this, synthesizer);
}
TC.diagnose(requirement->getLoc(), diag::broken_errortype_requirement);
return nullptr;

View File

@@ -722,4 +722,23 @@ ErrorBridgingTests.test("Error archetype identity") {
=== nsError)
}
private class NonPrintAsObjCClass: NSObject {
@objc enum Error: Int, Swift.Error {
case foo
}
}
@objc private enum NonPrintAsObjCError: Int, Error {
case bar
}
ErrorBridgingTests.test("@objc enum error domains") {
// If an @objc enum error is not eligible for PrintAsObjC, we should treat it
// as though it inherited the default implementation, which calls
// String(reflecting:).
expectEqual(NonPrintAsObjCClass.Error.foo._domain,
String(reflecting: NonPrintAsObjCClass.Error.self))
expectEqual(NonPrintAsObjCError.bar._domain,
String(reflecting: NonPrintAsObjCError.self))
}
runAllTests()