Skip to content

Commit 5ad39c8

Browse files
committed
[Typed throws] Record thrown error types and conversions in the AST
For any operation that can throw an error, such as calls, property accesses, and non-exhaustive do..catch statements, record the thrown error type along with the conversion from that thrown error to the error type expected in context, as appropriate. This will prevent later stages from having to re-compute the conversion sequences.
1 parent 7c6dacb commit 5ad39c8

19 files changed

+334
-75
lines changed

include/swift/AST/Expr.h

+41-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/AST/FreestandingMacroExpansion.h"
2929
#include "swift/AST/FunctionRefKind.h"
3030
#include "swift/AST/ProtocolConformanceRef.h"
31+
#include "swift/AST/ThrownErrorDestination.h"
3132
#include "swift/AST/TypeAlignments.h"
3233
#include "swift/Basic/Debug.h"
3334
#include "swift/Basic/InlineBitfield.h"
@@ -324,9 +325,8 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
324325
NumCaptures : 32
325326
);
326327

327-
SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1+1+1+1,
328+
SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1+1,
328329
ThrowsIsSet : 1,
329-
Throws : 1,
330330
ImplicitlyAsync : 1,
331331
ImplicitlyThrows : 1,
332332
NoAsync : 1,
@@ -1180,6 +1180,11 @@ class DeclRefExpr : public Expr {
11801180
DeclNameLoc Loc;
11811181
ActorIsolation implicitActorHopTarget;
11821182

1183+
/// Destination information for a thrown error, which includes any
1184+
/// necessary conversions from the actual type thrown to the type that
1185+
/// is expected by the enclosing context.
1186+
ThrownErrorDestination ThrowDest;
1187+
11831188
public:
11841189
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
11851190
AccessSemantics semantics = AccessSemantics::Ordinary,
@@ -1230,6 +1235,14 @@ class DeclRefExpr : public Expr {
12301235
return Bits.DeclRefExpr.IsImplicitlyThrows;
12311236
}
12321237

1238+
/// The error thrown from this access.
1239+
ThrownErrorDestination throws() const { return ThrowDest; }
1240+
1241+
void setThrows(ThrownErrorDestination throws) {
1242+
assert(!ThrowDest);
1243+
ThrowDest = throws;
1244+
}
1245+
12331246
/// Set whether this reference must account for a `throw` occurring for reasons
12341247
/// other than the function implementation itself throwing, e.g. an
12351248
/// `DistributedActorSystem` implementing a `distributed func` call throwing a
@@ -1562,6 +1575,11 @@ class LookupExpr : public Expr {
15621575
assert(Base);
15631576
}
15641577

1578+
/// Destination information for a thrown error, which includes any
1579+
/// necessary conversions from the actual type thrown to the type that
1580+
/// is expected by the enclosing context.
1581+
ThrownErrorDestination ThrowDest;
1582+
15651583
public:
15661584
/// Retrieve the base of the expression.
15671585
Expr *getBase() const { return Base; }
@@ -1603,6 +1621,14 @@ class LookupExpr : public Expr {
16031621
implicitActorHopTarget = target;
16041622
}
16051623

1624+
/// The error thrown from this access.
1625+
ThrownErrorDestination throws() const { return ThrowDest; }
1626+
1627+
void setThrows(ThrownErrorDestination throws) {
1628+
assert(!ThrowDest);
1629+
ThrowDest = throws;
1630+
}
1631+
16061632
/// Determine whether this reference needs may implicitly throw.
16071633
///
16081634
/// This is the case for non-throwing `distributed func` declarations,
@@ -4627,6 +4653,11 @@ class ApplyExpr : public Expr {
46274653
// isolations in this struct.
46284654
llvm::Optional<ApplyIsolationCrossing> IsolationCrossing;
46294655

4656+
/// Destination information for a thrown error, which includes any
4657+
/// necessary conversions from the actual type thrown to the type that
4658+
/// is expected by the enclosing context.
4659+
ThrownErrorDestination ThrowDest;
4660+
46304661
protected:
46314662
ApplyExpr(ExprKind kind, Expr *fn, ArgumentList *argList, bool implicit,
46324663
Type ty = Type())
@@ -4656,16 +4687,18 @@ class ApplyExpr : public Expr {
46564687
/// Does this application throw? This is only meaningful after
46574688
/// complete type-checking.
46584689
///
4659-
/// If true, the function expression must have a throwing function
4660-
/// type. The converse is not true because of 'rethrows' functions.
4661-
bool throws() const {
4690+
/// Returns the thrown error destination, which includes both the type
4691+
/// thrown from this application as well as the the context's error type,
4692+
/// which may be different.
4693+
ThrownErrorDestination throws() const {
46624694
assert(Bits.ApplyExpr.ThrowsIsSet);
4663-
return Bits.ApplyExpr.Throws;
4695+
return ThrowDest;
46644696
}
4665-
void setThrows(bool throws) {
4697+
4698+
void setThrows(ThrownErrorDestination throws) {
46664699
assert(!Bits.ApplyExpr.ThrowsIsSet);
46674700
Bits.ApplyExpr.ThrowsIsSet = true;
4668-
Bits.ApplyExpr.Throws = throws;
4701+
ThrowDest = throws;
46694702
}
46704703

46714704
/// Is this a 'rethrows' function that is known not to throw?

include/swift/AST/Stmt.h

+11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/ConcreteDeclRef.h"
2525
#include "swift/AST/IfConfigClause.h"
2626
#include "swift/AST/TypeAlignments.h"
27+
#include "swift/AST/ThrownErrorDestination.h"
2728
#include "swift/Basic/Debug.h"
2829
#include "swift/Basic/NullablePtr.h"
2930
#include "llvm/ADT/None.h"
@@ -1383,6 +1384,7 @@ class DoCatchStmt final
13831384

13841385
SourceLoc DoLoc;
13851386
Stmt *Body;
1387+
ThrownErrorDestination RethrowDest;
13861388

13871389
DoCatchStmt(LabeledStmtInfo labelInfo, SourceLoc doLoc, Stmt *body,
13881390
ArrayRef<CaseStmt *> catches, llvm::Optional<bool> implicit)
@@ -1433,6 +1435,15 @@ class DoCatchStmt final
14331435
// rethrown.
14341436
Type getCaughtErrorType() const;
14351437

1438+
/// Retrieves the rethrown error and its conversion to the error type
1439+
/// expected by the enclosing context.
1440+
ThrownErrorDestination rethrows() const { return RethrowDest; }
1441+
1442+
void setRethrows(ThrownErrorDestination rethrows) {
1443+
assert(!RethrowDest);
1444+
RethrowDest = rethrows;
1445+
}
1446+
14361447
static bool classof(const Stmt *S) {
14371448
return S->getKind() == StmtKind::DoCatch;
14381449
}
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===--- ThrownErrorDestination.h - Thrown error dest -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the ThrownErrorDestination class.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_AST_THROWNERRORDESTINATION
18+
#define SWIFT_AST_THROWNERRORDESTINATION
19+
20+
#include "llvm/ADT/Optional.h"
21+
#include "llvm/ADT/PointerUnion.h"
22+
#include "swift/AST/Type.h"
23+
#include "swift/AST/TypeAlignments.h"
24+
25+
namespace swift {
26+
class Expr;
27+
class OpaqueValueExpr;
28+
29+
/// Describes an error thrown from a particular operation and its conversion
30+
/// to the error type expected by its context.
31+
class ThrownErrorDestination {
32+
struct Conversion {
33+
/// The opaque value, which is of the thrown error type.
34+
OpaqueValueExpr *thrownError;
35+
36+
/// The conversion from the opaque value type to the type needed by the
37+
/// context.
38+
Expr *conversion;
39+
};
40+
41+
llvm::PointerUnion<TypeBase *, Conversion *> storage;
42+
43+
ThrownErrorDestination(Type type) : storage(type.getPointer()) { }
44+
ThrownErrorDestination(Conversion *conversion) : storage(conversion) { }
45+
46+
public:
47+
/// Construct a non-throwing destination.
48+
ThrownErrorDestination() : storage(nullptr) { }
49+
50+
/// Construct a non-throwing destination.
51+
ThrownErrorDestination(std::nullptr_t) : storage(nullptr) { }
52+
53+
/// Whether there is a thrown error destination at all.
54+
explicit operator bool() const { return !storage.isNull(); }
55+
56+
/// A thrown error destination where the thrown type corresponds to the
57+
/// type caught (or rethrows) by the enclosing context.
58+
static ThrownErrorDestination forMatchingContextType(Type thrownError) {
59+
return ThrownErrorDestination(thrownError);
60+
}
61+
62+
/// A thrown error destination where the thrown error requires a conversion
63+
/// to the error type expected by its context.
64+
static ThrownErrorDestination forConversion(OpaqueValueExpr *thrownError,
65+
Expr *conversion);
66+
67+
/// Retrieve the type of error thrown from this function call.
68+
Type getThrownErrorType() const;
69+
70+
/// Retrieve the type of the error expected in this context.
71+
Type getContextErrorType() const;
72+
73+
/// Retrieve the conversion as a pair of (opaque thrown error value,
74+
/// conversion expression), when a conversion from the thrown error type
75+
/// to the context error type is required.
76+
llvm::Optional<std::pair<OpaqueValueExpr *, Expr *>> getConversion() const {
77+
if (auto conversion = storage.dyn_cast<Conversion *>()) {
78+
return std::make_pair(conversion->thrownError, conversion->conversion);
79+
}
80+
81+
return llvm::None;
82+
}
83+
};
84+
85+
} // end namespace swift
86+
87+
#endif // SWIFT_AST_THROWNERRORDESTINATION

lib/AST/ASTDumper.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,33 @@ namespace {
893893
printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label,
894894
Color);
895895
}
896+
897+
void printThrowDest(ThrownErrorDestination throws, bool wantNothrow) {
898+
if (!throws) {
899+
if (wantNothrow)
900+
printFlag("nothrow", ExprModifierColor);
901+
902+
return;
903+
}
904+
905+
auto thrownError = throws.getThrownErrorType();
906+
auto contextError = throws.getContextErrorType();
907+
if (thrownError->isEqual(contextError)) {
908+
// No translation of the thrown error type is required, so ony print
909+
// the thrown error type.
910+
Type errorExistentialType =
911+
contextError->getASTContext().getErrorExistentialType();
912+
if (errorExistentialType && thrownError->isEqual(errorExistentialType))
913+
printFlag("throws", ExprModifierColor);
914+
else {
915+
printFlag("throws(" + thrownError.getString() + ")", ExprModifierColor);
916+
}
917+
return;
918+
}
919+
920+
printFlag("throws(" + thrownError.getString() + " to " +
921+
contextError.getString() + ")", ExprModifierColor);
922+
}
896923
};
897924

898925
class PrintPattern : public PatternVisitor<PrintPattern, void, StringRef>,
@@ -2021,6 +2048,7 @@ class PrintStmt : public StmtVisitor<PrintStmt, void, StringRef>,
20212048

20222049
void visitDoCatchStmt(DoCatchStmt *S, StringRef label) {
20232050
printCommon(S, "do_catch_stmt", label);
2051+
printThrowDest(S->rethrows(), /*wantNothrow=*/true);
20242052
printRec(S->getBody(), "body");
20252053
printRecRange(S->getCatches(), Ctx, "catch_stmts");
20262054
printFoot();
@@ -2208,6 +2236,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
22082236

22092237
void visitDeclRefExpr(DeclRefExpr *E, StringRef label) {
22102238
printCommon(E, "declref_expr", label);
2239+
printThrowDest(E->throws(), /*wantNothrow=*/false);
22112240

22122241
printDeclRefField(E->getDeclRef(), "decl");
22132242
if (E->getAccessSemantics() != AccessSemantics::Ordinary)
@@ -2279,6 +2308,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
22792308

22802309
void visitMemberRefExpr(MemberRefExpr *E, StringRef label) {
22812310
printCommon(E, "member_ref_expr", label);
2311+
printThrowDest(E->throws(), /*wantNothrow=*/false);
22822312

22832313
printDeclRefField(E->getMember(), "decl");
22842314
if (E->getAccessSemantics() != AccessSemantics::Ordinary)
@@ -2290,6 +2320,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
22902320
}
22912321
void visitDynamicMemberRefExpr(DynamicMemberRefExpr *E, StringRef label) {
22922322
printCommon(E, "dynamic_member_ref_expr", label);
2323+
printThrowDest(E->throws(), /*wantNothrow=*/false);
22932324

22942325
printDeclRefField(E->getMember(), "decl");
22952326

@@ -2389,6 +2420,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
23892420
}
23902421
void visitSubscriptExpr(SubscriptExpr *E, StringRef label) {
23912422
printCommon(E, "subscript_expr", label);
2423+
printThrowDest(E->throws(), /*wantNothrow=*/false);
23922424

23932425
if (E->getAccessSemantics() != AccessSemantics::Ordinary)
23942426
printFlag(getDumpString(E->getAccessSemantics()), AccessLevelColor);
@@ -2410,6 +2442,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
24102442
}
24112443
void visitDynamicSubscriptExpr(DynamicSubscriptExpr *E, StringRef label) {
24122444
printCommon(E, "dynamic_subscript_expr", label);
2445+
printThrowDest(E->throws(), /*wantNothrow=*/false);
24132446

24142447
printDeclRefField(E->getMember(), "decl");
24152448

@@ -2798,7 +2831,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
27982831
void printApplyExpr(ApplyExpr *E, const char *NodeName, StringRef label) {
27992832
printCommon(E, NodeName, label);
28002833
if (E->isThrowsSet()) {
2801-
printFlag(E->throws() ? "throws" : "nothrow", ExprModifierColor);
2834+
printThrowDest(E->throws(), /*wantNothrow=*/true);
28022835
}
28032836
printFieldQuotedRaw([&](raw_ostream &OS) {
28042837
auto isolationCrossing = E->getIsolationCrossing();

lib/AST/Expr.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -2860,3 +2860,36 @@ Type Expr::findOriginalType() const {
28602860

28612861
return expr->getType()->getRValueType();
28622862
}
2863+
2864+
ThrownErrorDestination
2865+
ThrownErrorDestination::forConversion(OpaqueValueExpr *thrownError,
2866+
Expr *conversion) {
2867+
ASTContext &ctx = thrownError->getType()->getASTContext();
2868+
auto conversionStorage = ctx.Allocate<Conversion>();
2869+
conversionStorage->thrownError = thrownError;
2870+
conversionStorage->conversion = conversion;
2871+
2872+
return ThrownErrorDestination(conversionStorage);
2873+
}
2874+
2875+
Type ThrownErrorDestination::getThrownErrorType() const {
2876+
if (!*this)
2877+
return Type();
2878+
2879+
if (auto type = storage.dyn_cast<TypeBase *>())
2880+
return Type(type);
2881+
2882+
auto conversion = storage.get<Conversion *>();
2883+
return conversion->thrownError->getType();
2884+
}
2885+
2886+
Type ThrownErrorDestination::getContextErrorType() const {
2887+
if (!*this)
2888+
return Type();
2889+
2890+
if (auto type = storage.dyn_cast<TypeBase *>())
2891+
return Type(type);
2892+
2893+
auto conversion = storage.get<Conversion *>();
2894+
return conversion->conversion->getType();
2895+
}

0 commit comments

Comments
 (0)