Skip to content

Commit

Permalink
Make @objc enums that conform to ErrorType also conform to _Objective…
Browse files Browse the repository at this point in the history
…CBridgeableErrorType.

This allows @objc enum error types produced in Objective-C (e.g., via
+[NSError errorWithDomain:code:userInfo:]) to be bridged back to their
original enum types in Swift via pattern matching/catch blocks.

This finishes rdar://problem/20577517.

Swift SVN r28803
  • Loading branch information
DougGregor committed May 20, 2015
1 parent db53a22 commit 7405064
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 19 deletions.
1 change: 1 addition & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ IDENTIFIER(init)
IDENTIFIER(load)
IDENTIFIER(next)
IDENTIFIER(NSError)
IDENTIFIER(_NSErrorDomain)
IDENTIFIER(NSObject)
IDENTIFIER(objectAtIndexedSubscript)
IDENTIFIER(objectForKeyedSubscript)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ PROTOCOL(Hashable)
PROTOCOL(Comparable)
PROTOCOL(ErrorType)
PROTOCOL(_OptionSetType)
PROTOCOL(_BridgedNSError)

PROTOCOL(_ObjectiveCBridgeable)
PROTOCOL(_DestructorSafeContainer)
Expand Down
22 changes: 19 additions & 3 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -740,9 +740,23 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
if (Impl.KnownProtocols[index])
return Impl.KnownProtocols[index];

// Find all of the declarations with this name in the Swift module.
// Find all of the declarations with this name in the appropriate module.
SmallVector<ValueDecl *, 1> results;
lookupInSwiftModule(getProtocolName(kind), results);

// _BridgedNSError is in the Foundation module.
if (kind == KnownProtocolKind::_BridgedNSError) {
Module *foundation = const_cast<ASTContext *>(this)->getModuleByName(
FOUNDATION_MODULE_NAME);
if (!foundation)
return nullptr;

auto identifier = getIdentifier(getProtocolName(kind));
foundation->lookupValue({ }, identifier, NLKind::UnqualifiedLookup,
results);
} else {
lookupInSwiftModule(getProtocolName(kind), results);
}

