| //===--- SwitchEnumBuilder.h ----------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_SILGEN_SWITCHENUMBUILDER_H |
| #define SWIFT_SILGEN_SWITCHENUMBUILDER_H |
| |
| #include "ArgumentScope.h" |
| #include "Scope.h" |
| |
| namespace swift { |
| namespace Lowering { |
| |
| class SILGenFunction; |
| |
| /// TODO: std::variant. |
| struct SwitchCaseBranchDest { |
| Optional<JumpDest> jumpDest; |
| NullablePtr<SILBasicBlock> block; |
| |
| SwitchCaseBranchDest() : jumpDest(), block() {} |
| |
| /// Implicit conversion. |
| SwitchCaseBranchDest(JumpDest jumpDest) : jumpDest(jumpDest), block() {} |
| |
| /// Implicit conversion. |
| SwitchCaseBranchDest(SILBasicBlock *block) : jumpDest(), block(block) {} |
| |
| explicit operator bool() const { |
| return jumpDest.hasValue() || block.isNonNull(); |
| } |
| |
| bool hasJumpDest() const { return jumpDest.hasValue(); } |
| bool hasBlock() const { return bool(block); } |
| |
| SILBasicBlock *getBlock() { return block.getPtrOrNull(); } |
| JumpDest &getJumpDest() { return jumpDest.getValue(); } |
| }; |
| |
| /// A cleanup scope RAII object, like FullExpr, that comes with a JumpDest for a |
| /// continuation block. It is intended to be used to handle switch cases. |
| /// |
| /// You *must* call exit() at some point. |
| /// |
| /// This scope is also exposed to the debug info. |
| class SwitchCaseFullExpr { |
| SILGenFunction &SGF; |
| ArgumentScope scope; |
| CleanupLocation loc; |
| SwitchCaseBranchDest branchDest; |
| |
| public: |
| SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc); |
| SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc, |
| SwitchCaseBranchDest branchDest); |
| |
| ~SwitchCaseFullExpr(); |
| |
| SwitchCaseFullExpr(const SwitchCaseFullExpr &) = delete; |
| SwitchCaseFullExpr &operator=(const SwitchCaseFullExpr &) = delete; |
| |
| /// Pop the scope and branch to the branch destination. If this case has an |
| /// associated JumpDest as its branch destination, then this will cause |
| /// cleanups associated with the jump dest to be emitted. |
| void exitAndBranch(SILLocation loc, ArrayRef<SILValue> result = {}); |
| |
| /// Pop the scope and do not branch to the branch destination. This is |
| /// intended to be used in situations where one wants to model a switch region |
| /// that ends midway through one of the case blocks. |
| void exit(); |
| |
| /// Do not pop the scope and do not branch to the branch destination. But do |
| /// invalidate the scope. This can occur when emitting unreachables. |
| void unreachableExit(); |
| }; |
| |
| /// A class for building switch enums that handles all of the ownership |
| /// requirements for the user. |
| /// |
| /// It assumes that the user passes in a block that takes in a ManagedValue and |
| /// returns a ManagedValue for the blocks exit argument. Should return an empty |
| /// ManagedValue to signal no result. |
| /// |
| /// TODO: Allow cases to take JumpDest as continuation blocks and then |
| /// force exitBranchAndCleanup to be run. |
| class SwitchEnumBuilder { |
| public: |
| using NormalCaseHandler = |
| std::function<void(ManagedValue, SwitchCaseFullExpr &&)>; |
| using DefaultCaseHandler = |
| std::function<void(ManagedValue, SwitchCaseFullExpr &&)>; |
| |
| enum class DefaultDispatchTime { BeforeNormalCases, AfterNormalCases }; |
| |
| private: |
| struct NormalCaseData { |
| EnumElementDecl *decl; |
| SILBasicBlock *block; |
| SwitchCaseBranchDest branchDest; |
| NormalCaseHandler handler; |
| ProfileCounter count; |
| |
| NormalCaseData(EnumElementDecl *decl, SILBasicBlock *block, |
| SwitchCaseBranchDest branchDest, NormalCaseHandler handler, |
| ProfileCounter count) |
| : decl(decl), block(block), branchDest(branchDest), handler(handler), |
| count(count) {} |
| ~NormalCaseData() = default; |
| }; |
| |
| struct DefaultCaseData { |
| SILBasicBlock *block; |
| SwitchCaseBranchDest branchDest; |
| DefaultCaseHandler handler; |
| DefaultDispatchTime dispatchTime; |
| ProfileCounter count; |
| |
| DefaultCaseData(SILBasicBlock *block, SwitchCaseBranchDest branchDest, |
| DefaultCaseHandler handler, |
| DefaultDispatchTime dispatchTime, ProfileCounter count) |
| : block(block), branchDest(branchDest), handler(handler), |
| dispatchTime(dispatchTime), count(count) {} |
| ~DefaultCaseData() = default; |
| }; |
| |
| SILGenBuilder &builder; |
| SILLocation loc; |
| ManagedValue subjectExprOperand; |
| llvm::Optional<DefaultCaseData> defaultBlockData; |
| llvm::SmallVector<NormalCaseData, 8> caseDataArray; |
| |
| public: |
| SwitchEnumBuilder(SILGenBuilder &builder, SILLocation loc, |
| ManagedValue subjectExprOperand) |
| : builder(builder), loc(loc), subjectExprOperand(subjectExprOperand) {} |
| |
| void addDefaultCase( |
| SILBasicBlock *defaultBlock, SwitchCaseBranchDest branchDest, |
| DefaultCaseHandler handle, |
| DefaultDispatchTime dispatchTime = DefaultDispatchTime::AfterNormalCases, |
| ProfileCounter count = ProfileCounter()) { |
| defaultBlockData.emplace(defaultBlock, branchDest, handle, dispatchTime, |
| count); |
| } |
| |
| void addCase(EnumElementDecl *decl, SILBasicBlock *caseBlock, |
| SwitchCaseBranchDest branchDest, NormalCaseHandler handle, |
| ProfileCounter count = ProfileCounter()) { |
| caseDataArray.emplace_back(decl, caseBlock, branchDest, handle, count); |
| } |
| |
| void addOptionalSomeCase(SILBasicBlock *caseBlock) { |
| auto *decl = getSGF().getASTContext().getOptionalSomeDecl(); |
| caseDataArray.emplace_back( |
| decl, caseBlock, nullptr, |
| [](ManagedValue mv, SwitchCaseFullExpr &&expr) { expr.exit(); }, |
| ProfileCounter()); |
| } |
| |
| void addOptionalNoneCase(SILBasicBlock *caseBlock) { |
| auto *decl = getSGF().getASTContext().getOptionalNoneDecl(); |
| caseDataArray.emplace_back( |
| decl, caseBlock, nullptr, |
| [](ManagedValue mv, SwitchCaseFullExpr &&expr) { expr.exit(); }, |
| ProfileCounter()); |
| } |
| |
| void addOptionalSomeCase(SILBasicBlock *caseBlock, |
| SwitchCaseBranchDest branchDest, |
| NormalCaseHandler handle, |
| ProfileCounter count = ProfileCounter()) { |
| auto *decl = getSGF().getASTContext().getOptionalSomeDecl(); |
| caseDataArray.emplace_back(decl, caseBlock, branchDest, handle, count); |
| } |
| |
| void addOptionalNoneCase(SILBasicBlock *caseBlock, |
| SwitchCaseBranchDest branchDest, |
| NormalCaseHandler handle, |
| ProfileCounter count = ProfileCounter()) { |
| auto *decl = getSGF().getASTContext().getOptionalNoneDecl(); |
| caseDataArray.emplace_back(decl, caseBlock, branchDest, handle, count); |
| } |
| |
| void emit() &&; |
| |
| private: |
| SILGenFunction &getSGF() const { return builder.getSILGenFunction(); } |
| }; |
| |
| } // end Lowering namespace |
| } // end swift namespace |
| |
| #endif |