Skip to content

Commit 58cce27

Browse files
authored
Merge pull request #3940 from DougGregor/rdar-27543121
[Sema] Improve diagnostics for attempt to throw an error *code*
2 parents 8d854d2 + 31edf71 commit 58cce27

File tree

5 files changed

+120
-3
lines changed

5 files changed

+120
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ ERROR(cannot_convert_to_return_type_nil,none,
283283

284284
ERROR(cannot_convert_thrown_type,none,
285285
"thrown expression type %0 does not conform to 'Error'", (Type))
286+
ERROR(cannot_throw_error_code,none,
287+
"thrown error code type %0 does not conform to 'Error'; construct an %1 "
288+
"instance", (Type, Type))
289+
286290
ERROR(cannot_throw_nil,none,
287291
"cannot infer concrete Error for thrown 'nil' value", ())
288292

lib/Sema/CSDiag.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3677,7 +3677,7 @@ bool FailureDiagnosis::diagnoseContextualConversionError() {
36773677
diagIDProtocol = diag::cannot_convert_to_return_type_protocol;
36783678
nilDiag = diag::cannot_convert_to_return_type_nil;
36793679
break;
3680-
case CTP_ThrowStmt:
3680+
case CTP_ThrowStmt: {
36813681
if (isa<NilLiteralExpr>(expr->getValueProvidingExpr())) {
36823682
diagnose(expr->getLoc(), diag::cannot_throw_nil);
36833683
return true;
@@ -3686,14 +3686,42 @@ bool FailureDiagnosis::diagnoseContextualConversionError() {
36863686
if (isUnresolvedOrTypeVarType(exprType) ||
36873687
exprType->isEqual(contextualType))
36883688
return false;
3689-
3689+
3690+
// If we tried to throw the error code of an error type, suggest object
3691+
// construction.
3692+
auto &TC = CS->getTypeChecker();
3693+
if (auto errorCodeProtocol =
3694+
TC.Context.getProtocol(KnownProtocolKind::ErrorCodeProtocol)) {
3695+
ProtocolConformance *conformance = nullptr;
3696+
if (TC.conformsToProtocol(expr->getType(), errorCodeProtocol, CS->DC,
3697+
ConformanceCheckFlags::InExpression,
3698+
&conformance) &&
3699+
conformance) {
3700+
Type errorCodeType = expr->getType();
3701+
Type errorType =
3702+
ProtocolConformance::getTypeWitnessByName(errorCodeType, conformance,
3703+
TC.Context.Id_ErrorType,
3704+
&TC)->getCanonicalType();
3705+
if (errorType) {
3706+
auto diag = diagnose(expr->getLoc(), diag::cannot_throw_error_code,
3707+
errorCodeType, errorType);
3708+
if (auto unresolvedDot = dyn_cast<UnresolvedDotExpr>(expr)) {
3709+
diag.fixItInsert(unresolvedDot->getDotLoc(), "(");
3710+
diag.fixItInsertAfter(unresolvedDot->getEndLoc(), ")");
3711+
}
3712+
return true;
3713+
}
3714+
}
3715+
}
3716+
36903717
// The conversion destination of throw is always ErrorType (at the moment)
36913718
// if this ever expands, this should be a specific form like () is for
36923719
// return.
36933720
diagnose(expr->getLoc(), diag::cannot_convert_thrown_type, exprType)
36943721
.highlight(expr->getSourceRange());
36953722
return true;
3696-
3723+
}
3724+
36973725
case CTP_EnumCaseRawValue:
36983726
diagID = diag::cannot_convert_raw_initializer_value;
36993727
diagIDProtocol = diag::cannot_convert_raw_initializer_value;

test/Constraints/ErrorBridging.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ extension Error {
6868
return self // expected-error{{cannot convert return expression of type 'Self' to return type 'NSError'}}
6969
}
7070
}
71+
72+
// rdar://problem/27543121
73+
func throwErrorCode() throws {
74+
throw FictionalServerError.meltedDown // expected-error{{thrown error code type 'FictionalServerError.Code' does not conform to 'Error'; construct an 'FictionalServerError' instance}}{{29-29=(}}{{40-40=)}}
75+
}

test/Inputs/clang-importer-sdk/swift-modules/Foundation.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,76 @@ func _convertNSErrorToError(_ string: NSError?) -> Error
254254

255255
@_silgen_name("swift_convertErrorToNSError")
256256
func _convertErrorToNSError(_ string: Error) -> NSError
257+
258+
/// An internal protocol to represent Swift error enums that map to standard
259+
/// Cocoa NSError domains.
260+
public protocol _ObjectiveCBridgeableError : Error {
261+
/// Produce a value of the error type corresponding to the given NSError,
262+
/// or return nil if it cannot be bridged.
263+
init?(_bridgedNSError: NSError)
264+
}
265+
266+
/// Describes a bridged error that stores the underlying NSError, so
267+
/// it can be queried.
268+
public protocol _BridgedStoredNSError : _ObjectiveCBridgeableError {
269+
/// The type of an error code.
270+
associatedtype Code: _ErrorCodeProtocol
271+
272+
/// The error code for the given error.
273+
var code: Code { get }
274+
275+
//// Retrieves the embedded NSError.
276+
var _nsError: NSError { get }
277+
278+
/// Create a new instance of the error type with the given embedded
279+
/// NSError.
280+
///
281+
/// The \c error must have the appropriate domain for this error
282+
/// type.
283+
init(_nsError error: NSError)
284+
}
285+
286+
public protocol _ErrorCodeProtocol {
287+
/// The corresponding error code.
288+
associatedtype _ErrorType
289+
}
290+
291+
public extension _BridgedStoredNSError {
292+
public init?(_bridgedNSError error: NSError) {
293+
self.init(_nsError: error)
294+
}
295+
}
296+
297+
/// Various helper implementations for _BridgedStoredNSError
298+
public extension _BridgedStoredNSError
299+
where Code: RawRepresentable, Code.RawValue: SignedInteger {
300+
// FIXME: Generalize to Integer.
301+
public var code: Code {
302+
return Code(rawValue: numericCast(_nsError.code))!
303+
}
304+
305+
/// Initialize an error within this domain with the given ``code``
306+
/// and ``userInfo``.
307+
public init(_ code: Code, userInfo: [String : Any] = [:]) {
308+
self.init(_nsError: NSError(domain: "", code: 0, userInfo: [:]))
309+
}
310+
311+
/// The user-info dictionary for an error that was bridged from
312+
/// NSError.
313+
var userInfo: [String : Any] { return [:] }
314+
}
315+
316+
/// Various helper implementations for _BridgedStoredNSError
317+
public extension _BridgedStoredNSError
318+
where Code: RawRepresentable, Code.RawValue: UnsignedInteger {
319+
// FIXME: Generalize to Integer.
320+
public var code: Code {
321+
return Code(rawValue: numericCast(_nsError.code))!
322+
}
323+
324+
/// Initialize an error within this domain with the given ``code``
325+
/// and ``userInfo``.
326+
public init(_ code: Code, userInfo: [String : Any] = [:]) {
327+
self.init(_nsError: NSError(domain: "", code: 0, userInfo: [:]))
328+
}
329+
}

test/Inputs/clang-importer-sdk/usr/include/Foundation.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,3 +1065,10 @@ static const NSClothingStyle NSClothingStyleOfficeCasual __attribute__((availabi
10651065
void acceptError(NSError * _Nonnull error);
10661066
NSError * _Nonnull produceError(void);
10671067
NSError * _Nullable produceOptionalError(void);
1068+
1069+
extern NSString * const FictionalServerErrorDomain;
1070+
1071+
typedef enum __attribute__((ns_error_domain(FictionalServerErrorDomain))) FictionalServerErrorCode : NSInteger {
1072+
FictionalServerErrorMeltedDown = 1
1073+
} FictionalServerErrorCode;
1074+

0 commit comments

Comments
 (0)