for (auto result : results) {
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
Impl.KnownProtocols[index] = protocol;
Expand Down Expand Up @@ -1248,7 +1262,9 @@ ASTContext::getModule(ArrayRef<std::pair<Identifier, SourceLoc>> ModulePath) {
auto moduleID = ModulePath[0];
for (auto &importer : Impl.ModuleLoaders) {
if (Module *M = importer->loadModule(moduleID.second, ModulePath)) {
if (ModulePath.size() == 1 && ModulePath[0].first == StdlibModuleName)
if (ModulePath.size() == 1 &&
(ModulePath[0].first == StdlibModuleName ||
ModulePath[0].first == Id_Foundation))
recordKnownProtocols(M);
return M;
}
Expand Down
6 changes: 5 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1804,7 +1804,11 @@ bool NominalTypeDecl::derivesProtocolConformance(ProtocolDecl *protocol) const {
// Enums can explicitly derive their ErrorType conformance.
case KnownProtocolKind::ErrorType:
return true;


// @objc enums can explicitly derive their _BridgedNSError conformance.
case KnownProtocolKind::_BridgedNSError:
return isObjC();

default:
return false;
}
Expand Down
17 changes: 15 additions & 2 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,7 @@ bool ConformanceLookupTable::addProtocol(NominalTypeDecl *nominal,
// Record this as a conformance within the given declaration
// context.
AllConformances[dc].push_back(entry);

return true;
}

Expand Down Expand Up @@ -1422,7 +1423,20 @@ void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal,
// implied conformances.
if (resolver)
resolver->resolveInheritanceClause(dc);


// An @objc enum that explicitly conforms to the ErrorType protocol also
// implicitly conforms to _ObjectiveCBridgeableErrorType, via the known
// protocol _BridgedNSError.
if (conformingProtocol->isSpecificProtocol(KnownProtocolKind::ErrorType) &&
isa<EnumDecl>(nominal) && nominal->isObjC()) {
ASTContext &ctx = nominal->getASTContext();
if (auto bridgedNSError
= ctx.getProtocol(KnownProtocolKind::_BridgedNSError)) {
addProtocol(nominal, bridgedNSError, SourceLoc(),
ConformanceSource::forImplied(conformanceEntry));
}
}

// FIXME: It's odd that the inheritance clause isn't loaded at all during
// deserialization, so we have this weird separate path.
if (!conformingProtocol->getParentSourceFile()) {
Expand Down Expand Up @@ -2036,7 +2050,6 @@ void NominalTypeDecl::prepareConformanceTable() const {
if (resolver)
resolver->resolveRawType(theEnum);
if (theEnum->hasRawType()) {
// Simple enumerations conform to Equatable and Hashable.
if (auto rawRepresentable = getASTContext().getProtocol(
KnownProtocolKind::RawRepresentable)) {
ConformanceTable->addSynthesizedConformance(mutableThis,
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4538,6 +4538,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
case KnownProtocolKind::_BuiltinUTF16StringLiteralConvertible:
case KnownProtocolKind::_BuiltinUnicodeScalarLiteralConvertible:
case KnownProtocolKind::_OptionSetType:
case KnownProtocolKind::_BridgedNSError:
return SpecialProtocol::None;
}
}
Expand Down
78 changes: 78 additions & 0 deletions lib/Sema/DerivedConformanceErrorType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,81 @@ ValueDecl *DerivedConformance::deriveErrorType(TypeChecker &tc,
diag::broken_errortype_requirement);
return nullptr;
}

static void deriveBodyBridgedNSError_enum_NSErrorDomain(
AbstractFunctionDecl *domainDecl) {
// enum SomeEnum {
// @derived
// static var _NSErrorDomain: String {
// return "\(self)"
// }
// }

auto &C = domainDecl->getASTContext();

auto selfRef = createSelfDeclRef(domainDecl);
Expr *segmentElts[] = { selfRef };
auto segments = C.AllocateCopy(segmentElts);
auto string = new (C) InterpolatedStringLiteralExpr(SourceLoc(), segments);
string->setImplicit();

auto ret = new (C) ReturnStmt(SourceLoc(), string, /*implicit*/ true);
auto body = BraceStmt::create(C, SourceLoc(),
ASTNode(ret),
SourceLoc());
domainDecl->setBody(body);
}

static ValueDecl *deriveBridgedNSError_enum_NSErrorDomain(TypeChecker &tc,
EnumDecl *enumDecl) {
// enum SomeEnum {
// @derived
// static var _NSErrorDomain: String {
// return "\(self)"
// }
// }

// Note that for @objc enums the format is assumed to be "MyModule.SomeEnum".
// If this changes, please change PrintAsObjC as well.

ASTContext &C = tc.Context;

auto stringTy = C.getStringDecl()->getDeclaredType();
Type enumType = enumDecl->getDeclaredTypeInContext();

// Define the getter.
auto getterDecl = declareDerivedPropertyGetter(tc, enumDecl, enumType,
stringTy, stringTy,
/*isStatic=*/true);
getterDecl->setBodySynthesizer(&deriveBodyBridgedNSError_enum_NSErrorDomain);

// Define the property.
VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl)
= declareDerivedReadOnlyProperty(tc, enumDecl, C.Id__NSErrorDomain,
stringTy, stringTy,
getterDecl, /*isStatic=*/true);

enumDecl->addMember(getterDecl);
enumDecl->addMember(propDecl);
enumDecl->addMember(pbDecl);
return propDecl;
}

ValueDecl *DerivedConformance::deriveBridgedNSError(TypeChecker &tc,
NominalTypeDecl *type,
ValueDecl *requirement) {
if (!canDeriveConformance(type))
return nullptr;

auto enumType = cast<EnumDecl>(type);

if (requirement->getName() == tc.Context.Id__NSErrorDomain)
return deriveBridgedNSError_enum_NSErrorDomain(tc, enumType);

tc.diagnose(requirement->getLoc(),
diag::broken_errortype_requirement);
return nullptr;
}

27 changes: 17 additions & 10 deletions lib/Sema/DerivedConformanceRawRepresentable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,22 +154,27 @@ FuncDecl *DerivedConformance::declareDerivedPropertyGetter(TypeChecker &tc,
NominalTypeDecl *typeDecl,
Type contextType,
Type propertyInterfaceType,
Type propertyContextType) {
Type propertyContextType,
bool isStatic) {
auto &C = tc.Context;


Type selfType = contextType;
if (isStatic)
selfType = MetatypeType::get(selfType);

VarDecl *selfDecl = new (C) ParamDecl(/*IsLet*/true,
SourceLoc(),
Identifier(),
SourceLoc(),
C.Id_self,
contextType,
selfType,
typeDecl);
selfDecl->setImplicit();
Pattern *selfParam = new (C) NamedPattern(selfDecl, /*implicit*/ true);
selfParam->setType(contextType);
selfParam->setType(selfType);
selfParam = new (C) TypedPattern(selfParam,
TypeLoc::withoutLoc(contextType));
selfParam->setType(contextType);
TypeLoc::withoutLoc(selfType));
selfParam->setType(selfType);
Pattern *methodParam = TuplePattern::create(C, SourceLoc(),{},SourceLoc());
methodParam->setType(TupleType::getEmpty(C));
Pattern *params[] = {selfParam, methodParam};
Expand All @@ -180,12 +185,13 @@ FuncDecl *DerivedConformance::declareDerivedPropertyGetter(TypeChecker &tc,
params, TypeLoc::withoutLoc(propertyContextType),
typeDecl);
getterDecl->setImplicit();

getterDecl->setStatic(isStatic);

// Compute the type of the getter.
GenericParamList *genericParams = nullptr;
Type type = FunctionType::get(TupleType::getEmpty(C),
propertyContextType);
Type selfType = getterDecl->computeSelfType(&genericParams);
selfType = getterDecl->computeSelfType(&genericParams);
if (genericParams)
type = PolymorphicFunctionType::get(selfType, type, genericParams);
else
Expand Down Expand Up @@ -218,10 +224,11 @@ DerivedConformance::declareDerivedReadOnlyProperty(TypeChecker &tc,
Identifier name,
Type propertyInterfaceType,
Type propertyContextType,
FuncDecl *getterDecl) {
FuncDecl *getterDecl,
bool isStatic) {
auto &C = tc.Context;

VarDecl *propDecl = new (C) VarDecl(/*static*/ false,
VarDecl *propDecl = new (C) VarDecl(isStatic,
/*let*/ false,
SourceLoc(), name,
propertyContextType,
Expand Down
13 changes: 11 additions & 2 deletions lib/Sema/DerivedConformances.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ ValueDecl *deriveErrorType(TypeChecker &tc,
NominalTypeDecl *type,
ValueDecl *requirement);

/// Derive a _BridgedNSError requirement for an @objc enum type.
///
/// \returns the derived member, which will also be added to the type.
ValueDecl *deriveBridgedNSError(TypeChecker &tc,
NominalTypeDecl *type,
ValueDecl *requirement);

/// Insert an operator declaration associated with a nominal type. The
/// declaration is added at global scope.
void _insertOperatorDecl(NominalTypeDecl *scope, Decl *member);
Expand All @@ -95,7 +102,8 @@ FuncDecl *declareDerivedPropertyGetter(TypeChecker &tc,
NominalTypeDecl *typeDecl,
Type contextType,
Type propertyInterfaceType,
Type propertyContextType);
Type propertyContextType,
bool isStatic = false);

/// Declare a read-only property with an existing getter.
std::pair<VarDecl *, PatternBindingDecl *>
Expand All @@ -104,7 +112,8 @@ declareDerivedReadOnlyProperty(TypeChecker &tc,
Identifier name,
Type propertyInterfaceType,
Type propertyContextType,
FuncDecl *getterDecl);
FuncDecl *getterDecl,
bool isStatic = false);


/// Build a reference to the 'self' decl of a derived function.
Expand Down
6 changes: 5 additions & 1 deletion lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3852,7 +3852,11 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(NominalTypeDecl *TypeDecl,

case KnownProtocolKind::ErrorType:
return DerivedConformance::deriveErrorType(*this, TypeDecl, Requirement);


case KnownProtocolKind::_BridgedNSError:
return DerivedConformance::deriveBridgedNSError(*this, TypeDecl,
Requirement);

default:
return nullptr;
}
Expand Down
26 changes: 26 additions & 0 deletions test/decl/enum/objc_enum_errortype.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %target-parse-verify-swift

import Foundation

func acceptBridgeableNSError<E : _ObjectiveCBridgeableErrorType>(e: E) { }

@objc enum E1 : Int, ErrorType, _BridgedNSError {
case A = 1
}

acceptBridgeableNSError(E1.A)

@objc enum E2 : Int, ErrorType {
case A = 1
}

acceptBridgeableNSError(E2.A)


@objc enum E3 : Int {
case A = 1
}

acceptBridgeableNSError(E3.A)
// expected-error@-1{{cannot invoke 'acceptBridgeableNSError' with an argument list of type '(E3)'}}
// expected-note@-2{{expected an argument list of type '(E)'}}

0 comments on commit 7405064

Please sign in to comment.