Skip to content

[Typed throws] Add upcoming feature FullTypedThrows #69225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 17, 2023
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
1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ UPCOMING_FEATURE(ExistentialAny, 335, 6)
UPCOMING_FEATURE(ImportObjcForwardDeclarations, 384, 6)
UPCOMING_FEATURE(DisableOutwardActorInference, 401, 6)
UPCOMING_FEATURE(InternalImportsByDefault, 409, 6)
UPCOMING_FEATURE(FullTypedThrows, 410, 6)

EXPERIMENTAL_FEATURE(StaticAssert, false)
EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false)
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3575,6 +3575,10 @@ static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) {
return decl->hasClangNode();
}

static bool usesFeatureFullTypedThrows(Decl *decl) {
return false;
}

static bool usesFeatureTypedThrows(Decl *decl) {
if (auto func = dyn_cast<AbstractFunctionDecl>(decl))
return func->getThrownTypeRepr() != nullptr;
Expand Down
46 changes: 43 additions & 3 deletions lib/Sema/TypeCheckEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,27 @@ static void simple_display(llvm::raw_ostream &out, ConditionalEffectKind kind) {
llvm_unreachable("Bad conditional effect kind");
}

/// Remove the type erasure to an existential error, to extract the
/// underlying error.
static Expr *removeErasureToExistentialError(Expr *expr) {
Type type = expr->getType();
if (!type)
return expr;

ASTContext &ctx = type->getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::FullTypedThrows) ||
!ctx.LangOpts.hasFeature(Feature::TypedThrows))
return expr;

// Look for an outer erasure expression.
if (auto erasure = dyn_cast<ErasureExpr>(expr)) {
if (type->isEqual(ctx.getErrorExistentialType()))
return erasure->getSubExpr();
}

return expr;
}

/// A type expressing the result of classifying whether a call or function
/// throws or is async.
class Classification {
Expand Down Expand Up @@ -989,6 +1010,10 @@ class ApplyClassifier {
if (!thrownValue)
return Classification::forInvalidCode();

// If we are doing full typed throws, look through an existential
// conversion to find the underlying type.
thrownValue = removeErasureToExistentialError(thrownValue);

Type thrownType = thrownValue->getType();
if (!thrownType)
return Classification::forInvalidCode();
Expand Down Expand Up @@ -2893,6 +2918,18 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>

if (!CurContext.handlesThrows(ConditionalEffectKind::Always))
CurContext.diagnoseUnhandledThrowStmt(Ctx.Diags, S);
else {
SourceLoc loc = S->getThrowLoc();
Expr *thrownValue = S->getSubExpr();
Type thrownErrorType = thrownValue->getType();
Type caughtErrorType = getCaughtErrorTypeAt(loc);
if (!caughtErrorType->isEqual(thrownErrorType)) {
thrownValue = removeErasureToExistentialError(thrownValue);
Type thrownErrorType = thrownValue->getType();
if (!checkThrownErrorType(loc, thrownErrorType))
S->setSubExpr(thrownValue);
}
}
}

return ShouldRecurse;
Expand Down Expand Up @@ -2978,16 +3015,19 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>

/// Check the thrown error type against the type that can be caught or
/// rethrown by the context.
void checkThrownErrorType(SourceLoc loc, Type thrownErrorType) {
///
/// Returns \c true if an error occurred, false otherwise.
bool checkThrownErrorType(SourceLoc loc, Type thrownErrorType) {
Type caughtErrorType = getCaughtErrorTypeAt(loc);
if (caughtErrorType->isEqual(thrownErrorType))
return;
return false ;

OpaqueValueExpr *opaque = new (Ctx) OpaqueValueExpr(loc, thrownErrorType);
Expr *rethrowExpr = opaque;
TypeChecker::typeCheckExpression(
Type resultType = TypeChecker::typeCheckExpression(
rethrowExpr, CurContext.getDeclContext(),
{caughtErrorType, /*FIXME:*/CTP_ThrowStmt});
return resultType.isNull();
}

ShouldRecurse_t checkAwait(AwaitExpr *E) {
Expand Down
17 changes: 12 additions & 5 deletions test/stmt/typed_throws.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows
// RUN: %target-typecheck-verify-swift -enable-experimental-feature TypedThrows -enable-upcoming-feature FullTypedThrows

enum MyError: Error {
case failed
Expand All @@ -15,16 +15,23 @@ func processMyError(_: MyError) { }
func doSomething() throws(MyError) { }
func doHomework() throws(HomeworkError) { }

func testDoCatchErrorTyped() {
#if false
// FIXME: Deal with throws directly in the do...catch blocks.
func testDoCatchErrorTyped(cond: Bool) {
do {
throw MyError.failed
} catch {
assert(error == .failed)
processMyError(error)
}
#endif

do {
if cond {
throw MyError.failed
} else {
throw HomeworkError.dogAteIt
}
} catch {
processMyError(error) // expected-error{{cannot convert value of type 'any Error' to expected argument type 'MyError'}}
}

// Throwing a typed error in a do...catch catches the error with that type.
do {
Expand Down