diff --git a/include/swift/SIL/SILType.h b/include/swift/SIL/SILType.h index cb46848f10d..3bb2b0b7193 100644 --- a/include/swift/SIL/SILType.h +++ b/include/swift/SIL/SILType.h @@ -28,8 +28,10 @@ #include "swift/SIL/SILDeclRef.h" namespace swift { - class ASTContext; - class VarDecl; + +class ASTContext; +class VarDecl; +class SILFunction; namespace Lowering { class AbstractionPattern; @@ -462,6 +464,13 @@ public: /// Otherwise directly returns the given type. SILType unwrapAnyOptionalType() const; + /// Wraps one level of optional type. + /// + /// Returns the lowered Optional if the given type is T. + /// + /// \arg F The SILFunction where the SILType is used. + SILType wrapAnyOptionalType(SILFunction &F) const; + /// Returns true if this is the AnyObject SILType; bool isAnyObject() const { return getSwiftRValueType()->isAnyObject(); } diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp index 1cb2a04a140..ed4bfd09f61 100644 --- a/lib/SIL/SILType.cpp +++ b/lib/SIL/SILType.cpp @@ -625,3 +625,13 @@ bool SILModuleConventions::isPassedIndirectlyInSIL(SILType type, SILModule &M) { bool SILFunctionType::isNoReturnFunction() { return getDirectFormalResultsType().getSwiftRValueType()->isUninhabited(); } + +SILType SILType::wrapAnyOptionalType(SILFunction &F) const { + SILModule &M = F.getModule(); + EnumDecl *OptionalDecl = M.getASTContext().getOptionalDecl(OTK_Optional); + BoundGenericType *BoundEnumDecl = + BoundGenericType::get(OptionalDecl, Type(), {getSwiftRValueType()}); + AbstractionPattern Pattern(F.getLoweredFunctionType()->getGenericSignature(), + BoundEnumDecl->getCanonicalType()); + return M.Types.getLoweredType(Pattern, BoundEnumDecl); +} diff --git a/lib/SILGen/CMakeLists.txt b/lib/SILGen/CMakeLists.txt index d120100312b..0e7ee0450eb 100644 --- a/lib/SILGen/CMakeLists.txt +++ b/lib/SILGen/CMakeLists.txt @@ -5,6 +5,7 @@ add_swift_library(swiftSILGen STATIC FormalEvaluation.cpp ManagedValue.cpp RValue.cpp + SwitchCaseFullExpr.cpp SILGen.cpp SILGenApply.cpp SILGenBridging.cpp diff --git a/lib/SILGen/SILGenBuilder.cpp b/lib/SILGen/SILGenBuilder.cpp index 111b28057d1..33b1408b253 100644 --- a/lib/SILGen/SILGenBuilder.cpp +++ b/lib/SILGen/SILGenBuilder.cpp @@ -11,7 +11,11 @@ //===----------------------------------------------------------------------===// #include "SILGenBuilder.h" +#include "ArgumentSource.h" +#include "RValue.h" #include "SILGenFunction.h" +#include "Scope.h" +#include "SwitchCaseFullExpr.h" using namespace swift; using namespace Lowering; @@ -546,12 +550,45 @@ void SILGenBuilder::createCheckedCastBranch(SILLocation loc, bool isExact, trueBlock, falseBlock); } -ManagedValue SILGenBuilder::createUpcast(SILLocation Loc, ManagedValue Original, - SILType Type) { - CleanupCloner Cloner(*this, Original); +ManagedValue SILGenBuilder::createUpcast(SILLocation loc, ManagedValue original, + SILType type) { + CleanupCloner cloner(*this, original); SILValue convertedValue = - SILBuilder::createUpcast(Loc, Original.forward(gen), Type); - return Cloner.clone(convertedValue); + SILBuilder::createUpcast(loc, original.forward(gen), type); + return cloner.clone(convertedValue); +} + +ManagedValue SILGenBuilder::createOptionalSome(SILLocation loc, + ManagedValue arg) { + CleanupCloner cloner(*this, arg); + auto &argTL = getFunction().getTypeLowering(arg.getType()); + SILType optionalType = arg.getType().wrapAnyOptionalType(getFunction()); + if (argTL.isLoadable()) { + SILValue someValue = + SILBuilder::createOptionalSome(loc, arg.forward(gen), optionalType); + return cloner.clone(someValue); + } + + SILValue tempResult = gen.emitTemporaryAllocation(loc, optionalType); + RValue rvalue(gen, loc, arg.getType().getSwiftRValueType(), arg); + ArgumentSource argValue(loc, std::move(rvalue)); + gen.emitInjectOptionalValueInto( + loc, std::move(argValue), tempResult, + getFunction().getTypeLowering(tempResult->getType())); + return ManagedValue::forUnmanaged(tempResult); +} + +ManagedValue SILGenBuilder::createManagedOptionalNone(SILLocation loc, + SILType type) { + if (!type.isAddressOnly(getModule())) { + SILValue noneValue = SILBuilder::createOptionalNone(loc, type); + return ManagedValue::forUnmanaged(noneValue); + } + + SILValue tempResult = gen.emitTemporaryAllocation(loc, type); + gen.emitInjectOptionalNothingInto(loc, tempResult, + gen.F.getTypeLowering(type)); + return ManagedValue::forUnmanaged(tempResult); } ManagedValue SILGenBuilder::createTupleElementAddr(SILLocation Loc, @@ -569,3 +606,84 @@ ManagedValue SILGenBuilder::createTupleElementAddr(SILLocation Loc, SILType Type = Value.getType().getTupleElementType(Index); return createTupleElementAddr(Loc, Value, Index, Type); } + +//===----------------------------------------------------------------------===// +// Switch Enum Builder +//===----------------------------------------------------------------------===// + +void SwitchEnumBuilder::emit() && { + bool isAddressOnly = optional.getType().isAddressOnly(builder.getModule()); + using DeclBlockPair = std::pair; + { + // TODO: We could store the data in CaseBB form and not have to do this. + llvm::SmallVector caseBlocks; + std::transform(caseDataArray.begin(), caseDataArray.end(), + std::back_inserter(caseBlocks), + [](NormalCaseData &caseData) -> DeclBlockPair { + return {caseData.decl, caseData.block}; + }); + SILBasicBlock *defaultBlock = + defaultBlockData ? defaultBlockData->block : nullptr; + if (isAddressOnly) { + builder.createSwitchEnumAddr(loc, optional.getValue(), defaultBlock, + caseBlocks); + } else { + if (optional.getType().isAddress()) { + // TODO: Refactor this into a maybe load. + if (optional.hasCleanup()) { + optional = builder.createLoadTake(loc, optional); + } else { + optional = builder.createLoadCopy(loc, optional); + } + } + builder.createSwitchEnum(loc, optional.forward(getSGF()), defaultBlock, + caseBlocks); + } + } + + for (NormalCaseData &caseData : caseDataArray) { + EnumElementDecl *decl = caseData.decl; + SILBasicBlock *caseBlock = caseData.block; + NullablePtr contBlock = caseData.contBlock; + NormalCaseHandler handler = caseData.handler; + + // Don't allow cleanups to escape the conditional block. + SwitchCaseFullExpr presentScope(builder.getSILGenFunction(), + CleanupLocation::get(loc), + contBlock.getPtrOrNull()); + builder.emitBlock(caseBlock); + + ManagedValue input; + if (decl->getArgumentInterfaceType()) { + // Pull the payload out if we have one. + SILType inputType = + optional.getType().getEnumElementType(decl, builder.getModule()); + input = optional; + if (!isAddressOnly) { + input = builder.createOwnedPHIArgument(inputType); + } + } + handler(input, presentScope); + assert(!builder.hasValidInsertionPoint()); + } + + // If we have a default BB, create an argument for the original loaded value + // and destroy it there. + if (defaultBlockData) { + SILBasicBlock *defaultBlock = defaultBlockData->block; + NullablePtr contBB = defaultBlockData->contBlock; + DefaultCaseHandler handler = defaultBlockData->handler; + + // Don't allow cleanups to escape the conditional block. + SwitchCaseFullExpr presentScope(builder.getSILGenFunction(), + CleanupLocation::get(loc), + contBB.getPtrOrNull()); + builder.emitBlock(defaultBlock); + ManagedValue input = optional; + if (!isAddressOnly) { + input = builder.createOwnedPHIArgument(optional.getType()); + } + handler(input, presentScope); + assert(!builder.hasValidInsertionPoint()); + } +} diff --git a/lib/SILGen/SILGenBuilder.h b/lib/SILGen/SILGenBuilder.h index 4407e0d1ecb..64050067097 100644 --- a/lib/SILGen/SILGenBuilder.h +++ b/lib/SILGen/SILGenBuilder.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SILGEN_SILGENBUILDER_H #define SWIFT_SILGEN_SILGENBUILDER_H +#include "JumpDest.h" #include "ManagedValue.h" #include "swift/SIL/SILBuilder.h" @@ -235,6 +236,86 @@ public: using SILBuilder::createUpcast; ManagedValue createUpcast(SILLocation loc, ManagedValue original, SILType type); + + using SILBuilder::createUncheckedRefCast; + ManagedValue createUncheckedRefCast(SILLocation loc, ManagedValue original, + SILType type); + + using SILBuilder::createOpenExistentialRef; + ManagedValue createOpenExistentialRef(SILLocation loc, ManagedValue arg, + SILType openedType); + + using SILBuilder::createOptionalSome; + ManagedValue createOptionalSome(SILLocation Loc, ManagedValue Arg); + ManagedValue createManagedOptionalNone(SILLocation Loc, SILType Type); +}; + +class SwitchCaseFullExpr; + +/// 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. +class SwitchEnumBuilder { +public: + using NormalCaseHandler = + std::function; + using DefaultCaseHandler = + std::function; + +private: + struct NormalCaseData { + EnumElementDecl *decl; + SILBasicBlock *block; + NullablePtr contBlock; + NormalCaseHandler handler; + + NormalCaseData(EnumElementDecl *decl, SILBasicBlock *block, + NullablePtr contBlock, + NormalCaseHandler handler) + : decl(decl), block(block), contBlock(contBlock), handler(handler) {} + ~NormalCaseData() = default; + }; + + struct DefaultCaseData { + SILBasicBlock *block; + NullablePtr contBlock; + DefaultCaseHandler handler; + + DefaultCaseData(SILBasicBlock *block, NullablePtr contBlock, + DefaultCaseHandler handler) + : block(block), contBlock(contBlock), handler(handler) {} + ~DefaultCaseData() = default; + }; + + SILGenBuilder &builder; + SILLocation loc; + ManagedValue optional; + llvm::Optional defaultBlockData; + llvm::SmallVector caseDataArray; + +public: + SwitchEnumBuilder(SILGenBuilder &builder, SILLocation loc, + ManagedValue optional) + : builder(builder), loc(loc), optional(optional) {} + + void addDefaultCase(SILBasicBlock *defaultBlock, + NullablePtr contBlock, + DefaultCaseHandler handle) { + defaultBlockData.emplace(defaultBlock, contBlock, handle); + } + + void addCase(EnumElementDecl *decl, SILBasicBlock *caseBlock, + NullablePtr contBlock, NormalCaseHandler handle) { + caseDataArray.emplace_back(decl, caseBlock, contBlock, handle); + } + + void emit() &&; + +private: + SILGenFunction &getSGF() const { return builder.getSILGenFunction(); } }; } // namespace Lowering diff --git a/lib/SILGen/Scope.h b/lib/SILGen/Scope.h index 21068786ef5..ccc53abc8ea 100644 --- a/lib/SILGen/Scope.h +++ b/lib/SILGen/Scope.h @@ -62,6 +62,8 @@ public: ~Scope() { if (Depth.isValid()) popImpl(); } + + bool isValid() const { return Depth.isValid(); } }; /// A FullExpr is a RAII object recording that a full-expression has diff --git a/lib/SILGen/SwitchCaseFullExpr.cpp b/lib/SILGen/SwitchCaseFullExpr.cpp new file mode 100644 index 00000000000..5ca8477a68e --- /dev/null +++ b/lib/SILGen/SwitchCaseFullExpr.cpp @@ -0,0 +1,34 @@ +//===--- SwitchCaseFullExpr.cpp -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "SwitchCaseFullExpr.h" +#include "SILGenFunction.h" +#include "Scope.h" +#include "swift/SIL/SILLocation.h" + +using namespace swift; +using namespace Lowering; + +SwitchCaseFullExpr::SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc, + SILBasicBlock *contBlock) + : SGF(SGF), scope(SGF.Cleanups, loc), loc(loc), + contBlock(contBlock ? *contBlock : *SGF.B.splitBlockForFallthrough()) {} + +SwitchCaseFullExpr::~SwitchCaseFullExpr() { + assert(!scope.isValid() && "Switch Case Full Expr was not popped?!"); +} + +void SwitchCaseFullExpr::exit(SILLocation loc, ArrayRef branchArgs) { + assert(SGF.B.hasValidInsertionPoint()); + scope.pop(); + SGF.B.createBranch(loc, &contBlock, branchArgs); +} diff --git a/lib/SILGen/SwitchCaseFullExpr.h b/lib/SILGen/SwitchCaseFullExpr.h new file mode 100644 index 00000000000..9a90fceb92e --- /dev/null +++ b/lib/SILGen/SwitchCaseFullExpr.h @@ -0,0 +1,50 @@ +//===--- SwitchCaseFullExpr.h ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILGEN_SWITCHCASEFULLEXPR_H +#define SWIFT_SILGEN_SWITCHCASEFULLEXPR_H + +#include "Scope.h" + +namespace swift { +namespace Lowering { + +class SILGenFunction; + +/// 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; + SILBasicBlock &contBlock; + +public: + explicit SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc, + SILBasicBlock *contBlock = nullptr); + + ~SwitchCaseFullExpr(); + + SwitchCaseFullExpr(const SwitchCaseFullExpr &) = delete; + SwitchCaseFullExpr &operator=(const SwitchCaseFullExpr &) = delete; + + void exit(SILLocation loc, ArrayRef result = {}); +}; + +} // namespace Lowering +} // namespace swift + +#endif