Skip to content

[Typed throws] Record thrown error types and conversions in the AST #69376

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
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
49 changes: 41 additions & 8 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "swift/AST/FreestandingMacroExpansion.h"
#include "swift/AST/FunctionRefKind.h"
#include "swift/AST/ProtocolConformanceRef.h"
#include "swift/AST/ThrownErrorDestination.h"
#include "swift/AST/TypeAlignments.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/InlineBitfield.h"
Expand Down Expand Up @@ -324,9 +325,8 @@ class alignas(8) Expr : public ASTAllocated<Expr> {
NumCaptures : 32
);

SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ApplyExpr, Expr, 1+1+1+1+1,
ThrowsIsSet : 1,
Throws : 1,
ImplicitlyAsync : 1,
ImplicitlyThrows : 1,
NoAsync : 1,
Expand Down Expand Up @@ -1180,6 +1180,11 @@ class DeclRefExpr : public Expr {
DeclNameLoc Loc;
ActorIsolation implicitActorHopTarget;

/// Destination information for a thrown error, which includes any
/// necessary conversions from the actual type thrown to the type that
/// is expected by the enclosing context.
ThrownErrorDestination ThrowDest;

public:
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
AccessSemantics semantics = AccessSemantics::Ordinary,
Expand Down Expand Up @@ -1230,6 +1235,14 @@ class DeclRefExpr : public Expr {
return Bits.DeclRefExpr.IsImplicitlyThrows;
}

/// The error thrown from this access.
ThrownErrorDestination throws() const { return ThrowDest; }

void setThrows(ThrownErrorDestination throws) {
assert(!ThrowDest);
ThrowDest = throws;
}

/// Set whether this reference must account for a `throw` occurring for reasons
/// other than the function implementation itself throwing, e.g. an
/// `DistributedActorSystem` implementing a `distributed func` call throwing a
Expand Down Expand Up @@ -1562,6 +1575,11 @@ class LookupExpr : public Expr {
assert(Base);
}

/// Destination information for a thrown error, which includes any
/// necessary conversions from the actual type thrown to the type that
/// is expected by the enclosing context.
ThrownErrorDestination ThrowDest;

public:
/// Retrieve the base of the expression.
Expr *getBase() const { return Base; }
Expand Down Expand Up @@ -1603,6 +1621,14 @@ class LookupExpr : public Expr {
implicitActorHopTarget = target;
}

/// The error thrown from this access.
ThrownErrorDestination throws() const { return ThrowDest; }

void setThrows(ThrownErrorDestination throws) {
assert(!ThrowDest);
ThrowDest = throws;
}

/// Determine whether this reference needs may implicitly throw.
///
/// This is the case for non-throwing `distributed func` declarations,
Expand Down Expand Up @@ -4627,6 +4653,11 @@ class ApplyExpr : public Expr {
// isolations in this struct.
llvm::Optional<ApplyIsolationCrossing> IsolationCrossing;

/// Destination information for a thrown error, which includes any
/// necessary conversions from the actual type thrown to the type that
/// is expected by the enclosing context.
ThrownErrorDestination ThrowDest;

protected:
ApplyExpr(ExprKind kind, Expr *fn, ArgumentList *argList, bool implicit,
Type ty = Type())
Expand Down Expand Up @@ -4656,16 +4687,18 @@ class ApplyExpr : public Expr {
/// Does this application throw? This is only meaningful after
/// complete type-checking.
///
/// If true, the function expression must have a throwing function
/// type. The converse is not true because of 'rethrows' functions.
bool throws() const {
/// Returns the thrown error destination, which includes both the type
/// thrown from this application as well as the the context's error type,
/// which may be different.
ThrownErrorDestination throws() const {
assert(Bits.ApplyExpr.ThrowsIsSet);
return Bits.ApplyExpr.Throws;
return ThrowDest;
}
void setThrows(bool throws) {

void setThrows(ThrownErrorDestination throws) {
assert(!Bits.ApplyExpr.ThrowsIsSet);
Bits.ApplyExpr.ThrowsIsSet = true;
Bits.ApplyExpr.Throws = throws;
ThrowDest = throws;
}

/// Is this a 'rethrows' function that is known not to throw?
Expand Down
11 changes: 11 additions & 0 deletions include/swift/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/IfConfigClause.h"
#include "swift/AST/TypeAlignments.h"
#include "swift/AST/ThrownErrorDestination.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/NullablePtr.h"
#include "llvm/ADT/None.h"
Expand Down Expand Up @@ -1383,6 +1384,7 @@ class DoCatchStmt final

SourceLoc DoLoc;
Stmt *Body;
ThrownErrorDestination RethrowDest;

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

/// Retrieves the rethrown error and its conversion to the error type
/// expected by the enclosing context.
ThrownErrorDestination rethrows() const { return RethrowDest; }

void setRethrows(ThrownErrorDestination rethrows) {
assert(!RethrowDest);
RethrowDest = rethrows;
}

static bool classof(const Stmt *S) {
return S->getKind() == StmtKind::DoCatch;
}
Expand Down
87 changes: 87 additions & 0 deletions include/swift/AST/ThrownErrorDestination.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//===--- ThrownErrorDestination.h - Thrown error dest -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ThrownErrorDestination class.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_THROWNERRORDESTINATION
#define SWIFT_AST_THROWNERRORDESTINATION

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeAlignments.h"

namespace swift {
class Expr;
class OpaqueValueExpr;

/// Describes an error thrown from a particular operation and its conversion
/// to the error type expected by its context.
class ThrownErrorDestination {
struct Conversion {
/// The opaque value, which is of the thrown error type.
OpaqueValueExpr *thrownError;

/// The conversion from the opaque value type to the type needed by the
/// context.
Expr *conversion;
};

llvm::PointerUnion<TypeBase *, Conversion *> storage;

ThrownErrorDestination(Type type) : storage(type.getPointer()) { }
ThrownErrorDestination(Conversion *conversion) : storage(conversion) { }

public:
/// Construct a non-throwing destination.
ThrownErrorDestination() : storage(nullptr) { }

/// Construct a non-throwing destination.
ThrownErrorDestination(std::nullptr_t) : storage(nullptr) { }

/// Whether there is a thrown error destination at all.
explicit operator bool() const { return !storage.isNull(); }

/// A thrown error destination where the thrown type corresponds to the
/// type caught (or rethrows) by the enclosing context.
static ThrownErrorDestination forMatchingContextType(Type thrownError) {
return ThrownErrorDestination(thrownError);
}

/// A thrown error destination where the thrown error requires a conversion
/// to the error type expected by its context.
static ThrownErrorDestination forConversion(OpaqueValueExpr *thrownError,
Expr *conversion);

/// Retrieve the type of error thrown from this function call.
Type getThrownErrorType() const;

/// Retrieve the type of the error expected in this context.
Type getContextErrorType() const;

/// Retrieve the conversion as a pair of (opaque thrown error value,
/// conversion expression), when a conversion from the thrown error type
/// to the context error type is required.
llvm::Optional<std::pair<OpaqueValueExpr *, Expr *>> getConversion() const {
if (auto conversion = storage.dyn_cast<Conversion *>()) {
return std::make_pair(conversion->thrownError, conversion->conversion);
}

return llvm::None;
}
};

} // end namespace swift

#endif // SWIFT_AST_THROWNERRORDESTINATION
35 changes: 34 additions & 1 deletion lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,33 @@ namespace {
printFieldQuotedRaw([&](raw_ostream &OS) { declRef.dump(OS); }, label,
Color);
}

void printThrowDest(ThrownErrorDestination throws, bool wantNothrow) {
if (!throws) {
if (wantNothrow)
printFlag("nothrow", ExprModifierColor);

return;
}

auto thrownError = throws.getThrownErrorType();
auto contextError = throws.getContextErrorType();
if (thrownError->isEqual(contextError)) {
// No translation of the thrown error type is required, so ony print
// the thrown error type.
Type errorExistentialType =
contextError->getASTContext().getErrorExistentialType();
if (errorExistentialType && thrownError->isEqual(errorExistentialType))
printFlag("throws", ExprModifierColor);
else {
printFlag("throws(" + thrownError.getString() + ")", ExprModifierColor);
}
return;
}

printFlag("throws(" + thrownError.getString() + " to " +
contextError.getString() + ")", ExprModifierColor);
}
};

class PrintPattern : public PatternVisitor<PrintPattern, void, StringRef>,
Expand Down Expand Up @@ -2021,6 +2048,7 @@ class PrintStmt : public StmtVisitor<PrintStmt, void, StringRef>,

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

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

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

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

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

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

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

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

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

Expand Down Expand Up @@ -2798,7 +2831,7 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
void printApplyExpr(ApplyExpr *E, const char *NodeName, StringRef label) {
printCommon(E, NodeName, label);
if (E->isThrowsSet()) {
printFlag(E->throws() ? "throws" : "nothrow", ExprModifierColor);
printThrowDest(E->throws(), /*wantNothrow=*/true);
}
printFieldQuotedRaw([&](raw_ostream &OS) {
auto isolationCrossing = E->getIsolationCrossing();
Expand Down
33 changes: 33 additions & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2860,3 +2860,36 @@ Type Expr::findOriginalType() const {

return expr->getType()->getRValueType();
}

ThrownErrorDestination
ThrownErrorDestination::forConversion(OpaqueValueExpr *thrownError,
Expr *conversion) {
ASTContext &ctx = thrownError->getType()->getASTContext();
auto conversionStorage = ctx.Allocate<Conversion>();
conversionStorage->thrownError = thrownError;
conversionStorage->conversion = conversion;

return ThrownErrorDestination(conversionStorage);
}

Type ThrownErrorDestination::getThrownErrorType() const {
if (!*this)
return Type();

if (auto type = storage.dyn_cast<TypeBase *>())
return Type(type);

auto conversion = storage.get<Conversion *>();
return conversion->thrownError->getType();
}

Type ThrownErrorDestination::getContextErrorType() const {
if (!*this)
return Type();

if (auto type = storage.dyn_cast<TypeBase *>())
return Type(type);

auto conversion = storage.get<Conversion *>();
return conversion->conversion->getType();
}
Loading