[Constraint solver] Encode thrown error type in references to subscripts

A reference to a subscript (whether instance or static) produces a
function type, which is then applied as part of an application
constraint. Make sure that we encode whether the subscript can throw,
and the thrown error type if present, in that function type. This lets
us correctly treat subscripts as a potential throw sites.
This commit is contained in:
Doug Gregor
2023-12-13 15:22:21 -08:00
parent 8ad137fb51
commit 956c330611
2 changed files with 43 additions and 5 deletions

View File

@@ -2674,6 +2674,18 @@ ConstraintSystem::getTypeOfMemberReference(
[&](Type type) { return openType(type, replacements, locator); });
}
} else {
// Figure out the effect information for the reference.
FunctionType::ExtInfo info;
auto storage = cast<AbstractStorageDecl>(value);
// If the storage has a throwing getter, record that in the type.
if (auto effectfulGetter = storage->getEffectfulGetAccessor()) {
if (effectfulGetter->hasThrows()) {
Type thrownErrorType = effectfulGetter->getThrownInterfaceType();
info = info.withThrows(true, thrownErrorType);
}
}
// For a property, build a type (Self) -> PropType.
// For a subscript, build a type (Self) -> (Indices...) -> ElementType.
//
@@ -2687,8 +2699,7 @@ ConstraintSystem::getTypeOfMemberReference(
auto indices = subscript->getInterfaceType()
->castTo<AnyFunctionType>()->getParams();
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
refType = FunctionType::get(indices, elementTy, info);
} else {
// Delay the adjustment for preconcurrency until after we've formed
@@ -2720,8 +2731,6 @@ ConstraintSystem::getTypeOfMemberReference(
}
FunctionType::Param selfParam(selfTy, Identifier(), selfFlags);
// FIXME: Verify ExtInfo state is correct, not working by accident.
FunctionType::ExtInfo info;
openedType = FunctionType::get({selfParam}, refType, info);
}
assert(!openedType->hasTypeParameter());

View File

@@ -192,7 +192,19 @@ func testDoCatchErrorTypedInClosure(cond: Bool) {
}
}
func testDoCatchInClosure(cond: Bool) {
struct ThrowingMembers {
subscript(i: Int) -> Int {
get throws(MyError) { i }
}
}
struct ThrowingStaticSubscript {
static subscript(i: Int) -> Int {
get throws(MyError) { i }
}
}
func testDoCatchInClosure(cond: Bool, x: ThrowingMembers) {
apply {
do {
_ = try doSomething()
@@ -233,4 +245,21 @@ func testDoCatchInClosure(cond: Bool) {
let _: MyError = error
}
}
// Subscripts as potential throw sites
apply {
do {
_ = try x[5]
} catch {
let _: MyError = error
}
}
apply {
do {
_ = try ThrowingStaticSubscript[5]
} catch {
let _: MyError = error
}
}
}