Skip to content

Commit f2b12aa

Browse files
committed
[SE-0112] Allow bridging from nominal and generic types to NSError.
Previously, a type that conformed to 'Error' could not be bridged to NSError directly. Rather, one would have to go through the 'Error' protocol, e.g., myErrorValue as Error as NSError rather than myErrorValue as NSError Make the latter work.
1 parent 6e85962 commit f2b12aa

File tree

4 files changed

+66
-12
lines changed

4 files changed

+66
-12
lines changed

lib/AST/Type.cpp

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,12 +1975,17 @@ bool TypeBase::isBridgeableObjectType() {
19751975
}
19761976

19771977
bool TypeBase::isPotentiallyBridgedValueType() {
1978+
// struct and enum types
19781979
if (auto nominal = getAnyNominal()) {
19791980
if (isa<StructDecl>(nominal) || isa<EnumDecl>(nominal))
19801981
return true;
19811982
}
19821983

1983-
return isExistentialWithError();
1984+
// Error existentials.
1985+
if (isExistentialWithError()) return true;
1986+
1987+
// Archetypes.
1988+
return is<ArchetypeType>();
19841989
}
19851990

19861991
/// Determine whether this is a representable Objective-C object type.
@@ -2150,9 +2155,33 @@ getForeignRepresentable(Type type, ForeignLanguage language,
21502155
if (type->isTypeParameter() && language == ForeignLanguage::ObjectiveC)
21512156
return { ForeignRepresentableKind::Object, nullptr };
21522157

2158+
// In Objective-C, existentials involving Error are bridged
2159+
// to NSError.
2160+
if (language == ForeignLanguage::ObjectiveC &&
2161+
type->isExistentialWithError()) {
2162+
return { ForeignRepresentableKind::BridgedError, nullptr };
2163+
}
2164+
2165+
/// Determine whether the given type is a type that is bridged to NSError
2166+
/// because it conforms to the Error protocol.
2167+
auto isBridgedErrorViaConformance = [dc](Type type) -> bool {
2168+
ASTContext &ctx = type->getASTContext();
2169+
auto errorProto = ctx.getProtocol(KnownProtocolKind::Error);
2170+
if (!errorProto) return false;
2171+
2172+
return dc->getParentModule()->lookupConformance(type, errorProto,
2173+
ctx.getLazyResolver())
2174+
.hasValue();
2175+
};
2176+
21532177
auto nominal = type->getAnyNominal();
2154-
if (!nominal)
2178+
if (!nominal) {
2179+
/// It might still be a bridged Error via conformance to Error.
2180+
if (isBridgedErrorViaConformance(type))
2181+
return { ForeignRepresentableKind::BridgedError, nullptr };
2182+
21552183
return failure();
2184+
}
21562185

21572186
ASTContext &ctx = nominal->getASTContext();
21582187

@@ -2232,18 +2261,16 @@ getForeignRepresentable(Type type, ForeignLanguage language,
22322261
}
22332262
}
22342263

2235-
// In Objective-C, existentials involving Error are bridged
2236-
// to NSError.
2237-
if (language == ForeignLanguage::ObjectiveC &&
2238-
type->isExistentialWithError()) {
2239-
return { ForeignRepresentableKind::BridgedError, nullptr };
2240-
}
2241-
22422264
// Determine whether this nominal type is known to be representable
22432265
// in this foreign language.
22442266
auto result = ctx.getForeignRepresentationInfo(nominal, language, dc);
2245-
if (result.getKind() == ForeignRepresentableKind::None)
2267+
if (result.getKind() == ForeignRepresentableKind::None) {
2268+
/// It might still be a bridged Error via conformance to Error.
2269+
if (isBridgedErrorViaConformance(type))
2270+
return { ForeignRepresentableKind::BridgedError, nullptr };
2271+
22462272
return failure();
2273+
}
22472274

22482275
if (wasOptional && !result.isRepresentableAsOptional())
22492276
return failure();

stdlib/public/SDK/Foundation/NSError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public extension Error where Self : CustomNSError {
132132
public extension Error {
133133
/// Retrieve the localized description for this error.
134134
var localizedDescription: String {
135-
return (self as! NSError).localizedDescription
135+
return (self as NSError).localizedDescription
136136
}
137137
}
138138

test/Constraints/ErrorBridging.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,30 @@ let e1fix = ns1 as FooError // expected-error{{did you mean to use 'as!'}} {{17-
4141

4242
let esub = ns1 as Error
4343
let esub2 = ns1 as? Error // expected-warning{{conditional cast from 'NSError' to 'Error' always succeeds}}
44+
45+
// SR-1562 / rdar://problem/26370984
46+
enum MyError : Error {
47+
case failed
48+
}
49+
50+
func concrete1(myError: MyError) -> NSError {
51+
return myError as NSError
52+
}
53+
54+
func concrete2(myError: MyError) -> NSError {
55+
return myError // expected-error{{cannot convert return expression of type 'MyError' to return type 'NSError'}}
56+
}
57+
58+
func generic<T : Error>(error: T) -> NSError {
59+
return error as NSError
60+
}
61+
62+
extension Error {
63+
var asNSError: NSError {
64+
return self as NSError
65+
}
66+
67+
var asNSError2: NSError {
68+
return self // expected-error{{cannot convert return expression of type 'Self' to return type 'NSError'}}
69+
}
70+
}

test/Constraints/casts.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ var bad_d_is_b:Bool = D() is B // expected-warning{{always true}}
3030
func base_class_archetype_casts<T : B>(_ t: T) {
3131
var _ : B = t
3232
_ = B() as! T
33-
var _ : T = B() // expected-error{{cannot convert value of type 'B' to specified type 'T'}}
33+
var _ : T = B() // expected-error{{'B' is not convertible to 'T'; did you mean to use 'as!' to force downcast?}}
3434

3535
let b = B()
3636

0 commit comments

Comments
 (0)