Record the thrown error type for try? and try! in the AST

SILGen wants this so it knows what kind of error its going to end up
with.
This commit is contained in:
Doug Gregor
2023-10-31 14:18:31 -07:00
parent d3ede19150
commit c90c055fb6
6 changed files with 53 additions and 6 deletions

View File

@@ -1918,6 +1918,7 @@ public:
/// should dynamically assert if it does. /// should dynamically assert if it does.
class ForceTryExpr final : public AnyTryExpr { class ForceTryExpr final : public AnyTryExpr {
SourceLoc ExclaimLoc; SourceLoc ExclaimLoc;
Type thrownError;
public: public:
ForceTryExpr(SourceLoc tryLoc, Expr *sub, SourceLoc exclaimLoc, ForceTryExpr(SourceLoc tryLoc, Expr *sub, SourceLoc exclaimLoc,
@@ -1927,6 +1928,15 @@ public:
SourceLoc getExclaimLoc() const { return ExclaimLoc; } SourceLoc getExclaimLoc() const { return ExclaimLoc; }
/// Retrieve the type of the error thrown from the subexpression.
Type getThrownError() const { return thrownError; }
/// Set the type of the error thrown from the subexpression.
void setThrownError(Type type) {
assert(!thrownError || thrownError->isEqual(type));
thrownError = type;
}
static bool classof(const Expr *e) { static bool classof(const Expr *e) {
return e->getKind() == ExprKind::ForceTry; return e->getKind() == ExprKind::ForceTry;
} }
@@ -1937,6 +1947,7 @@ public:
/// Optional. If the code does throw, \c nil is produced. /// Optional. If the code does throw, \c nil is produced.
class OptionalTryExpr final : public AnyTryExpr { class OptionalTryExpr final : public AnyTryExpr {
SourceLoc QuestionLoc; SourceLoc QuestionLoc;
Type thrownError;
public: public:
OptionalTryExpr(SourceLoc tryLoc, Expr *sub, SourceLoc questionLoc, OptionalTryExpr(SourceLoc tryLoc, Expr *sub, SourceLoc questionLoc,
@@ -1946,6 +1957,15 @@ public:
SourceLoc getQuestionLoc() const { return QuestionLoc; } SourceLoc getQuestionLoc() const { return QuestionLoc; }
/// Retrieve the type of the error thrown from the subexpression.
Type getThrownError() const { return thrownError; }
/// Set the type of the error thrown from the subexpression.
void setThrownError(Type type) {
assert(!thrownError || thrownError->isEqual(type));
thrownError = type;
}
static bool classof(const Expr *e) { static bool classof(const Expr *e) {
return e->getKind() == ExprKind::OptionalTry; return e->getKind() == ExprKind::OptionalTry;
} }

View File

@@ -2690,12 +2690,22 @@ public:
void visitForceTryExpr(ForceTryExpr *E, StringRef label) { void visitForceTryExpr(ForceTryExpr *E, StringRef label) {
printCommon(E, "force_try_expr", label); printCommon(E, "force_try_expr", label);
PrintOptions PO;
PO.PrintTypesForDebugging = true;
printFieldQuoted(E->getThrownError().getString(PO), "thrown_error", TypeColor);
printRec(E->getSubExpr()); printRec(E->getSubExpr());
printFoot(); printFoot();
} }
void visitOptionalTryExpr(OptionalTryExpr *E, StringRef label) { void visitOptionalTryExpr(OptionalTryExpr *E, StringRef label) {
printCommon(E, "optional_try_expr", label); printCommon(E, "optional_try_expr", label);
PrintOptions PO;
PO.PrintTypesForDebugging = true;
printFieldQuoted(E->getThrownError().getString(PO), "thrown_error", TypeColor);
printRec(E->getSubExpr()); printRec(E->getSubExpr());
printFoot(); printFoot();
} }

View File

@@ -8892,7 +8892,8 @@ static Expr *wrapAsyncLetInitializer(
ConstraintSystem &cs, Expr *initializer, DeclContext *dc) { ConstraintSystem &cs, Expr *initializer, DeclContext *dc) {
// Form the autoclosure type. It is always 'async', and will be 'throws'. // Form the autoclosure type. It is always 'async', and will be 'throws'.
Type initializerType = initializer->getType(); Type initializerType = initializer->getType();
bool throws = TypeChecker::canThrow(cs.getASTContext(), initializer); bool throws = TypeChecker::canThrow(cs.getASTContext(), initializer)
.has_value();
auto extInfo = ASTExtInfoBuilder() auto extInfo = ASTExtInfoBuilder()
.withAsync() .withAsync()
.withConcurrent() .withConcurrent()

View File

@@ -3102,6 +3102,10 @@ private:
if (!Flags.has(ContextFlags::HasTryThrowSite)) if (!Flags.has(ContextFlags::HasTryThrowSite))
diagnoseRedundantTry(E); diagnoseRedundantTry(E);
if (auto thrownError = TypeChecker::canThrow(Ctx, E->getSubExpr())) {
E->setThrownError(*thrownError);
}
scope.preserveCoverageFromOptionalOrForcedTryOperand(); scope.preserveCoverageFromOptionalOrForcedTryOperand();
return ShouldNotRecurse; return ShouldNotRecurse;
} }
@@ -3117,6 +3121,10 @@ private:
if (!Flags.has(ContextFlags::HasTryThrowSite)) if (!Flags.has(ContextFlags::HasTryThrowSite))
diagnoseRedundantTry(E); diagnoseRedundantTry(E);
if (auto thrownError = TypeChecker::canThrow(Ctx, E->getSubExpr())) {
E->setThrownError(*thrownError);
}
scope.preserveCoverageFromOptionalOrForcedTryOperand(); scope.preserveCoverageFromOptionalOrForcedTryOperand();
return ShouldNotRecurse; return ShouldNotRecurse;
} }
@@ -3351,11 +3359,14 @@ void TypeChecker::checkPropertyWrapperEffects(
expr->walk(LocalFunctionEffectsChecker()); expr->walk(LocalFunctionEffectsChecker());
} }
bool TypeChecker::canThrow(ASTContext &ctx, Expr *expr) { llvm::Optional<Type> TypeChecker::canThrow(ASTContext &ctx, Expr *expr) {
ApplyClassifier classifier(ctx); ApplyClassifier classifier(ctx);
auto classification = classifier.classifyExpr(expr, EffectKind::Throws); auto classification = classifier.classifyExpr(expr, EffectKind::Throws);
return classification.getConditionalKind(EffectKind::Throws) != if (classification.getConditionalKind(EffectKind::Throws) ==
ConditionalEffectKind::None; ConditionalEffectKind::None)
return llvm::None;
return classification.getThrownError();
} }
Type TypeChecker::catchErrorType(ASTContext &ctx, DoCatchStmt *stmt) { Type TypeChecker::catchErrorType(ASTContext &ctx, DoCatchStmt *stmt) {

View File

@@ -1175,8 +1175,8 @@ void checkInitializerEffects(Initializer *I, Expr *E);
void checkEnumElementEffects(EnumElementDecl *D, Expr *expr); void checkEnumElementEffects(EnumElementDecl *D, Expr *expr);
void checkPropertyWrapperEffects(PatternBindingDecl *binding, Expr *expr); void checkPropertyWrapperEffects(PatternBindingDecl *binding, Expr *expr);
/// Whether the given expression can throw. /// Whether the given expression can throw, and if so, the thrown type.
bool canThrow(ASTContext &ctx, Expr *expr); llvm::Optional<Type> canThrow(ASTContext &ctx, Expr *expr);
/// Determine the error type that is thrown out of the body of the given /// Determine the error type that is thrown out of the body of the given
/// do-catch statement. /// do-catch statement.

View File

@@ -31,4 +31,9 @@ func throwsAnything() throws {
// swallow this error // swallow this error
_ = e _ = e
} // implicit rethrow } // implicit rethrow
// CHECK: force_try_expr{{.*}}thrown_error="MyError"
try! printOrFail("boom")
// CHECK: optional_try_expr{{.*}}thrown_error="MyError"
try? printOrFail("ssshhhhh")
} }