mirror of
https://github.com/apple/swift.git
synced 2026-03-04 18:24:35 +01:00
Merge pull request #9033 from rjmccall/rvalue-pointer-conversions
This commit is contained in:
344
include/swift/Basic/ExternalUnion.h
Normal file
344
include/swift/Basic/ExternalUnion.h
Normal file
@@ -0,0 +1,344 @@
|
||||
//===- ExternalUnion.h - A union with an external discriminator -*- 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 ExternalUnion class, which allows clients to
|
||||
// conveniently define unions of possibly non-trivial types whose
|
||||
// discriminator will be provided externally.
|
||||
//
|
||||
// It's the client's responsibility to call the appropriate
|
||||
// "special members" within its own special members.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_BASIC_EXTERNALUNION_H
|
||||
#define SWIFT_BASIC_EXTERNALUNION_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <assert.h>
|
||||
|
||||
namespace swift {
|
||||
|
||||
template <size_t... Values>
|
||||
struct static_max;
|
||||
|
||||
template <size_t Value>
|
||||
struct static_max<Value> {
|
||||
static const size_t value = Value;
|
||||
};
|
||||
|
||||
template <size_t Head, size_t... Tail>
|
||||
struct static_max<Head, Tail...> {
|
||||
static const size_t value =
|
||||
(Head > static_max<Tail...>::value ? Head : static_max<Tail...>::value);
|
||||
};
|
||||
|
||||
template <class T, class... Members>
|
||||
struct indexOf;
|
||||
|
||||
template <class T>
|
||||
struct indexOf<T> {
|
||||
constexpr static const int value = -1;
|
||||
};
|
||||
|
||||
template <class T, class U, class... Members>
|
||||
struct indexOf<T, U, Members...> {
|
||||
private:
|
||||
constexpr static const int indexInTail = indexOf<T, Members...>::value;
|
||||
public:
|
||||
constexpr static const int value =
|
||||
(std::is_same<T, U>::value ? 0 :
|
||||
indexInTail != -1 ? indexInTail + 1 : -1);
|
||||
};
|
||||
|
||||
template <class... Members>
|
||||
struct SpecialMembers;
|
||||
|
||||
/// An external union whose discriminator is just a position in
|
||||
/// the template arguments.
|
||||
///
|
||||
/// The external union itself is a trivial type, and it is the
|
||||
/// responsibility of the client to call the "special member functions"
|
||||
/// at the appropriate time.
|
||||
template <class... Members>
|
||||
class BasicExternalUnion {
|
||||
/// The value storage.
|
||||
LLVM_ALIGNAS(static_max<alignof(Members)...>::value)
|
||||
char Storage[static_max<sizeof(Members)...>::value];
|
||||
|
||||
public:
|
||||
/// Construct a union member in-place.
|
||||
template <class T, class... Args>
|
||||
T &emplaceWithoutIndex(int index, Args &&... args) {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
|
||||
return *(::new ((void*) &Storage) T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/// Construct a union member in-place.
|
||||
template <class T, class... Args>
|
||||
T &emplace(int index, Args &&... args) {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
assert(index == typeIndex && "current kind is wrong for value");
|
||||
|
||||
return *(::new ((void*) &Storage) T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/// Return a reference to a union member.
|
||||
template <class T>
|
||||
T &getWithoutIndex() {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
|
||||
return reinterpret_cast<T &>(Storage);
|
||||
}
|
||||
|
||||
/// Return a reference to a union member.
|
||||
template <class T>
|
||||
const T &getWithoutIndex() const {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
|
||||
return reinterpret_cast<const T &>(Storage);
|
||||
}
|
||||
|
||||
/// Return a reference to a union member, asserting that the current
|
||||
/// kind matches the type being extracted.
|
||||
template <class T>
|
||||
T &get(int index) {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
assert(index == typeIndex && "current kind is wrong for access");
|
||||
|
||||
return reinterpret_cast<T &>(Storage);
|
||||
}
|
||||
|
||||
/// Return a reference to a union member, asserting that the current
|
||||
/// kind matches the type being extracted.
|
||||
template <class T>
|
||||
const T &get(int index) const {
|
||||
constexpr int typeIndex = indexOf<T, Members...>::value;
|
||||
static_assert(typeIndex != -1, "type not in union");
|
||||
assert(index == typeIndex && "current kind is wrong for access");
|
||||
|
||||
return reinterpret_cast<const T &>(Storage);
|
||||
}
|
||||
|
||||
/// Copy-construct the union from another union.
|
||||
void copyConstruct(int index, const BasicExternalUnion &other) {
|
||||
if (index != -1) {
|
||||
SpecialMembers<Members...>::copyConstruct(Storage, index, other.Storage);
|
||||
}
|
||||
}
|
||||
|
||||
/// Move-construct the union from another union.
|
||||
void moveConstruct(int index, BasicExternalUnion &&other) {
|
||||
if (index != -1) {
|
||||
SpecialMembers<Members...>::moveConstruct(Storage, index, other.Storage);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy-assign the union from another union.
|
||||
void copyAssign(int thisIndex, int otherIndex,
|
||||
const BasicExternalUnion &other) {
|
||||
if (this == &other) {
|
||||
// do nothing
|
||||
} else if (thisIndex == otherIndex) {
|
||||
if (thisIndex != -1) {
|
||||
SpecialMembers<Members...>::copyAssignSame(thisIndex, Storage,
|
||||
other.Storage);
|
||||
}
|
||||
} else {
|
||||
destruct(thisIndex, Storage);
|
||||
copyConstruct(otherIndex, other);
|
||||
}
|
||||
}
|
||||
|
||||
/// Move-assign the union from another union.
|
||||
void moveAssign(int thisIndex, int otherIndex,
|
||||
BasicExternalUnion &&other) {
|
||||
assert(this != &other && "move-constructing value into itself?");
|
||||
|
||||
if (thisIndex == otherIndex) {
|
||||
if (thisIndex != -1) {
|
||||
SpecialMembers<Members...>::moveAssignSame(thisIndex, Storage,
|
||||
other.Storage);
|
||||
}
|
||||
} else {
|
||||
destruct(thisIndex);
|
||||
moveConstruct(otherIndex, std::move(other));
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy the union from another union.
|
||||
void destruct(int index) {
|
||||
if (index != -1) {
|
||||
SpecialMembers<Members...>::destruct(index, Storage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// An external union whose membership is determined by a kind type
|
||||
/// whose members are not necessarily 1-1 with the members of the union.
|
||||
///
|
||||
/// Clients must provide a function which translates the kind type
|
||||
/// into an index into the union's Members list, or -1 for kind values
|
||||
/// which do not require data in the union.
|
||||
template <class Kind, int (&GetIndexForKind)(Kind), class... Members>
|
||||
class ExternalUnion {
|
||||
BasicExternalUnion<Members...> Union;
|
||||
|
||||
public:
|
||||
/// Construct a union member in-place.
|
||||
template <class T, class... Args>
|
||||
T &emplace(Kind kind, Args &&... args) {
|
||||
#ifndef NDEBUG
|
||||
return Union.template emplace<T>(GetIndexForKind(kind),
|
||||
std::forward<Args>(args)...);
|
||||
#else
|
||||
return Union.template emplaceWithoutIndex<T>(std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return a reference to a union member, asserting that the current
|
||||
/// kind is right.
|
||||
template <class T>
|
||||
T &get(Kind kind) {
|
||||
#ifndef NDEBUG
|
||||
return Union.template get<T>(GetIndexForKind(kind));
|
||||
#else
|
||||
return Union.template getWithoutIndex<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Return a reference to a union member, asserting that the current
|
||||
/// kind is right.
|
||||
template <class T>
|
||||
const T &get(Kind kind) const {
|
||||
#ifndef NDEBUG
|
||||
return Union.template get<T>(GetIndexForKind(kind));
|
||||
#else
|
||||
return Union.template getWithoutIndex<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Copy-construct the union from another union.
|
||||
void copyConstruct(Kind kind, const ExternalUnion &other) {
|
||||
Union.copyConstruct(GetIndexForKind(kind), other.Union);
|
||||
}
|
||||
|
||||
/// Move-construct the union from another union.
|
||||
void moveConstruct(Kind kind, ExternalUnion &&other) {
|
||||
Union.moveConstruct(GetIndexForKind(kind), std::move(other.Union));
|
||||
}
|
||||
|
||||
/// Copy-assign the union from another union.
|
||||
void copyAssign(Kind thisKind, Kind otherKind, const ExternalUnion &other) {
|
||||
Union.copyAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind),
|
||||
other.Union);
|
||||
}
|
||||
|
||||
/// Move-assign the union from another union.
|
||||
void moveAssign(Kind thisKind, Kind otherKind, ExternalUnion &&other) {
|
||||
Union.moveAssign(GetIndexForKind(thisKind), GetIndexForKind(otherKind),
|
||||
std::move(other.Union));
|
||||
}
|
||||
|
||||
/// Destroy the union from another union.
|
||||
void destruct(Kind kind) {
|
||||
Union.destruct(GetIndexForKind(kind));
|
||||
}
|
||||
};
|
||||
|
||||
/// A helper class for defining special members.
|
||||
template <>
|
||||
struct SpecialMembers<> {
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void copyConstruct(void *self, int index, const void *other) {
|
||||
llvm_unreachable("bad index");
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void moveConstruct(void *self, int index, void *other) {
|
||||
llvm_unreachable("bad index");
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void copyAssignSame(int index, void *self, const void *other) {
|
||||
llvm_unreachable("bad index");
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void moveAssignSame(int index, void *self, void *other) {
|
||||
llvm_unreachable("bad index");
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void destruct(int index, void *self) {
|
||||
llvm_unreachable("bad index");
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class... Others>
|
||||
struct SpecialMembers<T, Others...> {
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void copyConstruct(void *self, int index, const void *other) {
|
||||
if (index == 0) {
|
||||
::new (self) T(*static_cast<const T *>(other));
|
||||
} else {
|
||||
SpecialMembers<Others...>::copyConstruct(self, index - 1, other);
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void moveConstruct(void *self, int index, void *other) {
|
||||
if (index == 0) {
|
||||
::new (self) T(std::move(*static_cast<T *>(other)));
|
||||
} else {
|
||||
SpecialMembers<Others...>::moveConstruct(self, index - 1, other);
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void copyAssignSame(int index, void *self, const void *other) {
|
||||
if (index == 0) {
|
||||
*static_cast<T*>(self) = *static_cast<const T *>(other);
|
||||
} else {
|
||||
SpecialMembers<Others...>::copyAssignSame(index - 1, self, other);
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void moveAssignSame(int index, void *self, void *other) {
|
||||
if (index == 0) {
|
||||
*static_cast<T*>(self) = std::move(*static_cast<T *>(other));
|
||||
} else {
|
||||
SpecialMembers<Others...>::moveAssignSame(index - 1, self, other);
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
static void destruct(int index, void *self) {
|
||||
if (index == 0) {
|
||||
static_cast<T*>(self)->T::~T();
|
||||
} else {
|
||||
SpecialMembers<Others...>::destruct(index - 1, self);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif // SWIFT_BASIC_CLUSTEREDBITVECTOR_H
|
||||
@@ -521,6 +521,9 @@ public:
|
||||
/// Get the builtin word type as a SILType;
|
||||
static SILType getBuiltinWordType(const ASTContext &C);
|
||||
|
||||
/// Given a value type, return an optional type wrapping it.
|
||||
static SILType getOptionalType(SILType valueType);
|
||||
|
||||
/// Get the standard exception type.
|
||||
static SILType getExceptionType(const ASTContext &C);
|
||||
|
||||
|
||||
@@ -1361,19 +1361,53 @@ public:
|
||||
}
|
||||
|
||||
void updateExprToPointerWhitelist(Expr *Base, Expr *Arg) {
|
||||
auto handleSubExpr = [&](Expr *SubExpr) {
|
||||
// if we have an inject into optional, strip it off.
|
||||
if (auto *InjectIntoOpt = dyn_cast<InjectIntoOptionalExpr>(SubExpr)) {
|
||||
SubExpr = InjectIntoOpt->getSubExpr();
|
||||
auto handleSubExpr = [&](Expr *origSubExpr) {
|
||||
auto subExpr = origSubExpr;
|
||||
unsigned optionalDepth = 0;
|
||||
|
||||
auto checkIsBindOptional = [&](Expr *expr) {
|
||||
for (unsigned depth = optionalDepth; depth; --depth) {
|
||||
if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
|
||||
expr = bind->getSubExpr();
|
||||
} else {
|
||||
Out << "malformed optional pointer conversion\n";
|
||||
origSubExpr->print(Out);
|
||||
Out << '\n';
|
||||
abort();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// These outer entities will be interleaved in multi-level optionals.
|
||||
while (true) {
|
||||
// Look through optional evaluations.
|
||||
if (auto *optionalEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
|
||||
subExpr = optionalEval->getSubExpr();
|
||||
optionalDepth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look through injections into Optional<Pointer>.
|
||||
if (auto *injectIntoOpt = dyn_cast<InjectIntoOptionalExpr>(subExpr)) {
|
||||
subExpr = injectIntoOpt->getSubExpr();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto *InOutToPtr = dyn_cast<InOutToPointerExpr>(SubExpr)) {
|
||||
WhitelistedInOutToPointerExpr.insert(InOutToPtr);
|
||||
// Whitelist inout-to-pointer conversions.
|
||||
if (auto *inOutToPtr = dyn_cast<InOutToPointerExpr>(subExpr)) {
|
||||
WhitelistedInOutToPointerExpr.insert(inOutToPtr);
|
||||
checkIsBindOptional(inOutToPtr->getSubExpr());
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *ArrayToPtr = dyn_cast<ArrayToPointerExpr>(SubExpr)) {
|
||||
WhitelistedArrayToPointerExpr.insert(ArrayToPtr);
|
||||
// Whitelist array-to-pointer conversions.
|
||||
if (auto *arrayToPtr = dyn_cast<ArrayToPointerExpr>(subExpr)) {
|
||||
WhitelistedArrayToPointerExpr.insert(arrayToPtr);
|
||||
checkIsBindOptional(arrayToPtr->getSubExpr());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -64,6 +64,13 @@ SILType SILType::getBuiltinWordType(const ASTContext &C) {
|
||||
return getPrimitiveObjectType(CanType(BuiltinIntegerType::getWordType(C)));
|
||||
}
|
||||
|
||||
SILType SILType::getOptionalType(SILType type) {
|
||||
auto &ctx = type.getSwiftRValueType()->getASTContext();
|
||||
auto optType = BoundGenericEnumType::get(ctx.getOptionalDecl(), Type(),
|
||||
{ type.getSwiftRValueType() });
|
||||
return getPrimitiveType(CanType(optType), type.getCategory());
|
||||
}
|
||||
|
||||
bool SILType::isTrivial(SILModule &M) const {
|
||||
return M.getTypeLowering(*this).isTrivial();
|
||||
}
|
||||
|
||||
@@ -145,6 +145,26 @@ ManagedValue ManagedValue::formalAccessBorrow(SILGenFunction &SGF,
|
||||
return SGF.emitFormalEvaluationManagedBeginBorrow(loc, getValue());
|
||||
}
|
||||
|
||||
ManagedValue ManagedValue::materialize(SILGenFunction &SGF,
|
||||
SILLocation loc) const {
|
||||
auto temporary = SGF.emitTemporaryAllocation(loc, getType());
|
||||
bool hadCleanup = hasCleanup();
|
||||
|
||||
// The temporary memory is +0 if the value was.
|
||||
if (hadCleanup) {
|
||||
SGF.B.emitStoreValueOperation(loc, forward(SGF), temporary,
|
||||
StoreOwnershipQualifier::Init);
|
||||
|
||||
// SEMANTIC SIL TODO: This should really be called a temporary LValue.
|
||||
return ManagedValue::forOwnedAddressRValue(temporary,
|
||||
SGF.enterDestroyCleanup(temporary));
|
||||
} else {
|
||||
auto object = SGF.emitManagedBeginBorrow(loc, getValue());
|
||||
SGF.emitManagedStoreBorrow(loc, object.getValue(), temporary);
|
||||
return ManagedValue::forBorrowedAddressRValue(temporary);
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedValue::print(raw_ostream &os) const {
|
||||
if (SILValue v = getValue()) {
|
||||
v->print(os);
|
||||
|
||||
@@ -270,6 +270,10 @@ public:
|
||||
return isLValue() ? *this : ManagedValue::forUnmanaged(getValue());
|
||||
}
|
||||
|
||||
/// Given a scalar value, materialize it into memory with the
|
||||
/// exact same level of cleanup it had before.
|
||||
ManagedValue materialize(SILGenFunction &SGF, SILLocation loc) const;
|
||||
|
||||
/// Disable the cleanup for this value.
|
||||
void forwardCleanup(SILGenFunction &SGF) const;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "swift/AST/ForeignErrorConvention.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "swift/AST/SubstitutionMap.h"
|
||||
#include "swift/Basic/ExternalUnion.h"
|
||||
#include "swift/Basic/Range.h"
|
||||
#include "swift/Basic/STLExtras.h"
|
||||
#include "swift/Basic/Unicode.h"
|
||||
@@ -1937,7 +1938,32 @@ static CanType claimNextParamClause(CanAnyFunctionType &type) {
|
||||
return result;
|
||||
}
|
||||
|
||||
class InOutArgument {
|
||||
namespace {
|
||||
|
||||
/// The original argument expression for some sort of complex
|
||||
/// argument emission.
|
||||
class OriginalArgument {
|
||||
llvm::PointerIntPair<Expr*, 1, bool> ExprAndIsIndirect;
|
||||
|
||||
public:
|
||||
OriginalArgument() = default;
|
||||
OriginalArgument(Expr *expr, bool indirect)
|
||||
: ExprAndIsIndirect(expr, indirect) {}
|
||||
|
||||
Expr *getExpr() const { return ExprAndIsIndirect.getPointer(); }
|
||||
bool isIndirect() const { return ExprAndIsIndirect.getInt(); }
|
||||
};
|
||||
|
||||
/// A delayed argument. Call arguments are evaluated in two phases:
|
||||
/// a formal evaluation phase and a formal access phase. The primary
|
||||
/// example of this is an l-value that is passed by reference, where
|
||||
/// the access to the l-value does not begin until the formal access
|
||||
/// phase, but there are other examples, generally relating to pointer
|
||||
/// conversions.
|
||||
///
|
||||
/// A DelayedArgument represents the part of evaluating an argument
|
||||
/// that's been delayed until the formal access phase.
|
||||
class DelayedArgument {
|
||||
public:
|
||||
enum KindTy {
|
||||
/// This is a true inout argument.
|
||||
@@ -1949,41 +1975,130 @@ public:
|
||||
/// This is a borrowed indirect argument.
|
||||
BorrowIndirect,
|
||||
|
||||
LastLVKindWithoutExtra = BorrowIndirect,
|
||||
|
||||
/// The l-value needs to be converted to a pointer type.
|
||||
LValueToPointer,
|
||||
|
||||
/// An array l-value needs to be converted to a pointer type.
|
||||
ArrayToPointer,
|
||||
LValueArrayToPointer,
|
||||
|
||||
LastLVKind = LValueArrayToPointer,
|
||||
|
||||
/// An array r-value needs to be converted to a pointer type.
|
||||
RValueArrayToPointer,
|
||||
|
||||
/// A string r-value needs to be converted to a pointer type.
|
||||
RValueStringToPointer,
|
||||
};
|
||||
|
||||
private:
|
||||
KindTy Kind;
|
||||
LValue LV;
|
||||
SILLocation Loc;
|
||||
Expr *OriginalExpr;
|
||||
union {
|
||||
SILGenFunction::PointerAccessInfo PointerInfo;
|
||||
SILGenFunction::ArrayAccessInfo ArrayInfo;
|
||||
|
||||
struct LValueStorage {
|
||||
LValue LV;
|
||||
SILLocation Loc;
|
||||
|
||||
LValueStorage(LValue &&lv, SILLocation loc) : LV(std::move(lv)), Loc(loc) {}
|
||||
};
|
||||
struct RValueStorage {
|
||||
ManagedValue RV;
|
||||
|
||||
RValueStorage(ManagedValue rv) : RV(rv) {}
|
||||
};
|
||||
|
||||
public:
|
||||
InOutArgument(KindTy kind, LValue &&lv, SILLocation loc)
|
||||
: Kind(kind), LV(std::move(lv)), Loc(loc) {}
|
||||
InOutArgument(SILGenFunction::PointerAccessInfo pointerInfo,
|
||||
LValue &&lv, SILLocation loc, Expr *originalExpr)
|
||||
: Kind(LValueToPointer), LV(std::move(lv)), Loc(loc),
|
||||
OriginalExpr(originalExpr) {
|
||||
PointerInfo = pointerInfo;
|
||||
static int getUnionIndexForValue(KindTy kind) {
|
||||
return (kind <= LastLVKind ? 0 : 1);
|
||||
}
|
||||
InOutArgument(SILGenFunction::ArrayAccessInfo arrayInfo,
|
||||
LValue &&lv, SILLocation loc, Expr *originalExpr)
|
||||
: Kind(ArrayToPointer), LV(std::move(lv)), Loc(loc),
|
||||
OriginalExpr(originalExpr) {
|
||||
ArrayInfo = arrayInfo;
|
||||
|
||||
/// Storage for either the l-value or the r-value.
|
||||
ExternalUnion<KindTy, getUnionIndexForValue,
|
||||
LValueStorage, RValueStorage> Value;
|
||||
|
||||
LValueStorage &LV() { return Value.get<LValueStorage>(Kind); }
|
||||
const LValueStorage &LV() const { return Value.get<LValueStorage>(Kind); }
|
||||
RValueStorage &RV() { return Value.get<RValueStorage>(Kind); }
|
||||
const RValueStorage &RV() const { return Value.get<RValueStorage>(Kind); }
|
||||
|
||||
/// The original argument expression, which will be emitted down
|
||||
/// to the point from which the l-value or r-value was generated.
|
||||
OriginalArgument Original;
|
||||
|
||||
using PointerAccessInfo = SILGenFunction::PointerAccessInfo;
|
||||
using ArrayAccessInfo = SILGenFunction::ArrayAccessInfo;
|
||||
static int getUnionIndexForExtra(KindTy kind) {
|
||||
switch (kind) {
|
||||
case LValueToPointer:
|
||||
return 0;
|
||||
case LValueArrayToPointer:
|
||||
case RValueArrayToPointer:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ExternalUnion<KindTy, getUnionIndexForExtra,
|
||||
PointerAccessInfo, ArrayAccessInfo> Extra;
|
||||
|
||||
public:
|
||||
DelayedArgument(KindTy kind, LValue &&lv, SILLocation loc)
|
||||
: Kind(kind) {
|
||||
assert(kind <= LastLVKindWithoutExtra &&
|
||||
"this constructor should only be used for simple l-value kinds");
|
||||
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
|
||||
}
|
||||
|
||||
DelayedArgument(KindTy kind, ManagedValue rv, OriginalArgument original)
|
||||
: Kind(kind), Original(original) {
|
||||
Value.emplace<RValueStorage>(Kind, rv);
|
||||
}
|
||||
|
||||
DelayedArgument(SILGenFunction::PointerAccessInfo pointerInfo,
|
||||
LValue &&lv, SILLocation loc, OriginalArgument original)
|
||||
: Kind(LValueToPointer), Original(original) {
|
||||
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
|
||||
Extra.emplace<PointerAccessInfo>(Kind, pointerInfo);
|
||||
}
|
||||
|
||||
DelayedArgument(SILGenFunction::ArrayAccessInfo arrayInfo,
|
||||
LValue &&lv, SILLocation loc, OriginalArgument original)
|
||||
: Kind(LValueArrayToPointer), Original(original) {
|
||||
Value.emplace<LValueStorage>(Kind, std::move(lv), loc);
|
||||
Extra.emplace<ArrayAccessInfo>(Kind, arrayInfo);
|
||||
}
|
||||
|
||||
DelayedArgument(KindTy kind,
|
||||
SILGenFunction::ArrayAccessInfo arrayInfo,
|
||||
ManagedValue rv, OriginalArgument original)
|
||||
: Kind(kind), Original(original) {
|
||||
Value.emplace<RValueStorage>(Kind, rv);
|
||||
Extra.emplace<ArrayAccessInfo>(Kind, arrayInfo);
|
||||
}
|
||||
|
||||
DelayedArgument(DelayedArgument &&other)
|
||||
: Kind(other.Kind), Original(other.Original) {
|
||||
Value.moveConstruct(Kind, std::move(other.Value));
|
||||
Extra.moveConstruct(Kind, std::move(other.Extra));
|
||||
}
|
||||
|
||||
DelayedArgument &operator=(DelayedArgument &&other) {
|
||||
Value.moveAssign(Kind, other.Kind, std::move(other.Value));
|
||||
Extra.moveAssign(Kind, other.Kind, std::move(other.Extra));
|
||||
Kind = other.Kind;
|
||||
Original = other.Original;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~DelayedArgument() {
|
||||
Extra.destruct(Kind);
|
||||
Value.destruct(Kind);
|
||||
}
|
||||
|
||||
bool isSimpleInOut() const { return Kind == InOut; }
|
||||
SILLocation getLocation() const { return Loc; }
|
||||
SILLocation getInOutLocation() const {
|
||||
assert(isSimpleInOut());
|
||||
return LV().Loc;
|
||||
}
|
||||
|
||||
ManagedValue emit(SILGenFunction &SGF) {
|
||||
switch (Kind) {
|
||||
@@ -1994,9 +2109,10 @@ public:
|
||||
case BorrowIndirect:
|
||||
return emitBorrowIndirect(SGF);
|
||||
case LValueToPointer:
|
||||
return emitLValueToPointer(SGF);
|
||||
case ArrayToPointer:
|
||||
return emitArrayToPointer(SGF);
|
||||
case LValueArrayToPointer:
|
||||
case RValueArrayToPointer:
|
||||
case RValueStringToPointer:
|
||||
return finishOriginalArgument(SGF);
|
||||
}
|
||||
llvm_unreachable("bad kind");
|
||||
}
|
||||
@@ -2012,62 +2128,171 @@ private:
|
||||
|
||||
ManagedValue emitBorrowDirect(SILGenFunction &SGF) {
|
||||
ManagedValue address = emitAddress(SGF, AccessKind::Read);
|
||||
return SGF.B.createLoadBorrow(Loc, address);
|
||||
}
|
||||
|
||||
ManagedValue emitLValueToPointer(SILGenFunction &SGF) {
|
||||
auto value = SGF.emitLValueToPointer(Loc, std::move(LV), PointerInfo);
|
||||
return finishOriginalExpr(SGF, value, OriginalExpr);
|
||||
}
|
||||
|
||||
ManagedValue emitArrayToPointer(SILGenFunction &SGF) {
|
||||
auto value = SGF.emitArrayToPointer(Loc, std::move(LV), ArrayInfo);
|
||||
return finishOriginalExpr(SGF, value, OriginalExpr);
|
||||
return SGF.B.createLoadBorrow(LV().Loc, address);
|
||||
}
|
||||
|
||||
ManagedValue emitAddress(SILGenFunction &SGF, AccessKind accessKind) {
|
||||
auto tsanKind =
|
||||
(accessKind == AccessKind::Read ? TSanKind::None : TSanKind::InoutAccess);
|
||||
return SGF.emitAddressOfLValue(Loc, std::move(LV), accessKind, tsanKind);
|
||||
return SGF.emitAddressOfLValue(LV().Loc, std::move(LV().LV),
|
||||
accessKind, tsanKind);
|
||||
}
|
||||
|
||||
ManagedValue finishOriginalExpr(SILGenFunction &SGF, ManagedValue innerValue,
|
||||
Expr *expr) {
|
||||
/// Replay the original argument expression.
|
||||
ManagedValue finishOriginalArgument(SILGenFunction &SGF) {
|
||||
auto results = finishOriginalExpr(SGF, Original.getExpr());
|
||||
auto value = results.first; // just let the owner go
|
||||
|
||||
if (Original.isIndirect() && !value.getType().isAddress()) {
|
||||
value = value.materialize(SGF, Original.getExpr());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// (value, owner)
|
||||
std::pair<ManagedValue, ManagedValue>
|
||||
finishOriginalExpr(SILGenFunction &SGF, Expr *expr) {
|
||||
|
||||
// This needs to handle all of the recursive cases from
|
||||
// ArgEmission::maybeEmitAsAccess.
|
||||
// ArgEmission::maybeEmitDelayed.
|
||||
|
||||
expr = expr->getSemanticsProvidingExpr();
|
||||
|
||||
// Handle injections into optionals.
|
||||
if (auto inject = dyn_cast<InjectIntoOptionalExpr>(expr)) {
|
||||
auto value = finishOriginalExpr(SGF, innerValue, inject->getSubExpr());
|
||||
auto ownedValue =
|
||||
finishOriginalExpr(SGF, inject->getSubExpr());
|
||||
auto &optionalTL = SGF.getTypeLowering(expr->getType());
|
||||
return SGF.emitInjectOptional(inject, optionalTL, SGFContext(),
|
||||
[&](SGFContext ctx) { return value; });
|
||||
|
||||
auto optValue = SGF.emitInjectOptional(inject, optionalTL, SGFContext(),
|
||||
[&](SGFContext ctx) { return ownedValue.first; });
|
||||
return {optValue, ownedValue.second};
|
||||
}
|
||||
|
||||
// Handle try!.
|
||||
if (auto forceTry = dyn_cast<ForceTryExpr>(expr)) {
|
||||
// Handle throws from the accessor? But what if the writeback throws?
|
||||
SILGenFunction::ForceTryEmission emission(SGF, forceTry);
|
||||
return finishOriginalExpr(SGF, innerValue, forceTry->getSubExpr());
|
||||
return finishOriginalExpr(SGF, forceTry->getSubExpr());
|
||||
}
|
||||
|
||||
// Handle optional evaluations.
|
||||
if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
|
||||
return finishOptionalEvaluation(SGF, optEval);
|
||||
}
|
||||
|
||||
// Done with the recursive cases. Make sure we handled everything.
|
||||
assert(isa<InOutToPointerExpr>(expr) ||
|
||||
isa<ArrayToPointerExpr>(expr));
|
||||
return innerValue;
|
||||
isa<ArrayToPointerExpr>(expr) ||
|
||||
isa<StringToPointerExpr>(expr));
|
||||
|
||||
switch (Kind) {
|
||||
case InOut:
|
||||
case BorrowDirect:
|
||||
case BorrowIndirect:
|
||||
llvm_unreachable("no original expr to finish in these cases");
|
||||
|
||||
case LValueToPointer:
|
||||
return {SGF.emitLValueToPointer(LV().Loc, std::move(LV().LV),
|
||||
Extra.get<PointerAccessInfo>(Kind)),
|
||||
/*owner*/ ManagedValue()};
|
||||
|
||||
case LValueArrayToPointer:
|
||||
return SGF.emitArrayToPointer(LV().Loc, std::move(LV().LV),
|
||||
Extra.get<ArrayAccessInfo>(Kind));
|
||||
|
||||
case RValueArrayToPointer: {
|
||||
auto pointerExpr = cast<ArrayToPointerExpr>(expr);
|
||||
auto optArrayValue = RV().RV;
|
||||
auto arrayValue = emitBindOptionals(SGF, optArrayValue,
|
||||
pointerExpr->getSubExpr());
|
||||
return SGF.emitArrayToPointer(pointerExpr, arrayValue,
|
||||
Extra.get<ArrayAccessInfo>(Kind));
|
||||
}
|
||||
|
||||
case RValueStringToPointer: {
|
||||
auto pointerExpr = cast<StringToPointerExpr>(expr);
|
||||
auto optStringValue = RV().RV;
|
||||
auto stringValue =
|
||||
emitBindOptionals(SGF, optStringValue, pointerExpr->getSubExpr());
|
||||
return SGF.emitStringToPointer(pointerExpr, stringValue,
|
||||
pointerExpr->getType());
|
||||
}
|
||||
}
|
||||
llvm_unreachable("bad kind");
|
||||
}
|
||||
|
||||
ManagedValue emitBindOptionals(SILGenFunction &SGF, ManagedValue optValue,
|
||||
Expr *expr) {
|
||||
expr = expr->getSemanticsProvidingExpr();
|
||||
auto bind = dyn_cast<BindOptionalExpr>(expr);
|
||||
|
||||
// If we don't find a bind, the value isn't optional.
|
||||
if (!bind) return optValue;
|
||||
|
||||
// Recurse.
|
||||
optValue = emitBindOptionals(SGF, optValue, bind->getSubExpr());
|
||||
|
||||
// Check whether the value is non-nil.
|
||||
SGF.emitBindOptional(bind, optValue, bind->getDepth());
|
||||
|
||||
// Extract the non-optional value.
|
||||
auto &optTL = SGF.getTypeLowering(optValue.getType());
|
||||
auto value = SGF.emitUncheckedGetOptionalValueFrom(bind, optValue, optTL);
|
||||
return value;
|
||||
}
|
||||
|
||||
std::pair<ManagedValue, ManagedValue>
|
||||
finishOptionalEvaluation(SILGenFunction &SGF, OptionalEvaluationExpr *eval) {
|
||||
SmallVector<ManagedValue, 2> results;
|
||||
|
||||
SGF.emitOptionalEvaluation(eval, eval->getType(), results, SGFContext(),
|
||||
[&](SmallVectorImpl<ManagedValue> &results, SGFContext C) {
|
||||
// Recurse.
|
||||
auto values = finishOriginalExpr(SGF, eval->getSubExpr());
|
||||
|
||||
// Our primary result is the value.
|
||||
results.push_back(values.first);
|
||||
|
||||
// Our secondary result is the owner, if we have one.
|
||||
if (auto owner = values.second) results.push_back(owner);
|
||||
});
|
||||
|
||||
assert(results.size() == 1 || results.size() == 2);
|
||||
|
||||
ManagedValue value = results[0];
|
||||
|
||||
ManagedValue owner;
|
||||
if (results.size() == 2) {
|
||||
owner = results[1];
|
||||
|
||||
// Create a new value-dependence here if the primary result is
|
||||
// trivial.
|
||||
auto &valueTL = SGF.getTypeLowering(value.getType());
|
||||
if (valueTL.isTrivial()) {
|
||||
SILValue dependentValue =
|
||||
SGF.B.createMarkDependence(eval, value.forward(SGF),
|
||||
owner.getValue());
|
||||
value = SGF.emitManagedRValueWithCleanup(dependentValue, valueTL);
|
||||
}
|
||||
}
|
||||
|
||||
return {value, owner};
|
||||
}
|
||||
};
|
||||
|
||||
/// Begin all the formal accesses for a set of inout arguments.
|
||||
static void beginInOutFormalAccesses(SILGenFunction &SGF,
|
||||
MutableArrayRef<InOutArgument> inoutArgs,
|
||||
} // end anonymous namespace
|
||||
|
||||
/// Perform the formal-access phase of call argument emission by emitting
|
||||
/// all of the delayed arguments.
|
||||
static void emitDelayedArguments(SILGenFunction &SGF,
|
||||
MutableArrayRef<DelayedArgument> delayedArgs,
|
||||
MutableArrayRef<SmallVector<ManagedValue, 4>> args) {
|
||||
assert(!inoutArgs.empty());
|
||||
assert(!delayedArgs.empty());
|
||||
|
||||
SmallVector<std::pair<SILValue, SILLocation>, 4> emittedInoutArgs;
|
||||
auto inoutNext = inoutArgs.begin();
|
||||
auto delayedNext = delayedArgs.begin();
|
||||
|
||||
// The assumption we make is that 'args' and 'inoutArgs' were built
|
||||
// up in parallel, with empty spots being dropped into 'args'
|
||||
@@ -2078,14 +2303,22 @@ static void beginInOutFormalAccesses(SILGenFunction &SGF,
|
||||
for (ManagedValue &siteArg : siteArgs) {
|
||||
if (siteArg) continue;
|
||||
|
||||
auto value = inoutNext->emit(SGF);
|
||||
assert(delayedNext != delayedArgs.end());
|
||||
auto &delayedArg = *delayedNext;
|
||||
|
||||
// Emit the delayed argument and replace it in the arguments array.
|
||||
auto value = delayedArg.emit(SGF);
|
||||
siteArg = value;
|
||||
if (inoutNext->isSimpleInOut()) {
|
||||
|
||||
// Remember all the simple inouts we emitted so we can perform
|
||||
// a basic inout-aliasing analysis.
|
||||
// This should be completely obviated by static enforcement.
|
||||
if (delayedArg.isSimpleInOut()) {
|
||||
emittedInoutArgs.push_back({value.getValue(),
|
||||
inoutNext->getLocation()});
|
||||
delayedArg.getInOutLocation()});
|
||||
}
|
||||
|
||||
if (++inoutNext == inoutArgs.end())
|
||||
if (++delayedNext == delayedArgs.end())
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@@ -2111,29 +2344,6 @@ done:
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a scalar value, materialize it into memory with the
|
||||
/// exact same level of cleanup it had before.
|
||||
static ManagedValue emitMaterializeIntoTemporary(SILGenFunction &SGF,
|
||||
SILLocation loc,
|
||||
ManagedValue object) {
|
||||
auto temporary = SGF.emitTemporaryAllocation(loc, object.getType());
|
||||
bool hadCleanup = object.hasCleanup();
|
||||
|
||||
// The temporary memory is +0 if the value was.
|
||||
if (hadCleanup) {
|
||||
SGF.B.emitStoreValueOperation(loc, object.forward(SGF), temporary,
|
||||
StoreOwnershipQualifier::Init);
|
||||
|
||||
// SEMANTIC SIL TODO: This should really be called a temporary LValue.
|
||||
return ManagedValue::forOwnedAddressRValue(temporary,
|
||||
SGF.enterDestroyCleanup(temporary));
|
||||
} else {
|
||||
object = SGF.emitManagedBeginBorrow(loc, object.getValue());
|
||||
SGF.emitManagedStoreBorrow(loc, object.getValue(), temporary);
|
||||
return ManagedValue::forBorrowedAddressRValue(temporary);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// A destination for an argument other than just "onto to the end
|
||||
@@ -2388,23 +2598,23 @@ class ArgEmitter {
|
||||
ClaimedParamsRef ParamInfos;
|
||||
SmallVectorImpl<ManagedValue> &Args;
|
||||
|
||||
/// Track any inout arguments that are emitted. Each corresponds
|
||||
/// Track any delayed arguments that are emitted. Each corresponds
|
||||
/// in order to a "hole" (a null value) in Args.
|
||||
SmallVectorImpl<InOutArgument> &InOutArguments;
|
||||
SmallVectorImpl<DelayedArgument> &DelayedArguments;
|
||||
|
||||
Optional<ArgSpecialDestArray> SpecialDests;
|
||||
public:
|
||||
ArgEmitter(SILGenFunction &SGF, SILFunctionTypeRepresentation Rep,
|
||||
ClaimedParamsRef paramInfos,
|
||||
SmallVectorImpl<ManagedValue> &args,
|
||||
SmallVectorImpl<InOutArgument> &inoutArgs,
|
||||
SmallVectorImpl<DelayedArgument> &delayedArgs,
|
||||
const Optional<ForeignErrorConvention> &foreignError,
|
||||
ImportAsMemberStatus foreignSelf,
|
||||
Optional<ArgSpecialDestArray> specialDests = None)
|
||||
: SGF(SGF), Rep(Rep), ForeignError(foreignError),
|
||||
ForeignSelf(foreignSelf),
|
||||
ParamInfos(paramInfos),
|
||||
Args(args), InOutArguments(inoutArgs), SpecialDests(specialDests) {
|
||||
Args(args), DelayedArguments(delayedArgs), SpecialDests(specialDests) {
|
||||
assert(!specialDests || specialDests->size() == paramInfos.size());
|
||||
}
|
||||
|
||||
@@ -2586,11 +2796,24 @@ private:
|
||||
// If no abstraction is required, try to honor the emission contexts.
|
||||
if (!contexts.RequiresReabstraction) {
|
||||
auto loc = arg.getLocation();
|
||||
result = std::move(arg).getAsSingleValue(SGF, contexts.ForEmission);
|
||||
|
||||
// Peephole certain argument emissions.
|
||||
if (arg.isExpr()) {
|
||||
auto expr = std::move(arg).asKnownExpr();
|
||||
|
||||
// Try the peepholes.
|
||||
if (maybeEmitDelayed(expr, OriginalArgument(expr, /*indirect*/ true)))
|
||||
return;
|
||||
|
||||
// Otherwise, just use the default logic.
|
||||
result = SGF.emitRValueAsSingleValue(expr, contexts.ForEmission);
|
||||
} else {
|
||||
result = std::move(arg).getAsSingleValue(SGF, contexts.ForEmission);
|
||||
}
|
||||
|
||||
// If it's not already in memory, put it there.
|
||||
if (!result.getType().isAddress()) {
|
||||
result = emitMaterializeIntoTemporary(SGF, loc, result);
|
||||
result = result.materialize(SGF, loc);
|
||||
}
|
||||
|
||||
// Otherwise, simultaneously emit and reabstract.
|
||||
@@ -2652,7 +2875,7 @@ private:
|
||||
|
||||
// Leave an empty space in the ManagedValue sequence and
|
||||
// remember that we had an inout argument.
|
||||
InOutArguments.emplace_back(InOutArgument::InOut, std::move(lv), loc);
|
||||
DelayedArguments.emplace_back(DelayedArgument::InOut, std::move(lv), loc);
|
||||
Args.push_back(ManagedValue());
|
||||
return;
|
||||
}
|
||||
@@ -2679,7 +2902,7 @@ private:
|
||||
auto expr = std::move(arg).asKnownExpr();
|
||||
|
||||
// Try the peepholes.
|
||||
if (maybeEmitAsAccess(expr, expr))
|
||||
if (maybeEmitDelayed(expr, OriginalArgument(expr, /*indirect*/ false)))
|
||||
return;
|
||||
|
||||
// Otherwise, just use the default logic.
|
||||
@@ -2707,42 +2930,39 @@ private:
|
||||
Args.push_back(value);
|
||||
}
|
||||
|
||||
bool maybeEmitAsAccess(Expr *expr, Expr *originalExpr) {
|
||||
bool maybeEmitDelayed(Expr *expr, OriginalArgument original) {
|
||||
expr = expr->getSemanticsProvidingExpr();
|
||||
|
||||
// Delay accessing inout-to-pointer arguments until the call.
|
||||
if (auto inoutToPointer = dyn_cast<InOutToPointerExpr>(expr)) {
|
||||
auto info = SGF.getPointerAccessInfo(inoutToPointer->getType());
|
||||
LValue lv = SGF.emitLValue(inoutToPointer->getSubExpr(), info.AccessKind);
|
||||
InOutArguments.emplace_back(info, std::move(lv), inoutToPointer,
|
||||
originalExpr);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
return emitDelayedConversion(inoutToPointer, original);
|
||||
}
|
||||
|
||||
// Delay accessing array-to-pointer arguments until the call.
|
||||
if (auto arrayToPointer = dyn_cast<ArrayToPointerExpr>(expr)) {
|
||||
auto arrayExpr = arrayToPointer->getSubExpr();
|
||||
if (auto inoutType = arrayExpr->getType()->getAs<InOutType>()) {
|
||||
auto info = SGF.getArrayAccessInfo(arrayToPointer->getType(),
|
||||
inoutType->getObjectType());
|
||||
LValue lv = SGF.emitLValue(arrayExpr, info.AccessKind);
|
||||
InOutArguments.emplace_back(info, std::move(lv), arrayToPointer,
|
||||
originalExpr);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
}
|
||||
return emitDelayedConversion(arrayToPointer, original);
|
||||
}
|
||||
|
||||
// TODO: if the array comes from a storage reference, borrow it.
|
||||
return false;
|
||||
// Delay accessing string-to-pointer arguments until the call.
|
||||
if (auto stringToPointer = dyn_cast<StringToPointerExpr>(expr)) {
|
||||
return emitDelayedConversion(stringToPointer, original);
|
||||
}
|
||||
|
||||
// Any recursive cases we handle here need to be handled in
|
||||
// InOutArgument::finishOriginalExpr.
|
||||
// DelayedArgument::finishOriginalExpr.
|
||||
|
||||
// Handle optional evaluations.
|
||||
if (auto optional = dyn_cast<OptionalEvaluationExpr>(expr)) {
|
||||
// The validity of just recursing here depends on the fact
|
||||
// that we only return true for the specific conversions above,
|
||||
// which are constrained by the ASTVerifier to only appear in
|
||||
// specific forms.
|
||||
return maybeEmitDelayed(optional->getSubExpr(), original);
|
||||
}
|
||||
|
||||
// Handle injections into optionals.
|
||||
if (auto inject = dyn_cast<InjectIntoOptionalExpr>(expr)) {
|
||||
return maybeEmitAsAccess(inject->getSubExpr(), originalExpr);
|
||||
return maybeEmitDelayed(inject->getSubExpr(), original);
|
||||
}
|
||||
|
||||
// Handle try! expressions.
|
||||
@@ -2750,12 +2970,71 @@ private:
|
||||
// Any expressions in the l-value must be routed appropriately.
|
||||
SILGenFunction::ForceTryEmission emission(SGF, forceTry);
|
||||
|
||||
return maybeEmitAsAccess(forceTry->getSubExpr(), originalExpr);
|
||||
return maybeEmitDelayed(forceTry->getSubExpr(), original);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool emitDelayedConversion(InOutToPointerExpr *pointerExpr,
|
||||
OriginalArgument original) {
|
||||
auto info = SGF.getPointerAccessInfo(pointerExpr->getType());
|
||||
LValue lv = SGF.emitLValue(pointerExpr->getSubExpr(), info.AccessKind);
|
||||
DelayedArguments.emplace_back(info, std::move(lv), pointerExpr, original);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool emitDelayedConversion(ArrayToPointerExpr *pointerExpr,
|
||||
OriginalArgument original) {
|
||||
auto arrayExpr = pointerExpr->getSubExpr();
|
||||
|
||||
// If the source of the conversion is an inout, emit the l-value
|
||||
// but delay the formal access.
|
||||
if (auto inoutType = arrayExpr->getType()->getAs<InOutType>()) {
|
||||
auto info = SGF.getArrayAccessInfo(pointerExpr->getType(),
|
||||
inoutType->getObjectType());
|
||||
LValue lv = SGF.emitLValue(arrayExpr, info.AccessKind);
|
||||
DelayedArguments.emplace_back(info, std::move(lv), pointerExpr,
|
||||
original);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, it's an r-value conversion.
|
||||
auto info = SGF.getArrayAccessInfo(pointerExpr->getType(),
|
||||
arrayExpr->getType());
|
||||
|
||||
auto rvalueExpr = lookThroughBindOptionals(arrayExpr);
|
||||
ManagedValue value = SGF.emitRValueAsSingleValue(rvalueExpr);
|
||||
DelayedArguments.emplace_back(DelayedArgument::RValueArrayToPointer,
|
||||
info, value, original);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Emit an rvalue-array-to-pointer conversion as a delayed argument.
|
||||
bool emitDelayedConversion(StringToPointerExpr *pointerExpr,
|
||||
OriginalArgument original) {
|
||||
auto rvalueExpr = lookThroughBindOptionals(pointerExpr->getSubExpr());
|
||||
ManagedValue value = SGF.emitRValueAsSingleValue(rvalueExpr);
|
||||
DelayedArguments.emplace_back(DelayedArgument::RValueStringToPointer,
|
||||
value, original);
|
||||
Args.push_back(ManagedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
static Expr *lookThroughBindOptionals(Expr *expr) {
|
||||
while (true) {
|
||||
expr = expr->getSemanticsProvidingExpr();
|
||||
if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
|
||||
expr = bind->getSubExpr();
|
||||
} else {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ManagedValue emitSubstToOrigArgument(ArgumentSource &&arg,
|
||||
SILType loweredSubstArgType,
|
||||
AbstractionPattern origParamType,
|
||||
@@ -3098,7 +3377,7 @@ struct ElementExtent {
|
||||
ArrayRef<ManagedValue> Args;
|
||||
/// The inout arguments which feed this tuple element.
|
||||
/// This is set in the second pass.
|
||||
MutableArrayRef<InOutArgument> InOutArgs;
|
||||
MutableArrayRef<DelayedArgument> DelayedArgs;
|
||||
|
||||
ElementExtent()
|
||||
: HasDestIndex(false)
|
||||
@@ -3139,7 +3418,7 @@ class TupleShuffleArgEmitter {
|
||||
// Used by flattenPatternFromInnerExtendIntoInnerParams and
|
||||
// splitInnerArgumentsCorrectly.
|
||||
SmallVector<ManagedValue, 8> innerArgs;
|
||||
SmallVector<InOutArgument, 2> innerInOutArgs;
|
||||
SmallVector<DelayedArgument, 2> innerDelayedArgs;
|
||||
|
||||
public:
|
||||
TupleShuffleArgEmitter(TupleShuffleExpr *e, ArrayRef<TupleTypeElt> innerElts,
|
||||
@@ -3302,7 +3581,7 @@ void TupleShuffleArgEmitter::flattenPatternFromInnerExtendIntoInnerParams(
|
||||
|
||||
void TupleShuffleArgEmitter::splitInnerArgumentsCorrectly(ArgEmitter &parent) {
|
||||
ArrayRef<ManagedValue> nextArgs = innerArgs;
|
||||
MutableArrayRef<InOutArgument> nextInOutArgs = innerInOutArgs;
|
||||
MutableArrayRef<DelayedArgument> nextDelayedArgs = innerDelayedArgs;
|
||||
for (auto &extent : innerExtents) {
|
||||
auto length = extent.Params.size();
|
||||
|
||||
@@ -3311,18 +3590,18 @@ void TupleShuffleArgEmitter::splitInnerArgumentsCorrectly(ArgEmitter &parent) {
|
||||
nextArgs = nextArgs.slice(length);
|
||||
|
||||
// Claim the correct number of inout arguments as well.
|
||||
unsigned numInOut = 0;
|
||||
size_t numDelayed = 0;
|
||||
for (auto arg : extent.Args) {
|
||||
assert(!arg.isInContext() || extent.HasDestIndex);
|
||||
if (!arg)
|
||||
numInOut++;
|
||||
numDelayed++;
|
||||
}
|
||||
extent.InOutArgs = nextInOutArgs.slice(0, numInOut);
|
||||
nextInOutArgs = nextInOutArgs.slice(numInOut);
|
||||
extent.DelayedArgs = nextDelayedArgs.slice(0, numDelayed);
|
||||
nextDelayedArgs = nextDelayedArgs.slice(numDelayed);
|
||||
}
|
||||
|
||||
assert(nextArgs.empty() && "didn't claim all args");
|
||||
assert(nextInOutArgs.empty() && "didn't claim all inout args");
|
||||
assert(nextDelayedArgs.empty() && "didn't claim all inout args");
|
||||
}
|
||||
|
||||
void TupleShuffleArgEmitter::emitDefaultArgsAndFinalize(ArgEmitter &parent) {
|
||||
@@ -3343,8 +3622,8 @@ void TupleShuffleArgEmitter::emitDefaultArgsAndFinalize(ArgEmitter &parent) {
|
||||
|
||||
// Move the appropriate inner arguments over as outer arguments.
|
||||
parent.Args.append(extent.Args.begin(), extent.Args.end());
|
||||
for (auto &inoutArg : extent.InOutArgs)
|
||||
parent.InOutArguments.push_back(std::move(inoutArg));
|
||||
for (auto &delayedArg : extent.DelayedArgs)
|
||||
parent.DelayedArguments.push_back(std::move(delayedArg));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3428,7 +3707,7 @@ void TupleShuffleArgEmitter::emit(ArgEmitter &parent) {
|
||||
// Emit the inner expression.
|
||||
if (!innerParams.empty()) {
|
||||
ArgEmitter(parent.SGF, parent.Rep, ClaimedParamsRef(innerParams), innerArgs,
|
||||
innerInOutArgs,
|
||||
innerDelayedArgs,
|
||||
/*foreign error*/ None, /*foreign self*/ ImportAsMemberStatus(),
|
||||
(innerSpecialDests ? ArgSpecialDestArray(*innerSpecialDests)
|
||||
: Optional<ArgSpecialDestArray>()))
|
||||
@@ -3741,13 +4020,13 @@ namespace {
|
||||
/// Evaluate arguments and begin any inout formal accesses.
|
||||
void emit(SILGenFunction &SGF, AbstractionPattern origParamType,
|
||||
ParamLowering &lowering, SmallVectorImpl<ManagedValue> &args,
|
||||
SmallVectorImpl<InOutArgument> &inoutArgs,
|
||||
SmallVectorImpl<DelayedArgument> &delayedArgs,
|
||||
const Optional<ForeignErrorConvention> &foreignError,
|
||||
const ImportAsMemberStatus &foreignSelf) && {
|
||||
auto params = lowering.claimParams(origParamType, getSubstArgType(),
|
||||
foreignError, foreignSelf);
|
||||
|
||||
ArgEmitter emitter(SGF, lowering.Rep, params, args, inoutArgs,
|
||||
ArgEmitter emitter(SGF, lowering.Rep, params, args, delayedArgs,
|
||||
foreignError, foreignSelf);
|
||||
emitter.emitTopLevel(std::move(ArgValue), origParamType);
|
||||
}
|
||||
@@ -4328,7 +4607,7 @@ void CallEmission::emitArgumentsForNormalApply(
|
||||
SmallVectorImpl<ManagedValue> &uncurriedArgs,
|
||||
Optional<SILLocation> &uncurriedLoc, CanFunctionType &formalApplyType) {
|
||||
SmallVector<SmallVector<ManagedValue, 4>, 2> args;
|
||||
SmallVector<InOutArgument, 2> inoutArgs;
|
||||
SmallVector<DelayedArgument, 2> delayedArgs;
|
||||
auto expectedUncurriedOrigFormalType =
|
||||
getUncurriedOrigFormalType(origFormalType);
|
||||
(void)expectedUncurriedOrigFormalType;
|
||||
@@ -4368,7 +4647,7 @@ void CallEmission::emitArgumentsForNormalApply(
|
||||
bool isParamSite = &site == &uncurriedSites.back();
|
||||
|
||||
std::move(site).emit(SGF, origParamType, paramLowering, args.back(),
|
||||
inoutArgs,
|
||||
delayedArgs,
|
||||
// Claim the foreign error with the method
|
||||
// formal params.
|
||||
isParamSite ? foreignError : None,
|
||||
@@ -4383,9 +4662,10 @@ void CallEmission::emitArgumentsForNormalApply(
|
||||
expectedUncurriedOrigFormalType.getType() &&
|
||||
"getUncurriedOrigFormalType and emitArgumentsForNormalCall are out of "
|
||||
"sync");
|
||||
// Begin the formal accesses to any inout arguments we have.
|
||||
if (!inoutArgs.empty()) {
|
||||
beginInOutFormalAccesses(SGF, inoutArgs, args);
|
||||
|
||||
// Emit any delayed arguments: formal accesses to inout arguments, etc.
|
||||
if (!delayedArgs.empty()) {
|
||||
emitDelayedArguments(SGF, delayedArgs, args);
|
||||
}
|
||||
|
||||
// Uncurry the arguments in calling convention order.
|
||||
@@ -4419,7 +4699,7 @@ RValue CallEmission::applyRemainingCallSites(
|
||||
ParamLowering paramLowering(substFnType, SGF);
|
||||
|
||||
SmallVector<ManagedValue, 4> siteArgs;
|
||||
SmallVector<InOutArgument, 2> inoutArgs;
|
||||
SmallVector<DelayedArgument, 2> delayedArgs;
|
||||
|
||||
// TODO: foreign errors for block or function pointer values?
|
||||
assert(substFnType->hasErrorResult() ||
|
||||
@@ -4443,10 +4723,10 @@ RValue CallEmission::applyRemainingCallSites(
|
||||
ArgumentScope argScope(SGF, loc);
|
||||
|
||||
std::move(extraSites[i])
|
||||
.emit(SGF, origParamType, paramLowering, siteArgs, inoutArgs,
|
||||
.emit(SGF, origParamType, paramLowering, siteArgs, delayedArgs,
|
||||
foreignError, foreignSelf);
|
||||
if (!inoutArgs.empty()) {
|
||||
beginInOutFormalAccesses(SGF, inoutArgs, siteArgs);
|
||||
if (!delayedArgs.empty()) {
|
||||
emitDelayedArguments(SGF, delayedArgs, siteArgs);
|
||||
}
|
||||
|
||||
ApplyOptions options = ApplyOptions::None;
|
||||
@@ -5030,7 +5310,7 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorObjectBaseArg() {
|
||||
isNonClassProtocolMember(accessor.getDecl()))) &&
|
||||
"passing unmaterialized r-value as inout argument");
|
||||
|
||||
base = emitMaterializeIntoTemporary(SGF, loc, base);
|
||||
base = base.materialize(SGF, loc);
|
||||
if (selfParam.isIndirectInOut()) {
|
||||
// Drop the cleanup if we have one.
|
||||
auto baseLV = ManagedValue::forLValue(base.getValue());
|
||||
|
||||
@@ -3701,11 +3701,10 @@ namespace {
|
||||
/// emitOptimizedOptionalEvaluation - Look for cases where we can short-circuit
|
||||
/// evaluation of an OptionalEvaluationExpr by pattern matching the AST.
|
||||
///
|
||||
static bool emitOptimizedOptionalEvaluation(OptionalEvaluationExpr *E,
|
||||
SILValue &LoadableResult,
|
||||
Initialization *optInit,
|
||||
RValueEmitter &RVE) {
|
||||
auto &SGF = RVE.SGF;
|
||||
static bool emitOptimizedOptionalEvaluation(SILGenFunction &SGF,
|
||||
OptionalEvaluationExpr *E,
|
||||
ManagedValue &result,
|
||||
SGFContext ctx) {
|
||||
// It is a common occurrence to get conversions back and forth from T! to T?.
|
||||
// Peephole these by looking for a subexpression that is a BindOptionalExpr.
|
||||
// If we see one, we can produce a single instruction, which doesn't require
|
||||
@@ -3727,16 +3726,41 @@ static bool emitOptimizedOptionalEvaluation(OptionalEvaluationExpr *E,
|
||||
|
||||
// SIL defines away abstraction differences between T? and T!,
|
||||
// so we can just emit the sub-initialization normally.
|
||||
if (optInit)
|
||||
SGF.emitExprInto(BO->getSubExpr(), optInit);
|
||||
else
|
||||
LoadableResult = SGF.emitRValueAsSingleValue(BO->getSubExpr()).forward(SGF);
|
||||
result = SGF.emitRValueAsSingleValue(BO->getSubExpr(), ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
|
||||
SGFContext C) {
|
||||
auto &optTL = SGF.getTypeLowering(E->getType());
|
||||
SmallVector<ManagedValue, 1> results;
|
||||
SGF.emitOptionalEvaluation(E, E->getType(), results, C,
|
||||
[&](SmallVectorImpl<ManagedValue> &results, SGFContext primaryC) {
|
||||
ManagedValue result;
|
||||
if (!emitOptimizedOptionalEvaluation(SGF, E, result, primaryC)) {
|
||||
result = SGF.emitRValueAsSingleValue(E->getSubExpr(), primaryC);
|
||||
}
|
||||
|
||||
assert(results.empty());
|
||||
results.push_back(result);
|
||||
});
|
||||
|
||||
assert(results.size() == 1);
|
||||
if (results[0].isInContext()) {
|
||||
return RValue();
|
||||
} else {
|
||||
return RValue(SGF, E, results[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void SILGenFunction::emitOptionalEvaluation(SILLocation loc, Type optType,
|
||||
SmallVectorImpl<ManagedValue> &results,
|
||||
SGFContext C,
|
||||
llvm::function_ref<void(SmallVectorImpl<ManagedValue> &,
|
||||
SGFContext primaryC)>
|
||||
generateNormalResults) {
|
||||
assert(results.empty());
|
||||
|
||||
auto &optTL = getTypeLowering(optType);
|
||||
|
||||
Initialization *optInit = C.getEmitInto();
|
||||
bool usingProvidedContext =
|
||||
@@ -3745,7 +3769,7 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
|
||||
// Form the optional using address operations if the type is address-only or
|
||||
// if we already have an address to use.
|
||||
bool isByAddress = ((usingProvidedContext || optTL.isAddressOnly()) &&
|
||||
SGF.silConv.useLoweredAddresses());
|
||||
silConv.useLoweredAddresses());
|
||||
|
||||
std::unique_ptr<TemporaryInitialization> optTemp;
|
||||
if (!isByAddress) {
|
||||
@@ -3756,7 +3780,7 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
|
||||
// Allocate the temporary for the Optional<T> if we didn't get one from the
|
||||
// context. This needs to happen outside of the cleanups scope we're about
|
||||
// to push.
|
||||
optTemp = SGF.emitTemporary(E, optTL);
|
||||
optTemp = emitTemporary(loc, optTL);
|
||||
optInit = optTemp.get();
|
||||
}
|
||||
assert(isByAddress == (optInit != nullptr));
|
||||
@@ -3764,46 +3788,69 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
|
||||
// Acquire the address to emit into outside of the cleanups scope.
|
||||
SILValue optAddr;
|
||||
if (isByAddress)
|
||||
optAddr = optInit->getAddressForInPlaceInitialization(SGF, E);
|
||||
optAddr = optInit->getAddressForInPlaceInitialization(*this, loc);
|
||||
|
||||
// Enter a cleanups scope.
|
||||
FullExpr scope(SGF.Cleanups, E);
|
||||
FullExpr scope(Cleanups, CleanupLocation::get(loc));
|
||||
|
||||
// Inside of the cleanups scope, create a new initialization to
|
||||
// emit into optAddr.
|
||||
std::unique_ptr<TemporaryInitialization> normalInit;
|
||||
if (isByAddress) {
|
||||
normalInit = SGF.useBufferAsTemporary(optAddr, optTL);
|
||||
normalInit = useBufferAsTemporary(optAddr, optTL);
|
||||
}
|
||||
|
||||
// Install a new optional-failure destination just outside of the
|
||||
// cleanups scope.
|
||||
SILBasicBlock *failureBB = SGF.createBasicBlock();
|
||||
RestoreOptionalFailureDest restoreFailureDest(SGF,
|
||||
JumpDest(failureBB, SGF.Cleanups.getCleanupsDepth(), E));
|
||||
SILBasicBlock *failureBB = createBasicBlock();
|
||||
RestoreOptionalFailureDest
|
||||
restoreFailureDest(*this, JumpDest(failureBB, Cleanups.getCleanupsDepth(),
|
||||
CleanupLocation::get(loc)));
|
||||
|
||||
SILValue normalArgument = nullptr;
|
||||
if (emitOptimizedOptionalEvaluation(E, normalArgument,
|
||||
normalInit.get(), *this)) {
|
||||
// Already emitted code for this.
|
||||
} else if (normalInit) {
|
||||
// Emit the operand into the temporary.
|
||||
SGF.emitExprInto(E->getSubExpr(), normalInit.get());
|
||||
} else {
|
||||
normalArgument = SGF.emitRValueAsSingleValue(E->getSubExpr()).forward(SGF);
|
||||
generateNormalResults(results, SGFContext(normalInit.get()));
|
||||
assert(results.size() >= 1 && "didn't include a normal result");
|
||||
assert(results[0].isInContext() ||
|
||||
results[0].getType().getObjectType()
|
||||
== optTL.getLoweredType().getObjectType());
|
||||
|
||||
// If we're emitting into the context, make sure the normal value is there.
|
||||
if (normalInit && !results[0].isInContext()) {
|
||||
normalInit->copyOrInitValueInto(*this, loc, results[0], /*init*/ true);
|
||||
normalInit->finishInitialization(*this);
|
||||
results[0] = ManagedValue::forInContext();
|
||||
}
|
||||
|
||||
assert(isByAddress == (normalArgument == nullptr) &&
|
||||
"shouldn't have emitted as scalar in this case");
|
||||
|
||||
// We fell out of the normal result, which generated a T? as either
|
||||
// a scalar in normalArgument or directly into normalInit.
|
||||
|
||||
// If we're using by-address initialization, we must've emitted into
|
||||
// normalInit. Forward its cleanup before popping the scope.
|
||||
if (isByAddress) {
|
||||
normalInit->getManagedAddress().forward(SGF);
|
||||
normalInit->getManagedAddress().forward(*this);
|
||||
normalInit.reset(); // Make sure we don't use this anymore.
|
||||
} else {
|
||||
assert(!results[0].isInContext());
|
||||
results[0].forward(*this);
|
||||
}
|
||||
|
||||
// For all the secondary results, forward their cleanups and make sure
|
||||
// they're of optional type so that we can inject nil into them in
|
||||
// the failure path.
|
||||
// (Should this be controllable by the client?)
|
||||
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
|
||||
assert(!result.isInContext() && "secondary result was in context");
|
||||
auto resultTy = result.getType();
|
||||
assert(resultTy.isObject() && "secondary result wasn't an object");
|
||||
|
||||
// Forward the cleanup.
|
||||
SILValue value = result.forward(*this);
|
||||
|
||||
// If it's not already an optional type, make it optional.
|
||||
if (!resultTy.getAnyOptionalObjectType()) {
|
||||
resultTy = SILType::getOptionalType(resultTy);
|
||||
value = B.createOptionalSome(loc, value, resultTy);
|
||||
result = ManagedValue::forUnmanaged(value);
|
||||
}
|
||||
}
|
||||
|
||||
// This concludes the conditional scope.
|
||||
@@ -3817,67 +3864,95 @@ RValue RValueEmitter::visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
|
||||
// Remove the dead failureBB.
|
||||
failureBB->eraseFromParent();
|
||||
|
||||
// Just re-manage normalArgument if we're not using address-based IRGen.
|
||||
if (!isByAddress)
|
||||
return RValue(SGF, E,
|
||||
SGF.emitManagedRValueWithCleanup(normalArgument, optTL));
|
||||
// Just re-manage all the secondary results.
|
||||
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
|
||||
result = emitManagedRValueWithCleanup(result.getValue());
|
||||
}
|
||||
|
||||
// Just re-manage the main result if we're not using address-based IRGen.
|
||||
if (!isByAddress) {
|
||||
results[0] = emitManagedRValueWithCleanup(results[0].getValue(), optTL);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we must have emitted into normalInit, which means that,
|
||||
// now that we're out of the cleanups scope, we need to finish optInit.
|
||||
optInit->finishInitialization(SGF);
|
||||
assert(results[0].isInContext());
|
||||
optInit->finishInitialization(*this);
|
||||
|
||||
// If optInit came from the SGFContext, then we've successfully emitted
|
||||
// into that.
|
||||
if (usingProvidedContext)
|
||||
return RValue();
|
||||
if (usingProvidedContext) return;
|
||||
|
||||
// Otherwise, we must have emitted into optTemp.
|
||||
return RValue(SGF, E, optTemp->getManagedAddress());
|
||||
assert(optTemp);
|
||||
results[0] = optTemp->getManagedAddress();
|
||||
return;
|
||||
}
|
||||
|
||||
// Okay, we do have uses of the failure block, so we'll need to merge
|
||||
// control paths.
|
||||
|
||||
SILBasicBlock *contBB = SGF.createBasicBlock();
|
||||
SILBasicBlock *contBB = createBasicBlock();
|
||||
|
||||
// Branch to the continuation block.
|
||||
if (isByAddress)
|
||||
SGF.B.createBranch(E, contBB);
|
||||
else
|
||||
SGF.B.createBranch(E, contBB, normalArgument);
|
||||
SmallVector<SILValue, 4> bbArgs;
|
||||
if (!isByAddress)
|
||||
bbArgs.push_back(results[0].getValue());
|
||||
for (const auto &result : llvm::makeArrayRef(results).slice(1))
|
||||
bbArgs.push_back(result.getValue());
|
||||
|
||||
// Branch to the continuation block.
|
||||
B.createBranch(loc, contBB, bbArgs);
|
||||
|
||||
// In the failure block, inject nil into the result.
|
||||
SGF.B.emitBlock(failureBB);
|
||||
B.emitBlock(failureBB);
|
||||
|
||||
// Note that none of the code here introduces any cleanups.
|
||||
// If it did, we'd need to push a scope.
|
||||
bbArgs.clear();
|
||||
if (isByAddress) {
|
||||
SGF.emitInjectOptionalNothingInto(E, optAddr, optTL);
|
||||
SGF.B.createBranch(E, contBB);
|
||||
emitInjectOptionalNothingInto(loc, optAddr, optTL);
|
||||
} else {
|
||||
auto branchArg = SGF.getOptionalNoneValue(E, optTL);
|
||||
SGF.B.createBranch(E, contBB, branchArg);
|
||||
bbArgs.push_back(getOptionalNoneValue(loc, optTL));
|
||||
}
|
||||
for (const auto &result : llvm::makeArrayRef(results).slice(1)) {
|
||||
auto resultTy = result.getType();
|
||||
bbArgs.push_back(getOptionalNoneValue(loc, getTypeLowering(resultTy)));
|
||||
}
|
||||
B.createBranch(loc, contBB, bbArgs);
|
||||
|
||||
// Emit the continuation block.
|
||||
SGF.B.emitBlock(contBB);
|
||||
B.emitBlock(contBB);
|
||||
|
||||
// If this was done in SSA registers, then the value is provided as an
|
||||
// argument to the block; manage it and return.
|
||||
if (!isByAddress) {
|
||||
// Create a PHI for the optional result if desired.
|
||||
if (isByAddress) {
|
||||
assert(results[0].isInContext());
|
||||
} else {
|
||||
auto arg = contBB->createPHIArgument(optTL.getLoweredType(),
|
||||
ValueOwnershipKind::Owned);
|
||||
return RValue(SGF, E, SGF.emitManagedRValueWithCleanup(arg, optTL));
|
||||
results[0] = emitManagedRValueWithCleanup(arg, optTL);
|
||||
}
|
||||
|
||||
// Otherwise, we need to manage the value in optInit.
|
||||
optInit->finishInitialization(SGF);
|
||||
|
||||
// If we emitted into the provided context, we're done.
|
||||
if (usingProvidedContext) {
|
||||
return RValue();
|
||||
// Create PHIs for all the secondary results and manage them.
|
||||
for (auto &result : MutableArrayRef<ManagedValue>(results).slice(1)) {
|
||||
auto arg = contBB->createPHIArgument(result.getType(),
|
||||
ValueOwnershipKind::Owned);
|
||||
result = emitManagedRValueWithCleanup(arg);
|
||||
}
|
||||
|
||||
|
||||
// We may need to manage the value in optInit.
|
||||
if (!isByAddress) return;
|
||||
|
||||
assert(results[0].isInContext());
|
||||
optInit->finishInitialization(*this);
|
||||
|
||||
// If we didn't emit into the provided context, the primary result
|
||||
// is really a temporary.
|
||||
if (usingProvidedContext) return;
|
||||
|
||||
assert(optTemp);
|
||||
return RValue(SGF, E, optTemp->getManagedAddress());
|
||||
results[0] = optTemp->getManagedAddress();
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitForceValueExpr(ForceValueExpr *E, SGFContext C) {
|
||||
@@ -4213,10 +4288,9 @@ ManagedValue SILGenFunction::emitLValueToPointer(SILLocation loc, LValue &&lv,
|
||||
.getAsSingleValue(*this, loc);
|
||||
}
|
||||
|
||||
static ManagedValue emitArrayToPointer(SILGenFunction &SGF,
|
||||
SILLocation loc,
|
||||
ManagedValue array,
|
||||
SILGenFunction::ArrayAccessInfo accessInfo) {
|
||||
static std::pair<ManagedValue, ManagedValue>
|
||||
emitArrayToPointer(SILGenFunction &SGF, SILLocation loc, ManagedValue array,
|
||||
SILGenFunction::ArrayAccessInfo accessInfo) {
|
||||
auto &ctx = SGF.getASTContext();
|
||||
|
||||
FuncDecl *converter;
|
||||
@@ -4251,8 +4325,13 @@ static ManagedValue emitArrayToPointer(SILGenFunction &SGF,
|
||||
.getAll(resultScalars);
|
||||
assert(resultScalars.size() == 2);
|
||||
|
||||
// Mark the dependence of the pointer on the owner value.
|
||||
auto owner = resultScalars[0];
|
||||
auto pointer = resultScalars[1].forward(SGF);
|
||||
pointer = SGF.B.createMarkDependence(loc, pointer, owner.getValue());
|
||||
|
||||
// The owner's already in its own cleanup. Return the pointer.
|
||||
return resultScalars[1];
|
||||
return {ManagedValue::forTrivialObjectRValue(pointer), owner};
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitArrayToPointerExpr(ArrayToPointerExpr *E,
|
||||
@@ -4273,7 +4352,7 @@ RValue RValueEmitter::visitArrayToPointerExpr(ArrayToPointerExpr *E,
|
||||
array = SGF.emitRValueAsSingleValue(subExpr);
|
||||
}
|
||||
|
||||
auto pointer = ::emitArrayToPointer(SGF, E, array, accessInfo);
|
||||
auto pointer = ::emitArrayToPointer(SGF, E, array, accessInfo).first;
|
||||
return RValue(SGF, E, pointer);
|
||||
}
|
||||
|
||||
@@ -4283,34 +4362,54 @@ SILGenFunction::getArrayAccessInfo(Type pointerType, Type arrayType) {
|
||||
return { pointerType, arrayType, pointerAccessInfo.AccessKind };
|
||||
}
|
||||
|
||||
ManagedValue SILGenFunction::emitArrayToPointer(SILLocation loc, LValue &&lv,
|
||||
ArrayAccessInfo accessInfo) {
|
||||
std::pair<ManagedValue, ManagedValue>
|
||||
SILGenFunction::emitArrayToPointer(SILLocation loc, LValue &&lv,
|
||||
ArrayAccessInfo accessInfo) {
|
||||
auto array =
|
||||
emitAddressOfLValue(loc, std::move(lv), accessInfo.AccessKind);
|
||||
return ::emitArrayToPointer(*this, loc, array, accessInfo);
|
||||
}
|
||||
|
||||
std::pair<ManagedValue, ManagedValue>
|
||||
SILGenFunction::emitArrayToPointer(SILLocation loc, ManagedValue array,
|
||||
ArrayAccessInfo accessInfo) {
|
||||
return ::emitArrayToPointer(*this, loc, array, accessInfo);
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitStringToPointerExpr(StringToPointerExpr *E,
|
||||
SGFContext C) {
|
||||
auto &Ctx = SGF.getASTContext();
|
||||
FuncDecl *converter = Ctx.getConvertConstStringToUTF8PointerArgument(nullptr);
|
||||
|
||||
// Get the original value.
|
||||
ManagedValue orig = SGF.emitRValueAsSingleValue(E->getSubExpr());
|
||||
|
||||
// Perform the conversion.
|
||||
auto results = SGF.emitStringToPointer(E, orig, E->getType());
|
||||
|
||||
// Implicitly leave the owner managed and return the pointer.
|
||||
return RValue(SGF, E, results.first);
|
||||
}
|
||||
|
||||
std::pair<ManagedValue, ManagedValue>
|
||||
SILGenFunction::emitStringToPointer(SILLocation loc, ManagedValue stringValue,
|
||||
Type pointerType) {
|
||||
auto &Ctx = getASTContext();
|
||||
FuncDecl *converter = Ctx.getConvertConstStringToUTF8PointerArgument(nullptr);
|
||||
|
||||
// Invoke the conversion intrinsic, which will produce an owner-pointer pair.
|
||||
auto subMap = E->getType()->getCanonicalType()
|
||||
->getContextSubstitutionMap(SGF.SGM.M.getSwiftModule(),
|
||||
SGF.getPointerProtocol());
|
||||
auto subMap = pointerType->getContextSubstitutionMap(SGM.M.getSwiftModule(),
|
||||
getPointerProtocol());
|
||||
SmallVector<ManagedValue, 2> results;
|
||||
SGF.emitApplyOfLibraryIntrinsic(E, converter, subMap, orig, C).getAll(results);
|
||||
emitApplyOfLibraryIntrinsic(loc, converter, subMap, stringValue, SGFContext())
|
||||
.getAll(results);
|
||||
assert(results.size() == 2);
|
||||
|
||||
// Implicitly leave the owner managed and return the pointer.
|
||||
// FIXME: should this be using mark_dependence?
|
||||
auto pointer = results[1];
|
||||
return RValue(SGF, E, pointer);
|
||||
|
||||
// Mark the dependence of the pointer on the owner value.
|
||||
auto owner = results[0];
|
||||
auto pointer = results[1].forward(*this);
|
||||
pointer = B.createMarkDependence(loc, pointer, owner.getValue());
|
||||
|
||||
return {ManagedValue::forTrivialObjectRValue(pointer), owner};
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitPointerToPointerExpr(PointerToPointerExpr *E,
|
||||
SGFContext C) {
|
||||
auto &Ctx = SGF.getASTContext();
|
||||
|
||||
@@ -1512,6 +1512,13 @@ public:
|
||||
void emitBindOptional(SILLocation loc, ManagedValue optionalAddrOrValue,
|
||||
unsigned depth);
|
||||
|
||||
void emitOptionalEvaluation(SILLocation loc, Type optionalType,
|
||||
SmallVectorImpl<ManagedValue> &results,
|
||||
SGFContext C,
|
||||
llvm::function_ref<void(SmallVectorImpl<ManagedValue> &,
|
||||
SGFContext primaryC)>
|
||||
generateNormalResults);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Bridging thunks
|
||||
//===--------------------------------------------------------------------===//
|
||||
@@ -1751,8 +1758,17 @@ public:
|
||||
swift::AccessKind AccessKind;
|
||||
};
|
||||
ArrayAccessInfo getArrayAccessInfo(Type pointerType, Type arrayType);
|
||||
ManagedValue emitArrayToPointer(SILLocation loc, LValue &&lvalue,
|
||||
ArrayAccessInfo accessInfo);
|
||||
std::pair<ManagedValue,ManagedValue>
|
||||
emitArrayToPointer(SILLocation loc, LValue &&lvalue,
|
||||
ArrayAccessInfo accessInfo);
|
||||
|
||||
std::pair<ManagedValue,ManagedValue>
|
||||
emitArrayToPointer(SILLocation loc, ManagedValue arrayValue,
|
||||
ArrayAccessInfo accessInfo);
|
||||
|
||||
std::pair<ManagedValue,ManagedValue>
|
||||
emitStringToPointer(SILLocation loc, ManagedValue stringValue,
|
||||
Type pointerType);
|
||||
|
||||
class ForceTryEmission {
|
||||
SILGenFunction &SGF;
|
||||
|
||||
@@ -5101,6 +5101,8 @@ Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
|
||||
Type fromValueType = fromType->getAnyOptionalObjectType();
|
||||
Type toValueType = toType->getAnyOptionalObjectType();
|
||||
|
||||
// FIXME: this depth is wrong, it needs to be the depth of
|
||||
// OptionalEvaluationExprs introduced by the call to coerceToType below.
|
||||
expr =
|
||||
cs.cacheType(new (tc.Context) BindOptionalExpr(expr,
|
||||
expr->getSourceRange().End,
|
||||
|
||||
@@ -9,12 +9,16 @@ func sideEffect1() -> Int { return 1 }
|
||||
func sideEffect2() -> Int { return 2 }
|
||||
func takesMutablePointer(_ x: UnsafeMutablePointer<Int>) {}
|
||||
func takesConstPointer(_ x: UnsafePointer<Int>) {}
|
||||
func takesOptConstPointer(_ x: UnsafePointer<Int>?, and: Int) {}
|
||||
func takesOptOptConstPointer(_ x: UnsafePointer<Int>??, and: Int) {}
|
||||
func takesMutablePointer(_ x: UnsafeMutablePointer<Int>, and: Int) {}
|
||||
func takesConstPointer(_ x: UnsafePointer<Int>, and: Int) {}
|
||||
func takesMutableVoidPointer(_ x: UnsafeMutableRawPointer) {}
|
||||
func takesConstVoidPointer(_ x: UnsafeRawPointer) {}
|
||||
func takesMutableRawPointer(_ x: UnsafeMutableRawPointer) {}
|
||||
func takesConstRawPointer(_ x: UnsafeRawPointer) {}
|
||||
func takesOptConstRawPointer(_ x: UnsafeRawPointer?, and: Int) {}
|
||||
func takesOptOptConstRawPointer(_ x: UnsafeRawPointer??, and: Int) {}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_T018pointer_conversion0A9ToPointerySpySiG_SPySiGSvtF
|
||||
// CHECK: bb0([[MP:%.*]] : $UnsafeMutablePointer<Int>, [[CP:%.*]] : $UnsafePointer<Int>, [[MRP:%.*]] : $UnsafeMutableRawPointer):
|
||||
@@ -88,7 +92,8 @@ func arrayToPointer() {
|
||||
// CHECK: [[CONVERT_MUTABLE:%.*]] = function_ref @_T0s37_convertMutableArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_MUTABLE]]<Int, UnsafeMutablePointer<Int>>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_MUTABLE_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeMutablePointer<Int> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_MUTABLE_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
|
||||
takesConstPointer(ints)
|
||||
@@ -96,7 +101,8 @@ func arrayToPointer() {
|
||||
// CHECK: [[CONVERT_CONST:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_CONST]]<Int, UnsafePointer<Int>>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_CONST_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafePointer<Int> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_CONST_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
|
||||
takesMutableRawPointer(&ints)
|
||||
@@ -104,7 +110,8 @@ func arrayToPointer() {
|
||||
// CHECK: [[CONVERT_MUTABLE:%.*]] = function_ref @_T0s37_convertMutableArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_MUTABLE]]<Int, UnsafeMutableRawPointer>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_MUTABLE_RAW_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_MUTABLE_RAW_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
|
||||
takesConstRawPointer(ints)
|
||||
@@ -112,7 +119,8 @@ func arrayToPointer() {
|
||||
// CHECK: [[CONVERT_CONST:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_CONST]]<Int, UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
}
|
||||
|
||||
@@ -123,7 +131,8 @@ func stringToPointer(_ s: String) {
|
||||
// CHECK: [[CONVERT_STRING:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_STRING]]<UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_CONST_VOID_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_CONST_VOID_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
|
||||
takesConstRawPointer(s)
|
||||
@@ -131,7 +140,8 @@ func stringToPointer(_ s: String) {
|
||||
// CHECK: [[CONVERT_STRING:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArgument{{[_0-9a-zA-Z]*}}F
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT_STRING]]<UnsafeRawPointer>([[POINTER_BUF:%[0-9]*]],
|
||||
// CHECK: [[POINTER:%.*]] = load [trivial] [[POINTER_BUF]]
|
||||
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[POINTER]])
|
||||
// CHECK: [[DEPENDENT:%.*]] = mark_dependence [[POINTER]] : $UnsafeRawPointer on [[OWNER]]
|
||||
// CHECK: apply [[TAKES_CONST_RAW_POINTER]]([[DEPENDENT]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
}
|
||||
|
||||
@@ -267,3 +277,152 @@ func inoutPointerOrdering() {
|
||||
// CHECK: end_access [[ACCESS]]
|
||||
takesConstPointer(&array[sideEffect1()], and: sideEffect2())
|
||||
}
|
||||
|
||||
// rdar://problem/31542269
|
||||
// CHECK-LABEL: sil hidden @_T018pointer_conversion20optArrayToOptPointerySaySiGSg5array_tF
|
||||
func optArrayToOptPointer(array: [Int]?) {
|
||||
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion20takesOptConstPointerySPySiGSg_Si3andtF
|
||||
// CHECK: [[BORROW:%.*]] = begin_borrow %0
|
||||
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
|
||||
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
|
||||
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
|
||||
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
|
||||
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_BB]]:
|
||||
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
|
||||
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArguments9AnyObject_pSg_q_tSayxGs01_E0R_r0_lF
|
||||
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafePointer<Int>
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<Int, UnsafePointer<Int>>([[TEMP:%.*]], [[SOME_VALUE]])
|
||||
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
|
||||
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafePointer<Int> on [[OWNER]]
|
||||
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.some!enumelt.1, [[DEP]]
|
||||
// CHECK: dealloc_stack [[TEMP]]
|
||||
// CHECK: br [[CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafePointer<Int>>, [[OWNER]] : $Optional<AnyObject>)
|
||||
// CHECK: [[CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafePointer<Int>>, [[OWNER:%.*]] : $Optional<AnyObject>):
|
||||
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafePointer<Int>> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES]]([[OPTDEP]], [[RESULT1]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
// CHECK: end_borrow [[BORROW]]
|
||||
// CHECK: destroy_value %0
|
||||
// CHECK: [[NONE_BB]]:
|
||||
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.none
|
||||
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
|
||||
// CHECK: br [[CONT_BB]]([[NO_VALUE]] : $Optional<UnsafePointer<Int>>, [[NO_OWNER]] : $Optional<AnyObject>)
|
||||
takesOptConstPointer(array, and: sideEffect1())
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_T018pointer_conversion013optOptArrayTodD7PointerySaySiGSgSg5array_tF
|
||||
func optOptArrayToOptOptPointer(array: [Int]??) {
|
||||
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion08takesOptD12ConstPointerySPySiGSgSg_Si3andtF
|
||||
// CHECK: [[BORROW:%.*]] = begin_borrow %0
|
||||
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
|
||||
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
|
||||
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
|
||||
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
|
||||
// FIXME: this should really go somewhere that will make nil, not some(nil)
|
||||
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_BB]]:
|
||||
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
|
||||
// CHECK: [[T0:%.*]] = select_enum [[SOME_VALUE]]
|
||||
// CHECK: cond_br [[T0]], [[SOME_SOME_BB:bb[0-9]+]], [[SOME_NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_NONE_BB]]:
|
||||
// CHECK: destroy_value [[SOME_VALUE]]
|
||||
// CHECK: br [[NONE_BB]]
|
||||
// CHECK: [[SOME_SOME_BB]]:
|
||||
// CHECK: [[SOME_SOME_VALUE:%.*]] = unchecked_enum_data [[SOME_VALUE]]
|
||||
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s35_convertConstArrayToPointerArguments9AnyObject_pSg_q_tSayxGs01_E0R_r0_lF
|
||||
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafePointer<Int>
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<Int, UnsafePointer<Int>>([[TEMP:%.*]], [[SOME_SOME_VALUE]])
|
||||
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
|
||||
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafePointer<Int> on [[OWNER]]
|
||||
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.some!enumelt.1, [[DEP]]
|
||||
// CHECK: dealloc_stack [[TEMP]]
|
||||
// CHECK: br [[SOME_CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafePointer<Int>>, [[OWNER]] : $Optional<AnyObject>)
|
||||
// CHECK: [[SOME_CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafePointer<Int>>, [[OWNER:%.*]] : $Optional<AnyObject>):
|
||||
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafePointer<Int>> on [[OWNER]]
|
||||
// CHECK: [[OPTOPTPTR:%.*]] = enum $Optional<Optional<UnsafePointer<Int>>>, #Optional.some!enumelt.1, [[OPTDEP]]
|
||||
// CHECK: [[OPTOPTDEP:%.*]] = mark_dependence [[OPTOPTPTR]] : $Optional<Optional<UnsafePointer<Int>>> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES]]([[OPTOPTDEP]], [[RESULT1]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
// CHECK: end_borrow [[BORROW]]
|
||||
// CHECK: destroy_value %0
|
||||
// CHECK: [[NONE_BB]]:
|
||||
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafePointer<Int>>, #Optional.none
|
||||
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
|
||||
// CHECK: br [[SOME_CONT_BB]]([[NO_VALUE]] : $Optional<UnsafePointer<Int>>, [[NO_OWNER]] : $Optional<AnyObject>)
|
||||
takesOptOptConstPointer(array, and: sideEffect1())
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_T018pointer_conversion21optStringToOptPointerySSSg6string_tF
|
||||
func optStringToOptPointer(string: String?) {
|
||||
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion23takesOptConstRawPointerySVSg_Si3andtF
|
||||
// CHECK: [[BORROW:%.*]] = begin_borrow %0
|
||||
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
|
||||
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
|
||||
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
|
||||
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
|
||||
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_BB]]:
|
||||
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
|
||||
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArguments9AnyObject_pSg_xtSSs01_F0RzlF
|
||||
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafeRawPointer
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<UnsafeRawPointer>([[TEMP:%.*]], [[SOME_VALUE]])
|
||||
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
|
||||
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafeRawPointer on [[OWNER]]
|
||||
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt.1, [[DEP]]
|
||||
// CHECK: dealloc_stack [[TEMP]]
|
||||
// CHECK: br [[CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafeRawPointer>, [[OWNER]] : $Optional<AnyObject>)
|
||||
// CHECK: [[CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafeRawPointer>, [[OWNER:%.*]] : $Optional<AnyObject>):
|
||||
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafeRawPointer> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES]]([[OPTDEP]], [[RESULT1]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
// CHECK: end_borrow [[BORROW]]
|
||||
// CHECK: destroy_value %0
|
||||
// CHECK: [[NONE_BB]]:
|
||||
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.none
|
||||
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
|
||||
// CHECK: br [[CONT_BB]]([[NO_VALUE]] : $Optional<UnsafeRawPointer>, [[NO_OWNER]] : $Optional<AnyObject>)
|
||||
takesOptConstRawPointer(string, and: sideEffect1())
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @_T018pointer_conversion014optOptStringTodD7PointerySSSgSg6string_tF
|
||||
func optOptStringToOptOptPointer(string: String??) {
|
||||
// CHECK: [[TAKES:%.*]] = function_ref @_T018pointer_conversion08takesOptD15ConstRawPointerySVSgSg_Si3andtF
|
||||
// CHECK: [[BORROW:%.*]] = begin_borrow %0
|
||||
// CHECK: [[COPY:%.*]] = copy_value [[BORROW]]
|
||||
// CHECK: [[SIDE1:%.*]] = function_ref @_T018pointer_conversion11sideEffect1SiyF
|
||||
// CHECK: [[RESULT1:%.*]] = apply [[SIDE1]]()
|
||||
// CHECK: [[T0:%.*]] = select_enum [[COPY]]
|
||||
// FIXME: this should really go somewhere that will make nil, not some(nil)
|
||||
// CHECK: cond_br [[T0]], [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_BB]]:
|
||||
// CHECK: [[SOME_VALUE:%.*]] = unchecked_enum_data [[COPY]]
|
||||
// CHECK: [[T0:%.*]] = select_enum [[SOME_VALUE]]
|
||||
// CHECK: cond_br [[T0]], [[SOME_SOME_BB:bb[0-9]+]], [[SOME_NONE_BB:bb[0-9]+]]
|
||||
// CHECK: [[SOME_NONE_BB]]:
|
||||
// CHECK: destroy_value [[SOME_VALUE]]
|
||||
// CHECK: br [[NONE_BB]]
|
||||
// CHECK: [[SOME_SOME_BB]]:
|
||||
// CHECK: [[SOME_SOME_VALUE:%.*]] = unchecked_enum_data [[SOME_VALUE]]
|
||||
// CHECK: [[CONVERT:%.*]] = function_ref @_T0s40_convertConstStringToUTF8PointerArguments9AnyObject_pSg_xtSSs01_F0RzlF
|
||||
// CHECK: [[TEMP:%.*]] = alloc_stack $UnsafeRawPointer
|
||||
// CHECK: [[OWNER:%.*]] = apply [[CONVERT]]<UnsafeRawPointer>([[TEMP:%.*]], [[SOME_SOME_VALUE]])
|
||||
// CHECK: [[PTR:%.*]] = load [trivial] [[TEMP]]
|
||||
// CHECK: [[DEP:%.*]] = mark_dependence [[PTR]] : $UnsafeRawPointer on [[OWNER]]
|
||||
// CHECK: [[OPTPTR:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.some!enumelt.1, [[DEP]]
|
||||
// CHECK: dealloc_stack [[TEMP]]
|
||||
// CHECK: br [[SOME_CONT_BB:bb[0-9]+]]([[OPTPTR]] : $Optional<UnsafeRawPointer>, [[OWNER]] : $Optional<AnyObject>)
|
||||
// CHECK: [[SOME_CONT_BB]]([[OPTPTR:%.*]] : $Optional<UnsafeRawPointer>, [[OWNER:%.*]] : $Optional<AnyObject>):
|
||||
// CHECK: [[OPTDEP:%.*]] = mark_dependence [[OPTPTR]] : $Optional<UnsafeRawPointer> on [[OWNER]]
|
||||
// CHECK: [[OPTOPTPTR:%.*]] = enum $Optional<Optional<UnsafeRawPointer>>, #Optional.some!enumelt.1, [[OPTDEP]]
|
||||
// CHECK: [[OPTOPTDEP:%.*]] = mark_dependence [[OPTOPTPTR]] : $Optional<Optional<UnsafeRawPointer>> on [[OWNER]]
|
||||
// CHECK: apply [[TAKES]]([[OPTOPTDEP]], [[RESULT1]])
|
||||
// CHECK: destroy_value [[OWNER]]
|
||||
// CHECK: end_borrow [[BORROW]]
|
||||
// CHECK: destroy_value %0
|
||||
// CHECK: [[NONE_BB]]:
|
||||
// CHECK: [[NO_VALUE:%.*]] = enum $Optional<UnsafeRawPointer>, #Optional.none
|
||||
// CHECK: [[NO_OWNER:%.*]] = enum $Optional<AnyObject>, #Optional.none
|
||||
// CHECK: br [[SOME_CONT_BB]]([[NO_VALUE]] : $Optional<UnsafeRawPointer>, [[NO_OWNER]] : $Optional<AnyObject>)
|
||||
takesOptOptConstRawPointer(string, and: sideEffect1())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user