blob: 472ad31ab55aaaa40d7e703cf70748e15f23d4f4 [file] [log] [blame]
//===--- 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 "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;
Scope 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 optional;
llvm::Optional<DefaultCaseData> defaultBlockData;
llvm::SmallVector<NormalCaseData, 8> caseDataArray;
public:
SwitchEnumBuilder(SILGenBuilder &builder, SILLocation loc,
ManagedValue optional)
: builder(builder), loc(loc), optional(optional) {}
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