Skip to content

Commit 4331d2f

Browse files
authored
Merge pull request #66966 from beccadax/unconventional-implementation
Match @objcImpl members’ foreign error conventions
2 parents e240ec5 + a8fbad3 commit 4331d2f

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,49 @@ ERROR(objc_implementation_required_attr_mismatch,none,
17191719
"the header",
17201720
(DescriptiveDeclKind, ValueDecl *, bool))
17211721

1722+
ERROR(objc_implementation_candidate_has_error_convention,none,
1723+
"%0 %1 does not match the declaration in the header because it throws an "
1724+
"error",
1725+
(DescriptiveDeclKind, ValueDecl *))
1726+
1727+
ERROR(objc_implementation_candidate_lacks_error_convention,none,
1728+
"%0 %1 does not match the declaration in the header because it does not "
1729+
"throw an error",
1730+
(DescriptiveDeclKind, ValueDecl *))
1731+
1732+
#define SELECT_FOREIGN_ERROR_CONVENTION_KIND \
1733+
"select{returning zero|returning non-zero|returning zero or a result|" \
1734+
"returning nil|setting the error parameter|%error}"
1735+
1736+
ERROR(objc_implementation_mismatched_error_convention_kind,none,
1737+
"%0 %1 does not match the declaration in the header because it indicates "
1738+
"an error by %" SELECT_FOREIGN_ERROR_CONVENTION_KIND "2, rather than by "
1739+
"%" SELECT_FOREIGN_ERROR_CONVENTION_KIND "3",
1740+
(DescriptiveDeclKind, ValueDecl *, unsigned, unsigned))
1741+
1742+
ERROR(objc_implementation_mismatched_error_convention_index,none,
1743+
"%0 %1 does not match the declaration in the header because it uses "
1744+
"parameter #%2 for the error, not parameter #%3; a selector part called "
1745+
"'error:' can control which parameter to use",
1746+
(DescriptiveDeclKind, ValueDecl *, unsigned, unsigned))
1747+
1748+
ERROR(objc_implementation_mismatched_error_convention_void_param,none,
1749+
"%0 %1 does not match the declaration in the header because it "
1750+
"%select{removes the error parameter|makes the error parameter 'Void'}2 "
1751+
"rather than %select{making it 'Void'|removing it}2",
1752+
(DescriptiveDeclKind, ValueDecl *, bool))
1753+
1754+
ERROR(objc_implementation_mismatched_error_convention_ownership,none,
1755+
"%0 %1 does not match the declaration in the header because it "
1756+
"%select{does not own|owns}2 the error parameter",
1757+
(DescriptiveDeclKind, ValueDecl *, bool))
1758+
1759+
ERROR(objc_implementation_mismatched_error_convention_other,none,
1760+
"%0 %1 does not match the declaration in the header because it uses a "
1761+
"different Objective-C error convention; please file a bug report with a "
1762+
"sample project, as the compiler should be able to describe the mismatch",
1763+
(DescriptiveDeclKind, ValueDecl *))
1764+
17221765
ERROR(objc_implementation_wrong_objc_name,none,
17231766
"selector %0 for %1 %2 not found in header; did you mean %3?",
17241767
(ObjCSelector, DescriptiveDeclKind, ValueDecl *, ObjCSelector))

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,6 +3035,7 @@ class ObjCImplementationChecker {
30353035
WrongType,
30363036
WrongWritability,
30373037
WrongRequiredAttr,
3038+
WrongForeignErrorConvention,
30383039

30393040
Match,
30403041
MatchWithExplicitObjCName,
@@ -3336,6 +3337,11 @@ class ObjCImplementationChecker {
33363337
if (reqCtor->isRequired() != cast<ConstructorDecl>(cand)->isRequired())
33373338
return MatchOutcome::WrongRequiredAttr;
33383339

3340+
if (auto reqAFD = dyn_cast<AbstractFunctionDecl>(req))
3341+
if (reqAFD->getForeignErrorConvention() !=
3342+
cast<AbstractFunctionDecl>(cand)->getForeignErrorConvention())
3343+
return MatchOutcome::WrongForeignErrorConvention;
3344+
33393345
// If we got here, everything matched. But at what quality?
33403346
if (explicitObjCName)
33413347
return MatchOutcome::MatchWithExplicitObjCName;
@@ -3439,6 +3445,49 @@ class ObjCImplementationChecker {
34393445
->getLocation());
34403446
return;
34413447
}
3448+
3449+
case MatchOutcome::WrongForeignErrorConvention: {
3450+
auto reqConv = cast<AbstractFunctionDecl>(req)->getForeignErrorConvention();
3451+
auto candConv = cast<AbstractFunctionDecl>(cand)->getForeignErrorConvention();
3452+
3453+
if (reqConv && !candConv)
3454+
diagnose(cand,
3455+
diag::objc_implementation_candidate_has_error_convention,
3456+
cand->getDescriptiveKind(), cand);
3457+
else if (!reqConv && candConv)
3458+
diagnose(cand,
3459+
diag::objc_implementation_candidate_lacks_error_convention,
3460+
cand->getDescriptiveKind(), cand);
3461+
else if (reqConv->getKind() != candConv->getKind())
3462+
diagnose(cand,
3463+
diag::objc_implementation_mismatched_error_convention_kind,
3464+
cand->getDescriptiveKind(), cand, candConv->getKind(),
3465+
reqConv->getKind());
3466+
else if (reqConv->getErrorParameterIndex()
3467+
!= candConv->getErrorParameterIndex())
3468+
diagnose(cand,
3469+
diag::objc_implementation_mismatched_error_convention_index,
3470+
cand->getDescriptiveKind(), cand,
3471+
candConv->getErrorParameterIndex() + 1,
3472+
reqConv->getErrorParameterIndex() + 1);
3473+
else if (reqConv->isErrorParameterReplacedWithVoid()
3474+
!= candConv->isErrorParameterReplacedWithVoid())
3475+
diagnose(cand,
3476+
diag::objc_implementation_mismatched_error_convention_void_param,
3477+
cand->getDescriptiveKind(), cand,
3478+
candConv->isErrorParameterReplacedWithVoid());
3479+
else if (reqConv->isErrorOwned() != candConv->isErrorOwned())
3480+
diagnose(cand,
3481+
diag::objc_implementation_mismatched_error_convention_ownership,
3482+
cand->getDescriptiveKind(), cand, candConv->isErrorOwned());
3483+
else
3484+
// Catch-all; probably shouldn't happen.
3485+
diagnose(cand,
3486+
diag::objc_implementation_mismatched_error_convention_other,
3487+
cand->getDescriptiveKind(), cand);
3488+
3489+
return;
3490+
}
34423491
}
34433492

