| //===--- 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 : uint8_t { |
| Invalid, |
| RValue, |
| LValue, |
| Expr, |
| }; |
| |
| struct RValueStorage { |
| RValue Value; |
| SILLocation Loc; |
| }; |
| struct LValueStorage { |
| LValue Value; |
| SILLocation Loc; |
| }; |
| |
| using StorageMembers = |
| ExternalUnionMembers<void, RValueStorage, LValueStorage, Expr*>; |
| |
| static StorageMembers::Index getStorageIndexForKind(Kind kind) { |
| switch (kind) { |
| case Kind::Invalid: return StorageMembers::indexOf<void>(); |
| case Kind::RValue: |
| return StorageMembers::indexOf<RValueStorage>(); |
| case Kind::LValue: return StorageMembers::indexOf<LValueStorage>(); |
| case Kind::Expr: return StorageMembers::indexOf<Expr*>(); |
| } |
| 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); |
| } |
| // 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: |
| return !asKnownRValue().isNull(); |
| case Kind::LValue: |
| return asKnownLValue().isValid(); |
| case Kind::Expr: |
| return asKnownExpr() != nullptr; |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| CanType getSubstRValueType() const & { |
| switch (StoredKind) { |
| case Kind::Invalid: |
| llvm_unreachable("argument source is invalid"); |
| case Kind::RValue: |
| return asKnownRValue().getType(); |
| case Kind::LValue: |
| return asKnownLValue().getSubstFormalType(); |
| case Kind::Expr: |
| return asKnownExpr()->getType()->getInOutObjectType()->getCanonicalType(); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| bool hasLValueType() const & { |
| switch (StoredKind) { |
| case Kind::Invalid: llvm_unreachable("argument source is invalid"); |
| case Kind::RValue: |
| return false; |
| case Kind::LValue: return true; |
| case Kind::Expr: return asKnownExpr()->isSemanticallyInOutExpr(); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| SILLocation getLocation() const & { |
| switch (StoredKind) { |
| case Kind::Invalid: |
| llvm_unreachable("argument source is invalid"); |
| case Kind::RValue: |
| return getKnownRValueLocation(); |
| case Kind::LValue: |
| return getKnownLValueLocation(); |
| case Kind::Expr: |
| return asKnownExpr(); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| bool isExpr() const & { return StoredKind == Kind::Expr; } |
| bool isRValue() const & { return StoredKind == Kind::RValue; } |
| bool isLValue() const & { return StoredKind == Kind::LValue; } |
| |
| /// Whether this argument is for a default argument that should be delayed. |
| /// Note that this will return false for caller-side default arguments which |
| /// are emitted directly. |
| bool isDelayedDefaultArg() const { |
| switch (StoredKind) { |
| case Kind::Invalid: |
| llvm_unreachable("argument source is invalid"); |
| case Kind::RValue: |
| case Kind::LValue: |
| return false; |
| case Kind::Expr: { |
| auto *defaultArg = dyn_cast<DefaultArgumentExpr>(asKnownExpr()); |
| if (!defaultArg) |
| return false; |
| return !defaultArg->isCallerSide(); |
| } |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| /// Return the default argument owner and parameter index, consuming |
| /// the argument source. Will assert if this is not a default argument. |
| DefaultArgumentExpr *asKnownDefaultArg() && { |
| return cast<DefaultArgumentExpr>(std::move(*this).asKnownExpr()); |
| } |
| |
| /// Given that this source is storing an RValue, extract and clear |
| /// that value. |
| RValue &&asKnownRValue(SILGenFunction &SGF) && { |
| return std::move(Storage.get<RValueStorage>(StoredKind).Value); |
| } |
| const RValue &asKnownRValue() const & { |
| return 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); |
| } |
| const LValue &asKnownLValue() const & { |
| return Storage.get<LValueStorage>(StoredKind).Value; |
| } |
| SILLocation getKnownLValueLocation() const & { |
| return Storage.get<LValueStorage>(StoredKind).Loc; |
| } |
| |
| Expr *findStorageReferenceExprForBorrow() &&; |
| |
| /// 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; |
| } |
| |
| /// 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, |
| SILType loweredResultTy, |
| 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 &; |
| |
| 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 getSubstRValueType() under the |
| /// abstractions of origFormalType |
| ManagedValue materialize(SILGenFunction &SGF, |
| AbstractionPattern origFormalType, |
| SILType expectedType = SILType()) &&; |
| |
| bool isObviouslyEqual(const ArgumentSource &other) const; |
| |
| ArgumentSource copyForDiagnostics() 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); |
| |
| // Make this non-move accessor private to make it more difficult |
| // to accidentally re-emit values. |
| Expr *asKnownExpr() const & { |
| return Storage.get<Expr*>(StoredKind); |
| } |
| }; |
| |
| class PreparedArguments { |
| SmallVector<AnyFunctionType::Param, 8> Params; |
| std::vector<ArgumentSource> Arguments; |
| unsigned IsNull : 1; |
| public: |
| PreparedArguments() : IsNull(true) {} |
| explicit PreparedArguments(ArrayRef<AnyFunctionType::Param> params) |
| : IsNull(true) { |
| emplace(params); |
| } |
| |
| // Decompse an argument list expression. |
| PreparedArguments(ArrayRef<AnyFunctionType::Param> params, Expr *arg); |
| |
| // Move-only. |
| PreparedArguments(const PreparedArguments &) = delete; |
| PreparedArguments &operator=(const PreparedArguments &) = delete; |
| |
| PreparedArguments(PreparedArguments &&other) |
| : Params(std::move(other.Params)), Arguments(std::move(other.Arguments)), |
| IsNull(other.IsNull) {} |
| PreparedArguments &operator=(PreparedArguments &&other) { |
| Params = std::move(other.Params); |
| Arguments = std::move(other.Arguments); |
| IsNull = other.IsNull; |
| other.IsNull = true; |
| return *this; |
| } |
| |
| /// Returns true if this is a null argument list. Note that this always |
| /// indicates the total absence of an argument list rather than the |
| /// possible presence of an empty argument list. |
| bool isNull() const { return IsNull; } |
| |
| /// Returns true if this is a non-null and completed argument list. |
| bool isValid() const { |
| assert(!isNull()); |
| return Arguments.size() == Params.size(); |
| } |
| |
| /// Return the formal type of this argument list. |
| ArrayRef<AnyFunctionType::Param> getParams() const { |
| assert(!isNull()); |
| return Params; |
| } |
| |
| MutableArrayRef<ArgumentSource> getSources() && { |
| assert(isValid()); |
| return Arguments; |
| } |
| |
| /// Emplace a (probably incomplete) argument list. |
| void emplace(ArrayRef<AnyFunctionType::Param> params) { |
| assert(isNull()); |
| Params.append(params.begin(), params.end()); |
| IsNull = false; |
| } |
| |
| /// Add an emitted r-value argument to this argument list. |
| void add(SILLocation loc, RValue &&arg) { |
| assert(!isNull()); |
| Arguments.emplace_back(loc, std::move(arg)); |
| } |
| |
| /// Add an arbitrary argument source to these arguments. |
| /// |
| /// An argument list with an arbtrary argument source can't generally |
| /// be copied. |
| void addArbitrary(ArgumentSource &&arg) { |
| assert(!isNull()); |
| Arguments.emplace_back(std::move(arg)); |
| } |
| |
| /// Copy these prepared arguments. This propagates null. |
| PreparedArguments copy(SILGenFunction &SGF, SILLocation loc) const; |
| |
| bool isObviouslyEqual(const PreparedArguments &other) const; |
| |
| PreparedArguments copyForDiagnostics() const; |
| }; |
| |
| } // end namespace Lowering |
| } // end namespace swift |
| |
| #endif |