blob: 63f4417dc6faa6e42d6328832d20b5bfd4bfed80 [file] [log] [blame]
//===--- ArgumentSource.h - Abstracted source of an argument ----*- 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
//
//===----------------------------------------------------------------------===//
//
// A structure for holding an abstracted source of a call argument.
// It's either:
//
// - an expression, yielding an l-value or r-value
// - an RValue
// - an LValue
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_LOWERING_ARGUMENTSOURCE_H
#define SWIFT_LOWERING_ARGUMENTSOURCE_H
#include "swift/Basic/ExternalUnion.h"
#include "RValue.h"
#include "LValue.h"
namespace swift {
namespace Lowering {
class Conversion;
/// A means of generating an argument.
///
/// This is useful as a way to pass values around without either:
/// - requiring them to have already been evaluated or
/// - requiring them to come from an identifiable expression.
///
/// Being able to propagate values is important because there are a
/// number of cases (involving, say, property accessors) where values
/// are implicitly or previously generated. However, being able to
/// propagate Expr*s is also important because there are several kinds
/// of expressions (such as closures) which can be emitted more
/// efficiently with a known target abstraction level.
///
/// Because an ArgumentSource might contain an unevaluated expression,
/// care must be taken when dealing with multiple ArgumentSources to
/// preserve the original evaluation order of the program. APIs
/// working with multiple ArgumentSources should document the order in
/// which they plan to evaluate them.
class ArgumentSource {
enum class Kind : unsigned char {
Invalid,
RValue,
// An RValue that will be borrowed when emitted.
DelayedBorrowedRValue,
LValue,
Expr,
Tuple,
};
struct RValueStorage {
RValue Value;
SILLocation Loc;
};
struct LValueStorage {
LValue Value;
SILLocation Loc;
};
struct TupleStorage {
CanTupleType SubstType;
SILLocation Loc;
std::vector<ArgumentSource> Elements;
TupleStorage(CanTupleType type, SILLocation loc,
MutableArrayRef<ArgumentSource> elements)
: SubstType(type), Loc(loc) {
assert(type->getNumElements() == elements.size());
Elements.reserve(elements.size());
for (auto i : indices(elements)) {
Elements.push_back(std::move(elements[i]));
}
}
};
using StorageMembers =
ExternalUnionMembers<void, RValueStorage, LValueStorage,
Expr*, TupleStorage>;
static StorageMembers::Index getStorageIndexForKind(Kind kind) {
switch (kind) {
case Kind::Invalid: return StorageMembers::indexOf<void>();
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return StorageMembers::indexOf<RValueStorage>();
case Kind::LValue: return StorageMembers::indexOf<LValueStorage>();
case Kind::Expr: return StorageMembers::indexOf<Expr*>();
case Kind::Tuple: return StorageMembers::indexOf<TupleStorage>();
}
llvm_unreachable("bad kind");
}
ExternalUnion<Kind, StorageMembers, getStorageIndexForKind> Storage;
Kind StoredKind;
public:
ArgumentSource() : StoredKind(Kind::Invalid) {}
ArgumentSource(SILLocation loc, RValue &&value) : StoredKind(Kind::RValue) {
Storage.emplaceAggregate<RValueStorage>(StoredKind, std::move(value), loc);
}
ArgumentSource(SILLocation loc, LValue &&value) : StoredKind(Kind::LValue) {
Storage.emplaceAggregate<LValueStorage>(StoredKind, std::move(value), loc);
}
ArgumentSource(Expr *e) : StoredKind(Kind::Expr) {
assert(e && "initializing ArgumentSource with null expression");
Storage.emplace<Expr*>(StoredKind, e);
}
ArgumentSource(SILLocation loc, CanTupleType type,
MutableArrayRef<ArgumentSource> elements)
: StoredKind(Kind::Tuple) {
Storage.emplace<TupleStorage>(StoredKind, type, loc, elements);
}
// Cannot be copied.
ArgumentSource(const ArgumentSource &other) = delete;
ArgumentSource &operator=(const ArgumentSource &other) = delete;
// Can be moved.
ArgumentSource(ArgumentSource &&other) : StoredKind(other.StoredKind) {
Storage.moveConstruct(StoredKind, std::move(other.Storage));
}
ArgumentSource &operator=(ArgumentSource &&other) {
Storage.moveAssign(StoredKind, other.StoredKind, std::move(other.Storage));
StoredKind = other.StoredKind;
other.Storage.destruct(other.StoredKind);
other.StoredKind = Kind::Invalid;
return *this;
}
~ArgumentSource() {
Storage.destruct(StoredKind);
}
explicit operator bool() const & {
switch (StoredKind) {
case Kind::Invalid:
return false;
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return !asKnownRValue().isNull();
case Kind::LValue:
return asKnownLValue().isValid();
case Kind::Expr:
return asKnownExpr() != nullptr;
case Kind::Tuple:
return true;
}
llvm_unreachable("bad kind");
}
CanType getSubstType() const & {
switch (StoredKind) {
case Kind::Invalid:
llvm_unreachable("argument source is invalid");
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return asKnownRValue().getType();
case Kind::LValue:
return CanInOutType::get(asKnownLValue().getSubstFormalType());
case Kind::Expr:
return asKnownExpr()->getType()->getCanonicalType();
case Kind::Tuple:
return Storage.get<TupleStorage>(StoredKind).SubstType;
}
llvm_unreachable("bad kind");
}
SILType getSILSubstType(SILGenFunction &SGF) const &;
CanType getSubstRValueType() const & {
switch (StoredKind) {
case Kind::Invalid:
llvm_unreachable("argument source is invalid");
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return asKnownRValue().getType();
case Kind::LValue:
return asKnownLValue().getSubstFormalType();
case Kind::Expr:
return asKnownExpr()->getType()->getInOutObjectType()->getCanonicalType();
case Kind::Tuple:
return Storage.get<TupleStorage>(StoredKind).SubstType;
}
llvm_unreachable("bad kind");
}
SILType getSILSubstRValueType(SILGenFunction &SGF) const &;
bool hasLValueType() const & {
switch (StoredKind) {
case Kind::Invalid: llvm_unreachable("argument source is invalid");
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return false;
case Kind::LValue: return true;
case Kind::Expr: return asKnownExpr()->isSemanticallyInOutExpr();
case Kind::Tuple: return false;
}
llvm_unreachable("bad kind");
}
SILLocation getLocation() const & {
switch (StoredKind) {
case Kind::Invalid:
llvm_unreachable("argument source is invalid");
case Kind::RValue:
case Kind::DelayedBorrowedRValue:
return getKnownRValueLocation();
case Kind::LValue:
return getKnownLValueLocation();
case Kind::Expr:
return asKnownExpr();
case Kind::Tuple:
return getKnownTupleLocation();
}
llvm_unreachable("bad kind");
}
bool isExpr() const & { return StoredKind == Kind::Expr; }
bool isRValue() const & {
return StoredKind == Kind::RValue ||
StoredKind == Kind::DelayedBorrowedRValue;
}
bool isLValue() const & { return StoredKind == Kind::LValue; }
bool isTuple() const & { return StoredKind == Kind::Tuple; }
/// Given that this source is storing an RValue, extract and clear
/// that value.
RValue &&asKnownRValue(SILGenFunction &SGF) && {
if (isDelayedBorrowedRValue()) {
std::move(Storage.get<RValueStorage>(StoredKind).Value)
.borrow(SGF, getKnownRValueLocation());
}
return std::move(Storage.get<RValueStorage>(StoredKind).Value);
}
SILLocation getKnownRValueLocation() const & {
return Storage.get<RValueStorage>(StoredKind).Loc;
}
/// Given that this source is storing an LValue, extract and clear
/// that value.
LValue &&asKnownLValue() && {
return std::move(Storage.get<LValueStorage>(StoredKind).Value);
}
SILLocation getKnownLValueLocation() const & {
return Storage.get<LValueStorage>(StoredKind).Loc;
}
/// Given that this source is an expression, extract and clear
/// that expression.
Expr *asKnownExpr() && {
Expr *result = Storage.get<Expr*>(StoredKind);
Storage.resetToEmpty<Expr*>(StoredKind, Kind::Invalid);
StoredKind = Kind::Invalid;
return result;
}
SILLocation getKnownTupleLocation() const & {
return Storage.get<TupleStorage>(StoredKind).Loc;
}
template <class ResultType>
ResultType withKnownTupleElementSources(
llvm::function_ref<ResultType(SILLocation loc, CanTupleType type,
MutableArrayRef<ArgumentSource> elts)> callback) && {
auto &tuple = Storage.get<TupleStorage>(StoredKind);
auto result = callback(tuple.Loc, tuple.SubstType, tuple.Elements);
// We've consumed the tuple.
Storage.resetToEmpty<TupleStorage>(StoredKind, Kind::Invalid);
StoredKind = Kind::Invalid;
return result;
}
/// Force this source to become an r-value, then return an unmoved
/// handle to that r-value.
RValue &forceAndPeekRValue(SILGenFunction &SGF) &;
/// Return an unowned handle to the r-value stored in this source. Undefined
/// if this ArgumentSource is not an rvalue.
RValue &peekRValue() &;
RValue getAsRValue(SILGenFunction &SGF, SGFContext C = SGFContext()) &&;
ManagedValue getAsSingleValue(SILGenFunction &SGF,
SGFContext C = SGFContext()) &&;
ManagedValue getAsSingleValue(SILGenFunction &SGF,
AbstractionPattern origFormalType,
SGFContext C = SGFContext()) &&;
ManagedValue getConverted(SILGenFunction &SGF, const Conversion &conversion,
SGFContext C = SGFContext()) &&;
void forwardInto(SILGenFunction &SGF, Initialization *dest) &&;
void forwardInto(SILGenFunction &SGF, AbstractionPattern origFormalType,
Initialization *dest, const TypeLowering &destTL) &&;
/// If we have an rvalue, borrow the rvalue into a new ArgumentSource and
/// return the ArgumentSource. Otherwise, assert.
ArgumentSource borrow(SILGenFunction &SGF) const &;
/// If we have an rvalue, return an Argument Source that when the RValue is
/// retrieved, the RValue is always borrowed first.
///
/// This allows us to specify when creating callees that a value must be
/// borrowed, but emit the actual borrow once the callee is evaluated later in
/// SILGenApply. Ideally, the callee would always eagerly borrow, but since we
/// still have uncurrying, we can not do that.
ArgumentSource delayedBorrow(SILGenFunction &SGF) const &;
ManagedValue materialize(SILGenFunction &SGF) &&;
/// Emit this value to memory so that it follows the abstraction
/// patterns of the original formal type.
///
/// \param expectedType - the lowering of getSubstType() under the
/// abstractions of origFormalType
ManagedValue materialize(SILGenFunction &SGF,
AbstractionPattern origFormalType,
SILType expectedType = SILType()) &&;
// This is a hack and should be avoided.
void rewriteType(CanType newType) &;
/// Whether this argument source requires the callee to evaluate.
bool requiresCalleeToEvaluate() const;
void dump() const;
void dump(raw_ostream &os, unsigned indent = 0) const;
private:
/// Private helper constructor for delayed borrowed rvalues.
ArgumentSource(SILLocation loc, RValue &&rv, Kind kind);
/// Returns true if this ArgumentSource stores a delayed borrowed RValue.
///
/// This is private since we do not want users to be able to determine if the
/// given ArgumentSource is a normal RValue or a delayed borrow rvalue.
bool isDelayedBorrowedRValue() const & {
return StoredKind == Kind::DelayedBorrowedRValue;
}
// Make the non-move accessors private to make it more difficult
// to accidentally re-emit values.
const RValue &asKnownRValue() const & {
return Storage.get<RValueStorage>(StoredKind).Value;
}
// Make the non-move accessors private to make it more difficult
// to accidentally re-emit values.
const LValue &asKnownLValue() const & {
return Storage.get<LValueStorage>(StoredKind).Value;
}
Expr *asKnownExpr() const & {
return Storage.get<Expr*>(StoredKind);
}
RValue getKnownTupleAsRValue(SILGenFunction &SGF, SGFContext C) &&;
};
} // end namespace Lowering
} // end namespace swift
#endif