Skip to content

Commit a00fa74

Browse files
colemancdaDougGregor
authored andcommitted
[PrintAsObjC] Fix printing of 'Error' values as 'NSError *'.
Fixes SE-2159 / rdar://problem/27439384.
1 parent 9a32575 commit a00fa74

File tree

3 files changed

+75
-9
lines changed

3 files changed

+75
-9
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,9 +1243,16 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
12431243
void visitProtocolType(ProtocolType *PT,
12441244
Optional<OptionalTypeKind> optionalKind,
12451245
bool isMetatype = false) {
1246+
auto proto = PT->getDecl();
1247+
if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
1248+
if (isMetatype) os << "Class";
1249+
else os << "NSError *";
1250+
printNullability(optionalKind);
1251+
return;
1252+
}
1253+
12461254
os << (isMetatype ? "Class" : "id");
12471255

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

1269-
os << (isMetatype ? "Class" : "id");
1270-
1276+
// Dig out the protocols. If we see 'Error', record that we saw it.
1277+
bool hasError = false;
12711278
SmallVector<ProtocolDecl *, 4> protos;
1272-
std::transform(PCT->getProtocols().begin(), PCT->getProtocols().end(),
1273-
std::back_inserter(protos),
1274-
[] (Type ty) -> ProtocolDecl * {
1275-
return ty->castTo<ProtocolType>()->getDecl();
1276-
});
1279+
for (auto protoTy : PCT->getProtocols()) {
1280+
auto proto = protoTy->castTo<ProtocolType>()->getDecl();
1281+
if (proto->isSpecificProtocol(KnownProtocolKind::Error)) {
1282+
hasError = true;
1283+
continue;
1284+
}
1285+
1286+
protos.push_back(proto);
1287+
}
1288+
1289+
os << (isMetatype ? "Class"
1290+
: hasError ? "NSError"
1291+
: "id");
12771292
printProtocols(protos);
1293+
1294+
if (hasError && !isMetatype)
1295+
os << " *";
1296+
12781297
printNullability(optionalKind);
12791298
}
12801299

@@ -1630,7 +1649,8 @@ class ModuleWriter {
16301649

16311650
void forwardDeclare(const ProtocolDecl *PD) {
16321651
assert(PD->isObjC() ||
1633-
*PD->getKnownProtocolKind() == KnownProtocolKind::AnyObject);
1652+
*PD->getKnownProtocolKind() == KnownProtocolKind::AnyObject ||
1653+
*PD->getKnownProtocolKind() == KnownProtocolKind::Error);
16341654
forwardDeclare(PD, [&]{
16351655
os << "@protocol " << getNameForObjC(PD) << ";\n";
16361656
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This file is meant to be used with the mock SDK, not the real one.
2+
#import <Foundation.h>
3+
4+
@protocol ABCErrorProtocol <NSObject>
5+
6+
- (void)didFail:(NSError * _Nonnull)error;
7+
- (void)didFailOptional:(NSError * _Nullable)error;
8+
9+
@end

test/PrintAsObjC/error-delegate.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Please keep this file in alphabetical order!
2+
3+
// REQUIRES: objc_interop
4+
5+
// RUN: rm -rf %t && mkdir %t
6+
7+
// FIXME: BEGIN -enable-source-import hackaround
8+
// 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
9+
// 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
10+
// 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
11+
// FIXME: END -enable-source-import hackaround
12+
13+
// 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
14+
// 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
15+
16+
// RUN: FileCheck %s < %t/error-delegate.h
17+
// RUN: %check-in-clang %t/error-delegate.h
18+
19+
import Foundation
20+
21+
@objc protocol MySwiftProtocol { }
22+
23+
// CHECK-LABEL: @interface Test : NSObject <ABCErrorProtocol>
24+
// CHECK-NEXT: - (void)didFail:(NSError * _Nonnull)error;
25+
// CHECK-NEXT: - (void)didFailOptional:(NSError * _Nullable)error;
26+
// CHECK-NEXT-FIXME: - (void)composition:(NSError<MySwiftProtocol> * _Nonnull)error;
27+
// CHECK-NEXT-FIXME: - (void)compositionOptional:(NSError<MySwiftProtocol> * _Nullable)error;
28+
// CHECK-NEXT: - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
29+
// CHECK-NEXT: @end
30+
class Test : NSObject, ABCErrorProtocol {
31+
func didFail(_ error: Swift.Error) {}
32+
func didFailOptional(_ error: Swift.Error?) {}
33+
34+
// FIXME: SILGenc crashes on this.
35+
// func composition(_ error: MySwiftProtocol & Error) { }
36+
// func compositionOptional(_ error: (MySwiftProtocol & Error)?) { }
37+
}

0 commit comments

Comments
 (0)