Skip to content

Commit 33d6944

Browse files
committed
[Typed throws] Support overrides that are contravariant in the thrown error
1 parent 3d84493 commit 33d6944

File tree

6 files changed

+126
-6
lines changed

6 files changed

+126
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3362,6 +3362,9 @@ ERROR(override_class_declaration_in_extension,none,
33623362
ERROR(override_with_more_effects,none,
33633363
"cannot override non-%1 %0 with %1 %0",
33643364
(DescriptiveDeclKind, StringRef))
3365+
ERROR(override_typed_throws,none,
3366+
"%0 that throws %1 cannot override one that throws %2",
3367+
(DescriptiveDeclKind, Type, Type))
33653368
ERROR(override_throws_objc,none,
33663369
"overriding a throwing @objc %select{method|initializer}0 with "
33673370
"a non-throwing %select{method|initializer}0 is not supported", (bool))

include/swift/AST/ExtInfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ class ASTExtInfoBuilder {
544544
return bits == other.bits &&
545545
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) &&
546546
globalActor.getPointer() == other.globalActor.getPointer() &&
547-
thrownError.getPointer() == thrownError.getPointer();
547+
thrownError.getPointer() == other.thrownError.getPointer();
548548
}
549549

550550
constexpr std::tuple<unsigned, const void *, const void *, const void *>

lib/AST/Type.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3421,7 +3421,8 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
34213421
if (matchMode.contains(TypeMatchFlags::AllowOverride)) {
34223422
// Removing 'throwing' is ABI-compatible for synchronous functions, but
34233423
// not for async ones.
3424-
if (ext2.isThrowing() &&
3424+
if (ext2.isThrowing() && !ext1.isThrowing() &&
3425+
ext2.getThrownError().isNull() &&
34253426
!(ext2.isAsync() &&
34263427
matchMode.contains(TypeMatchFlags::AllowABICompatible))) {
34273428
ext1 = ext1.withThrows(true, ext2.getThrownError());

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "TypeCheckAvailability.h"
1818
#include "TypeCheckConcurrency.h"
1919
#include "TypeCheckDecl.h"
20+
#include "TypeCheckEffects.h"
2021
#include "TypeCheckObjC.h"
2122
#include "TypeChecker.h"
2223
#include "swift/AST/ASTVisitor.h"
@@ -2036,16 +2037,53 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
20362037
diags.diagnose(base, diag::overridden_here);
20372038
}
20382039
}
2039-
// If the overriding declaration is 'throws' but the base is not,
2040-
// complain. Do the same for 'async'
2040+
2041+
// Check effects.
20412042
if (auto overrideFn = dyn_cast<AbstractFunctionDecl>(override)) {
2042-
if (overrideFn->hasThrows() &&
2043-
!cast<AbstractFunctionDecl>(base)->hasThrows()) {
2043+
// Determine the thrown errors in the base and override declarations.
2044+
auto baseFn = cast<AbstractFunctionDecl>(base);
2045+
Type overrideThrownError =
2046+
overrideFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());
2047+
Type baseThrownError =
2048+
baseFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());
2049+
2050+
if (baseThrownError->hasTypeParameter()) {
2051+
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);
2052+
baseThrownError = baseThrownError.subst(subs);
2053+
baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError);
2054+
}
2055+
2056+
overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError);
2057+
2058+
// Check for a subtyping relationship.
2059+
switch (compareThrownErrorsForSubtyping(
2060+
overrideThrownError, baseThrownError, overrideFn)) {
2061+
case ThrownErrorSubtyping::DropsThrows:
20442062
diags.diagnose(override, diag::override_with_more_effects,
20452063
override->getDescriptiveKind(), "throwing");
20462064
diags.diagnose(base, diag::overridden_here);
2065+
break;
2066+
2067+
case ThrownErrorSubtyping::Mismatch:
2068+
diags.diagnose(override, diag::override_typed_throws,
2069+
override->getDescriptiveKind(), overrideThrownError,
2070+
baseThrownError);
2071+
diags.diagnose(base, diag::overridden_here);
2072+
break;
2073+
2074+
case ThrownErrorSubtyping::ExactMatch:
2075+
case ThrownErrorSubtyping::Subtype:
2076+
// Proper subtyping.
2077+
break;
2078+
2079+
case ThrownErrorSubtyping::Dependent:
2080+
// Only in already ill-formed code.
2081+
assert(ctx.Diags.hadAnyError());
2082+
break;
20472083
}
20482084