34443493
llvm_unreachable("Unknown MatchOutcome");

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@
106106

107107
- (void)doSomethingOverloadedWithCompletionHandler:(void (^ _Nonnull)())completionHandler;
108108
- (void)doSomethingOverloaded __attribute__((__swift_attr__("@_unavailableFromAsync(message: \"Use async doSomethingOverloaded instead.\")")));
109+
110+
- (BOOL)doSomethingThatCanFailWithHandler:(void (^ _Nonnull)())handler error:(NSError **)error;
111+
- (BOOL)doSomethingElseThatCanFail:(NSError **)error handler:(void (^ _Nonnull)())handler;
112+
- (BOOL)doSomethingThatCanFailWithWeirdParameterWithHandler:(void (^ _Nonnull)())handler :(NSError **)error;
113+
- (int)doSomethingThatCanFailWithWeirdReturnCodeWithError:(NSError **)error __attribute__((swift_error(nonzero_result)));
114+
109115
@end
110116

111117
@protocol PartiallyOptionalProtocol

test/decl/ext/objc_implementation.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,26 @@ protocol EmptySwiftProto {}
335335
@available(*, noasync)
336336
@objc(doSomethingOverloaded)
337337
public func doSomethingOverloaded() {}
338+
339+
@objc(doSomethingThatCanFailWithHandler:error:)
340+
public func doSomethingThatCanFail(handler: @escaping () -> Void) throws {
341+
// OK
342+
}
343+
344+
@objc(doSomethingElseThatCanFail:handler:)
345+
public func doSomethingElseThatCanFail(handler: @escaping () -> Void) throws {
346+
// OK
347+
}
348+
349+
@objc(doSomethingThatCanFailWithWeirdParameterWithHandler::)
350+
public func doSomethingThatCanFailWithWeirdParameter(handler: @escaping () -> Void) throws {
351+
// expected-error@-1 {{instance method 'doSomethingThatCanFailWithWeirdParameter(handler:)' does not match the declaration in the header because it uses parameter #1 for the error, not parameter #2; a selector part called 'error:' can control which parameter to use}}
352+
}
353+
354+
@objc(doSomethingThatCanFailWithWeirdReturnCodeWithError:)
355+
public func doSomethingThatCanFailWithWeirdReturnCode() throws {
356+
// expected-error@-1 {{instance method 'doSomethingThatCanFailWithWeirdReturnCode()' does not match the declaration in the header because it indicates an error by returning zero, rather than by returning non-zero}}
357+
}
338358
}
339359

340360
@_objcImplementation(Conformance) extension ObjCClass {

0 commit comments

Comments
 (0)