Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3366,6 +3366,9 @@ ERROR(override_class_declaration_in_extension,none,
ERROR(override_with_more_effects,none,
"cannot override non-%1 %0 with %1 %0",
(DescriptiveDeclKind, StringRef))
ERROR(override_typed_throws,none,
"%0 that throws %1 cannot override one that throws %2",
(DescriptiveDeclKind, Type, Type))
ERROR(override_throws_objc,none,
"overriding a throwing @objc %select{method|initializer}0 with "
"a non-throwing %select{method|initializer}0 is not supported", (bool))
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/ExtInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ class ASTExtInfoBuilder {
return bits == other.bits &&
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true) &&
globalActor.getPointer() == other.globalActor.getPointer() &&
thrownError.getPointer() == thrownError.getPointer();
thrownError.getPointer() == other.thrownError.getPointer();
}

constexpr std::tuple<unsigned, const void *, const void *, const void *>
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3421,7 +3421,8 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
if (matchMode.contains(TypeMatchFlags::AllowOverride)) {
// Removing 'throwing' is ABI-compatible for synchronous functions, but
// not for async ones.
if (ext2.isThrowing() &&
if (ext2.isThrowing() && !ext1.isThrowing() &&
ext2.getThrownError().isNull() &&
!(ext2.isAsync() &&
matchMode.contains(TypeMatchFlags::AllowABICompatible))) {
ext1 = ext1.withThrows(true, ext2.getThrownError());
Expand Down
47 changes: 43 additions & 4 deletions lib/Sema/TypeCheckDeclOverride.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "TypeCheckAvailability.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckDecl.h"
#include "TypeCheckEffects.h"
#include "TypeCheckObjC.h"
#include "TypeChecker.h"
#include "swift/AST/ASTVisitor.h"
Expand Down Expand Up @@ -2036,16 +2037,54 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
diags.diagnose(base, diag::overridden_here);
}
}
// If the overriding declaration is 'throws' but the base is not,
// complain. Do the same for 'async'

// Check effects.
if (auto overrideFn = dyn_cast<AbstractFunctionDecl>(override)) {
if (overrideFn->hasThrows() &&
!cast<AbstractFunctionDecl>(base)->hasThrows()) {
// Determine the thrown errors in the base and override declarations.
auto baseFn = cast<AbstractFunctionDecl>(base);
Type overrideThrownError =
overrideFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());
Type baseThrownError =
baseFn->getEffectiveThrownErrorType().value_or(ctx.getNeverType());

if (baseThrownError && baseThrownError->hasTypeParameter()) {
auto subs = SubstitutionMap::getOverrideSubstitutions(base, override);
baseThrownError = baseThrownError.subst(subs);
baseThrownError = overrideFn->mapTypeIntoContext(baseThrownError);
}

if (overrideThrownError)
overrideThrownError = overrideFn->mapTypeIntoContext(overrideThrownError);

// Check for a subtyping relationship.
switch (compareThrownErrorsForSubtyping(
overrideThrownError, baseThrownError, overrideFn)) {
case ThrownErrorSubtyping::DropsThrows:
diags.diagnose(override, diag::override_with_more_effects,
override->getDescriptiveKind(), "throwing");
diags.diagnose(base, diag::overridden_here);
break;

case ThrownErrorSubtyping::Mismatch:
diags.diagnose(override, diag::override_typed_throws,
override->getDescriptiveKind(), overrideThrownError,
baseThrownError);
diags.diagnose(base, diag::overridden_here);
break;

case ThrownErrorSubtyping::ExactMatch:
case ThrownErrorSubtyping::Subtype:
// Proper subtyping.
break;

case ThrownErrorSubtyping::Dependent:
// Only in already ill-formed code.
assert(ctx.Diags.hadAnyError());
break;
}

// If the override is 'async' but the base declaration is not, we have a
// problem.
if (overrideFn->hasAsync() &&
!cast<AbstractFunctionDecl>(base)->hasAsync()) {
diags.diagnose(override, diag::override_with_more_effects,
Expand Down
7 changes: 7 additions & 0 deletions lib/Sema/TypeCheckEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3595,6 +3595,13 @@ ThrownErrorSubtyping
swift::compareThrownErrorsForSubtyping(
Type subThrownError, Type superThrownError, DeclContext *dc
) {
// Deal with NULL errors. This should only occur when there is no standard
// library.
if (!subThrownError || !superThrownError) {
assert(!dc->getASTContext().getStdlibModule() && "NULL thrown error type");
return ThrownErrorSubtyping::ExactMatch;
}

// Easy case: exact match.
if (superThrownError->isEqual(subThrownError))
return ThrownErrorSubtyping::ExactMatch;
Expand Down
3 changes: 3 additions & 0 deletions test/IRGen/protocol_synthesized.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

import SynthesizedProtocol

enum Never { }
protocol Error { }

// 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

// Triggers the inclusion of the relevant ProtocolConformanceDescriptor
Expand Down
3 changes: 3 additions & 0 deletions test/SIL/OwnershipVerifier/definite_init.sil
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ sil_stage raw

import Builtin

enum Never { }
protocol Error { }

enum Optional<T> {
case some(T)
case none
Expand Down
20 changes: 20 additions & 0 deletions test/SILGen/typed_throws.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,24 @@ open class MyClass {
// CHECK-NEXT: throw_addr
public init<E>(body: () throws(E) -> Void) throws(E) {
}

func f() throws { }
}

// CHECK-LABEL: sil_vtable MySubclass {
// CHECK-NEXT: #MyClass.init!allocator: <E where E : Error> (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws10MySubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override]
// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKFAA0C5ClassCADyyKFTV [override]
// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws10MySubclassC1fyyAA0C5ErrorOYKF

class MySubclass: MyClass {
override func f() throws(MyError) { }
}

// CHECK-LABEL: sil_vtable MySubsubclass
// CHECK-NEXT: #MyClass.init!allocator: <E where E : Error> (MyClass.Type) -> (() throws(E) -> ()) throws(E) -> MyClass : @$s12typed_throws13MySubsubclassC4bodyACyyxYKXE_txYKcs5ErrorRzlufC [override]
// CHECK-NEXT: #MyClass.f: (MyClass) -> () throws -> () : @$s12typed_throws13MySubsubclassC1fyyF [override]
// CHECK-NEXT: #MySubclass.f: (MySubclass) -> () throws(MyError) -> () : @$s12typed_throws13MySubsubclassC1fyyF [override]
// CHECK-NEXT: #MySubsubclass.f: (MySubsubclass) -> () -> () : @$s12typed_throws13MySubsubclassC1fyyF
class MySubsubclass: MySubclass {
override func f() { }
}
58 changes: 58 additions & 0 deletions test/decl/class/typed_throws_override.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %target-typecheck-verify-swift -parse-as-library -enable-experimental-feature TypedThrows

enum MyError: Error {
case failed
}

enum HomeworkError: Error {
case dogAteIt
}

class SuperError: Error { }
class SubError: SuperError { }

class Super {
func f() throws { }
func g() throws(MyError) { }
func h() throws(HomeworkError) { } // expected-note{{overridden declaration is here}}
func i() throws(SuperError) { }

var propF: Int {
get throws { 5 }
}

var propG: Int {
get throws(MyError) { 5 }
}

var propH: Int {
get throws(HomeworkError) { 5 } // expected-note{{overridden declaration is here}}
}

var propI: Int {
get throws(SuperError) { 5 }
}
}

class Sub: Super {
override func f() throws(MyError) { } // okay to make type more specific
override func g() { } // okay to be non-throwing
override func h() throws(MyError) { } // expected-error{{instance method that throws 'MyError' cannot override one that throws 'HomeworkError'}}
override func i() throws(SubError) { } // okay to have a subtype

override var propF: Int {
get throws(MyError) { 5 } // okay to make type more specific
}

override var propG: Int {
get { 5 } // okay to be non-throwing
}

override var propH: Int {
get throws(MyError) { 5 } // expected-error{{getter that throws 'MyError' cannot override one that throws 'HomeworkError'}}
}

override var propI: Int {
get throws(SubError) { 5 } // okay to make type more specific
}
}