2085+
// If the override is 'async' but the base declaration is not, we have a
2086+
// problem.
20492087
if (overrideFn->hasAsync() &&
20502088
!cast<AbstractFunctionDecl>(base)->hasAsync()) {
20512089
diags.diagnose(override, diag::override_with_more_effects,

test/SILGen/typed_throws.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,24 @@ open class MyClass {
126126
// CHECK-NEXT: throw_addr
127127
public init<E>(body: () throws(E) -> Void) throws(E) {
128128
}
129+
130+
func f() throws { }
131+
}
132+
133+
// CHECK-LABEL: sil_vtable MySubclass {
134+
// CHECK-NEXT: #MyClass.init!allocator: <E where E : Error> (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws10MySubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override]
135+
// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKFAA0C5ClassCADyyKFTV [override]
136+
// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKF
137+
138+
class MySubclass: MyClass {
139+
override func f() throws(MyError) { }
140+
}
141+
142+
// CHECK-LABEL: sil_vtable MySubsubclass
143+
// CHECK-NEXT: #MyClass.init!allocator: <E where E : Error> (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws13MySubsubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override]
144+
// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws13MySubsubclassC1fyyF [override]
145+
// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws13MySubsubclassC1fyyF [override]
146+
// CHECK-NEXT: #MySubsubclass.f: (MySubsubclass) -> () -> () : @$s12typed_throws13MySubsubclassC1fyyF
147+
class MySubsubclass: MySubclass {
148+
override func f() { }
129149
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %target-typecheck-verify-swift -parse-as-library -enable-experimental-feature TypedThrows
2+
3+
enum MyError: Error {
4+
case failed
5+
}
6+
7+
enum HomeworkError: Error {
8+
case dogAteIt
9+
}
10+
11+
class SuperError: Error { }
12+
class SubError: SuperError { }
13+
14+
class Super {
15+
func f() throws { }
16+
func g() throws(MyError) { }
17+
func h() throws(HomeworkError) { } // expected-note{{overridden declaration is here}}
18+
func i() throws(SuperError) { }
19+
20+
var propF: Int {
21+
get throws { 5 }
22+
}
23+
24+
var propG: Int {
25+
get throws(MyError) { 5 }
26+
}
27+
28+
var propH: Int {
29+
get throws(HomeworkError) { 5 } // expected-note{{overridden declaration is here}}
30+
}
31+
32+
var propI: Int {
33+
get throws(SuperError) { 5 }
34+
}
35+
}
36+
37+
class Sub: Super {
38+
override func f() throws(MyError) { } // okay to make type more specific
39+
override func g() { } // okay to be non-throwing
40+
override func h() throws(MyError) { } // expected-error{{instance method that throws 'MyError' cannot override one that throws 'HomeworkError'}}
41+
override func i() throws(SubError) { } // okay to have a subtype
42+
43+
override var propF: Int {
44+
get throws(MyError) { 5 } // okay to make type more specific
45+
}
46+
47+
override var propG: Int {
48+
get { 5 } // okay to be non-throwing
49+
}
50+
51+
override var propH: Int {
52+
get throws(MyError) { 5 } // expected-error{{getter that throws 'MyError' cannot override one that throws 'HomeworkError'}}
53+
}
54+
55+
override var propI: Int {
56+
get throws(SubError) { 5 } // okay to make type more specific
57+
}
58+
}

0 commit comments

Comments
 (0)