Skip to content

Commit 359c892

Browse files
authored
Merge pull request #69922 from DougGregor/typed-throw-overrides
[Typed throws] Support overrides that are contravariant in the thrown error
2 parents a037a19 + d4e1558 commit 359c892

File tree

9 files changed

+140
-6
lines changed

9 files changed

+140
-6
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,9 @@ ERROR(override_class_declaration_in_extension,none,
33663366
ERROR(override_with_more_effects,none,
33673367
"cannot override non-%1 %0 with %1 %0",
33683368
(DescriptiveDeclKind, StringRef))
3369+
ERROR(override_typed_throws,none,
3370+
"%0 that throws %1 cannot override one that throws %2",
3371+
(DescriptiveDeclKind, Type, Type))
33693372
ERROR(override_throws_objc,none,
33703373
"overriding a throwing @objc %select{method|initializer}0 with "
33713374
"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: 43 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,54 @@ 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 && baseThrownError->hasTypeParameter()) {
2051+
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);
2052+
baseThrownError = baseThrownError.subst(subs);
2053+
baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError);
2054+
}
2055+
2056+
if (overrideThrownError)
2057+
overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError);
2058+
2059+
// Check for a subtyping relationship.
2060+
switch (compareThrownErrorsForSubtyping(
2061+
overrideThrownError, baseThrownError, overrideFn)) {
2062+
case ThrownErrorSubtyping::DropsThrows:
20442063
diags.diagnose(override, diag::override_with_more_effects,
20452064
override->getDescriptiveKind(), "throwing");
20462065
diags.diagnose(base, diag::overridden_here);
2066+
break;
2067+
2068+
case ThrownErrorSubtyping::Mismatch:
2069+
diags.diagnose(override, diag::override_typed_throws,
2070+
override->getDescriptiveKind(), overrideThrownError,
2071+
baseThrownError);
2072+
diags.diagnose(base, diag::overridden_here);
2073+
break;
2074+
2075+
case ThrownErrorSubtyping::ExactMatch:
2076+
case ThrownErrorSubtyping::Subtype:
2077+
// Proper subtyping.
2078+
break;
2079+
2080+
case ThrownErrorSubtyping::Dependent:
2081+
// Only in already ill-formed code.
2082+
assert(ctx.Diags.hadAnyError());
2083+
break;
20472084
}
20482085

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

lib/Sema/TypeCheckEffects.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,13 @@ ThrownErrorSubtyping
35953595
swift::compareThrownErrorsForSubtyping(
35963596
Type subThrownError, Type superThrownError, DeclContext *dc
35973597
) {
3598+
// Deal with NULL errors. This should only occur when there is no standard
3599+
// library.
3600+
if (!subThrownError || !superThrownError) {
3601+
assert(!dc->getASTContext().getStdlibModule() && "NULL thrown error type");
3602+
return ThrownErrorSubtyping::ExactMatch;
3603+
}
3604+
35983605
// Easy case: exact match.
35993606
if (superThrownError->isEqual(subThrownError))
36003607
return ThrownErrorSubtyping::ExactMatch;

test/IRGen/protocol_synthesized.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
import SynthesizedProtocol
1212

13+
enum Never { }
14+
protocol Error { }
15+
1316
// CHECK: @"$sSo5Flagsas9OptionSetSCMc" = linkonce_odr hidden constant { i32, i32, i32, i32, i16, i16, i32, i32 } { i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$ss9OptionSetMp" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCMc" to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5FlagsaMn" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 1) to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCWP" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 2) to {{i(32|64)}})){{( to i32\))?}}, i32 131200, i16 3, i16 1, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCWI" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 6) to {{i(32|64)}})){{( to i32\))?}}, i32 {{(trunc \(i64 )?}}sub ({{i(32|64)}} ptrtoint (ptr @"$sSo5Flagsas9OptionSetSCMcMK" to {{i(32|64)}}), {{i(32|64)}} ptrtoint (ptr getelementptr inbounds ({ i32, i32, i32, i32, i16, i16, i32, i32 }, ptr @"$sSo5Flagsas9OptionSetSCMc", i32 0, i32 7) to {{i(32|64)}})) {{(to i32\) )?}}}, section "{{[^"]*}}"{{(, comdat)?}},{{.*}} align 4
1417

1518
// Triggers the inclusion of the relevant ProtocolConformanceDescriptor

test/SIL/OwnershipVerifier/definite_init.sil

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ sil_stage raw
55

66
import Builtin
77

8+
enum Never { }
9+
protocol Error { }
10+
811
enum Optional<T> {
912
case some(T)
1013
case none

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)