Skip to content

Commit

Permalink
[PrintAsObjC] Fix printing of 'Error' values as 'NSError *'.
Browse files Browse the repository at this point in the history
Fixes SE-2159 / rdar://problem/27439384.
  • Loading branch information
colemancda authored and DougGregor committed Jul 26, 2016
1 parent 9a32575 commit a00fa74
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 9 deletions.
38 changes: 29 additions & 9 deletions lib/PrintAsObjC/PrintAsObjC.cpp
Expand Up @@ -1243,9 +1243,16 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
void visitProtocolType(ProtocolType *PT,
Optional<OptionalTypeKind> optionalKind,
bool isMetatype = false) {
auto proto = PT->getDecl();
if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
if (isMetatype) os << "Class";
else os << "NSError *";
printNullability(optionalKind);
return;
}

os << (isMetatype ? "Class" : "id");

auto proto = PT->getDecl();
assert(proto->isObjC());
if (auto knownKind = proto->getKnownProtocolKind()) {
if (*knownKind == KnownProtocolKind::AnyObject) {
Expand All @@ -1266,15 +1273,27 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
return visitProtocolType(singleProto, optionalKind, isMetatype);
PCT = cast<ProtocolCompositionType>(canonicalComposition);

os << (isMetatype ? "Class" : "id");

// Dig out the protocols. If we see 'Error', record that we saw it.
bool hasError = false;
SmallVector<ProtocolDecl *, 4> protos;
std::transform(PCT->getProtocols().begin(), PCT->getProtocols().end(),
std::back_inserter(protos),
[] (Type ty) -> ProtocolDecl * {
return ty->castTo<ProtocolType>()->getDecl();
});
for (auto protoTy : PCT->getProtocols()) {
auto proto = protoTy->castTo<ProtocolType>()->getDecl();
if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
hasError = true;
continue;
}

protos.push_back(proto);
}

os << (isMetatype ? "Class"
: hasError ? "NSError"
: "id");
printProtocols(protos);

if (hasError && !isMetatype)
os << " *";

printNullability(optionalKind);
}

Expand Down Expand Up @@ -1630,7 +1649,8 @@ class ModuleWriter {

void forwardDeclare(const ProtocolDecl *PD) {
assert(PD->isObjC() ||
*PD->getKnownProtocolKind() == KnownProtocolKind::AnyObject);
*PD->getKnownProtocolKind() == KnownProtocolKind::AnyObject ||
*PD->getKnownProtocolKind() == KnownProtocolKind::Error);
forwardDeclare(PD, [&]{
os << "@protocol " << getNameForObjC(PD) << ";\n";
});
Expand Down
9 changes: 9 additions & 0 deletions test/PrintAsObjC/Inputs/error-delegate.h
@@ -0,0 +1,9 @@
// This file is meant to be used with the mock SDK, not the real one.
#import <Foundation.h>

@protocol ABCErrorProtocol <NSObject>

- (void)didFail:(NSError * _Nonnull)error;
- (void)didFailOptional:(NSError * _Nullable)error;

@end
37 changes: 37 additions & 0 deletions test/PrintAsObjC/error-delegate.swift
@@ -0,0 +1,37 @@
// Please keep this file in alphabetical order!

// REQUIRES: objc_interop

// RUN: rm -rf %t && mkdir %t

// FIXME: BEGIN -enable-source-import hackaround
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/ObjectiveC.swift
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/CoreGraphics.swift
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -emit-module -o %t %S/../Inputs/clang-importer-sdk/swift-modules/Foundation.swift
// FIXME: END -enable-source-import hackaround

// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/error-delegate.h -emit-module -o %t %s
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/../Inputs/clang-importer-sdk -I %t) -import-objc-header %S/Inputs/error-delegate.h -parse-as-library %t/error-delegate.swiftmodule -parse -emit-objc-header-path %t/error-delegate.h

// RUN: FileCheck %s < %t/error-delegate.h
// RUN: %check-in-clang %t/error-delegate.h

import Foundation

@objc protocol MySwiftProtocol { }

// CHECK-LABEL: @interface Test : NSObject <ABCErrorProtocol>
// CHECK-NEXT: - (void)didFail:(NSError * _Nonnull)error;
// CHECK-NEXT: - (void)didFailOptional:(NSError * _Nullable)error;
// CHECK-NEXT-FIXME: - (void)composition:(NSError<MySwiftProtocol> * _Nonnull)error;
// CHECK-NEXT-FIXME: - (void)compositionOptional:(NSError<MySwiftProtocol> * _Nullable)error;
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
// CHECK-NEXT: @end
class Test : NSObject, ABCErrorProtocol {
func didFail(_ error: Swift.Error) {}
func didFailOptional(_ error: Swift.Error?) {}

// FIXME: SILGenc crashes on this.
// func composition(_ error: MySwiftProtocol & Error) { }
// func compositionOptional(_ error: (MySwiftProtocol & Error)?) { }
}

0 comments on commit a00fa74

Please sign in to comment.