Files
swift-mirror/lib/SILGen/SILGenLValue.cpp
Slava Pestov 2dbeeb0d3f AST: Make SubstFlags::UseErrorType the default behavior
We've fixed a number of bugs recently where callers did not expect
to get a null Type out of subst(). This occurs particularly often
in SourceKit, where the input AST is often invalid and the types
resulting from substitution are mostly used for display.

Let's fix all these potential problems in one fell swoop by changing
subst() to always return a Type, possibly one containing ErrorTypes.

Only a couple of places depended on the old behavior, and they were
easy enough to change from checking for a null Type to checking if
the result responds with true to hasError().

Also while we're at it, simplify a few call sites of subst().
2019-08-22 01:07:50 -04:00

4364 lines
175 KiB
C++

//===--- SILGenLValue.cpp - Constructs logical lvalues for SILGen ---------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Emission of l-value expressions and basic operations on them.
//
//===----------------------------------------------------------------------===//
#include "ASTVisitor.h"
#include "ArgumentScope.h"
#include "ArgumentSource.h"
#include "Conversion.h"
#include "Initialization.h"
#include "LValue.h"
#include "RValue.h"
#include "SILGen.h"
#include "Scope.h"
#include "swift/AST/DiagnosticsCommon.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/MemAccessUtils.h"
#include "swift/SIL/PrettyStackTrace.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SIL/TypeLowering.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift;
using namespace Lowering;
//===----------------------------------------------------------------------===//
namespace {
struct LValueWritebackCleanup : Cleanup {
FormalEvaluationContext::stable_iterator Depth;
LValueWritebackCleanup() : Depth() {}
void emit(SILGenFunction &SGF, CleanupLocation loc,
ForUnwind_t forUnwind) override {
FullExpr scope(SGF.Cleanups, loc);
// TODO: honor forUnwind!
auto &evaluation = getEvaluation(SGF);
evaluation.performWriteback(SGF, /*isFinal*/ false);
}
void dump(SILGenFunction &) const override {
#ifndef NDEBUG
llvm::errs() << "LValueWritebackCleanup\n"
<< "State: " << getState() << " Depth: " << Depth.getDepth()
<< "\n";
#endif
}
private:
ExclusiveBorrowFormalAccess &getEvaluation(SILGenFunction &SGF) {
auto &evaluation = *SGF.FormalEvalContext.find(Depth);
assert(evaluation.getKind() == FormalAccess::Exclusive);
return static_cast<ExclusiveBorrowFormalAccess &>(evaluation);
}
};
} // end anonymous namespace
/// Push a writeback onto the current LValueWriteback stack.
static void pushWriteback(SILGenFunction &SGF,
SILLocation loc,
std::unique_ptr<LogicalPathComponent> &&comp,
ManagedValue base,
MaterializedLValue materialized) {
assert(SGF.isInFormalEvaluationScope());
// Push a cleanup to execute the writeback consistently.
auto &context = SGF.FormalEvalContext;
LValueWritebackCleanup &cleanup =
SGF.Cleanups.pushCleanup<LValueWritebackCleanup>();
CleanupHandle handle = SGF.Cleanups.getTopCleanup();
context.push<ExclusiveBorrowFormalAccess>(loc, std::move(comp), base,
materialized, handle);
cleanup.Depth = context.stable_begin();
}
static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2);
void ExclusiveBorrowFormalAccess::diagnoseConflict(
const ExclusiveBorrowFormalAccess &rhs,
SILGenFunction &SGF) const {
// If the two writebacks we're comparing are of different kinds (e.g.
// ownership conversion vs a computed property) then they aren't the
// same and thus cannot conflict.
if (component->getKind() != rhs.component->getKind())
return;
// If the lvalues don't have the same base value (possibly null), then
// they aren't the same. Note that this is the primary source of false
// negative for this diagnostic.
SILValue lhsBaseValue = base.getValue(), rhsBaseValue = rhs.base.getValue();
if (lhsBaseValue != rhsBaseValue &&
(!lhsBaseValue || !rhsBaseValue ||
!RValue::areObviouslySameValue(lhsBaseValue, rhsBaseValue))) {
return;
}
auto lhsStorage = component->getAccessedStorage();
if (!lhsStorage) return;
auto rhsStorage = rhs.component->getAccessedStorage();
if (!rhsStorage) return;
// If the decls match, then this could conflict.
if (lhsStorage->Storage != rhsStorage->Storage ||
!lhsStorage->Storage ||
lhsStorage->IsSuper != rhsStorage->IsSuper)
return;
assert((lhsStorage->Indices != nullptr) == (rhsStorage->Indices != nullptr));
auto storage = lhsStorage->Storage;
// If the decl is monomorphically a stored property, allow aliases.
// It could be overridden by a computed property in a subclass, but
// that's not likely enough to be worth the strictness here.
auto impl = storage->getImplInfo();
// TODO: Stored properties with didSet accessors that don't look at the
// oldValue could also be addressed.
if ((impl.getReadImpl() == ReadImplKind::Stored ||
impl.getReadImpl() == ReadImplKind::Address) &&
(impl.getWriteImpl() == WriteImplKind::Immutable ||
impl.getWriteImpl() == WriteImplKind::Stored ||
impl.getWriteImpl() == WriteImplKind::MutableAddress)) {
return;
}
// If the property is a generic requirement, allow aliases, because
// it may be conformed to using a stored property.
if (isa<ProtocolDecl>(storage->getDeclContext()))
return;
// If this is a simple property access, then we must have a conflict.
if (!lhsStorage->Indices) {
assert(isa<VarDecl>(storage));
SGF.SGM.diagnose(loc, diag::writeback_overlap_property,
storage->getBaseName().getIdentifier())
.highlight(loc.getSourceRange());
SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
.highlight(rhs.loc.getSourceRange());
return;
}
// Otherwise, it is a subscript, check the index values.
// If the indices are literally identical SILValue's, then there is
// clearly a conflict.
if (!lhsStorage->Indices->isObviouslyEqual(*rhsStorage->Indices)) {
// If the index value doesn't lower to literally the same SILValue's,
// do some fuzzy matching to catch the common case.
if (!lhsStorage->IndexExprForDiagnostics ||
!rhsStorage->IndexExprForDiagnostics ||
!areCertainlyEqualIndices(lhsStorage->IndexExprForDiagnostics,
rhsStorage->IndexExprForDiagnostics))
return;
}
// The locations for the subscripts are almost certainly SubscriptExprs.
// If so, dig into them to produce better location info in the
// diagnostics and be able to do more precise analysis.
auto expr1 = loc.getAsASTNode<SubscriptExpr>();
auto expr2 = rhs.loc.getAsASTNode<SubscriptExpr>();
if (expr1 && expr2) {
SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript)
.highlight(expr1->getBase()->getSourceRange());
SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
.highlight(expr2->getBase()->getSourceRange());
} else {
SGF.SGM.diagnose(loc, diag::writeback_overlap_subscript)
.highlight(loc.getSourceRange());
SGF.SGM.diagnose(rhs.loc, diag::writebackoverlap_note)
.highlight(rhs.loc.getSourceRange());
}
}
//===----------------------------------------------------------------------===//
static CanType getSubstFormalRValueType(Expr *expr) {
return expr->getType()->getRValueType()->getCanonicalType();
}
static LValueTypeData getAbstractedTypeData(SILGenModule &SGM,
SGFAccessKind accessKind,
AbstractionPattern origFormalType,
CanType substFormalType) {
return {
accessKind,
origFormalType,
substFormalType,
SGM.Types.getLoweredRValueType(origFormalType, substFormalType)
};
}
static LValueTypeData getLogicalStorageTypeData(SILGenModule &SGM,
SGFAccessKind accessKind,
CanType substFormalType) {
assert(!isa<ReferenceStorageType>(substFormalType));
AbstractionPattern origFormalType(
substFormalType.getReferenceStorageReferent());
return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType);
}
static LValueTypeData getPhysicalStorageTypeData(SILGenModule &SGM,
SGFAccessKind accessKind,
AbstractStorageDecl *storage,
CanType substFormalType) {
assert(!isa<ReferenceStorageType>(substFormalType));
auto origFormalType = SGM.Types.getAbstractionPattern(storage)
.getReferenceStorageReferentType();
return getAbstractedTypeData(SGM, accessKind, origFormalType, substFormalType);
}
static bool shouldUseUnsafeEnforcement(VarDecl *var) {
if (var->isDebuggerVar())
return true;
// TODO: Check for the explicit "unsafe" attribute.
return false;
}
Optional<SILAccessEnforcement>
SILGenFunction::getStaticEnforcement(VarDecl *var) {
if (var && shouldUseUnsafeEnforcement(var))
return SILAccessEnforcement::Unsafe;
return SILAccessEnforcement::Static;
}
Optional<SILAccessEnforcement>
SILGenFunction::getDynamicEnforcement(VarDecl *var) {
if (getOptions().EnforceExclusivityDynamic) {
if (var && shouldUseUnsafeEnforcement(var))
return SILAccessEnforcement::Unsafe;
return SILAccessEnforcement::Dynamic;
}
return None;
}
Optional<SILAccessEnforcement>
SILGenFunction::getUnknownEnforcement(VarDecl *var) {
if (var && shouldUseUnsafeEnforcement(var))
return SILAccessEnforcement::Unsafe;
return SILAccessEnforcement::Unknown;
}
/// SILGenLValue - An ASTVisitor for building logical lvalues.
class LLVM_LIBRARY_VISIBILITY SILGenLValue
: public Lowering::ExprVisitor<SILGenLValue, LValue,
SGFAccessKind, LValueOptions>
{
public:
SILGenFunction &SGF;
SILGenLValue(SILGenFunction &SGF) : SGF(SGF) {}
LValue visitRec(Expr *e, SGFAccessKind accessKind, LValueOptions options,
AbstractionPattern orig = AbstractionPattern::getInvalid());
/// Dummy handler to log unimplemented nodes.
LValue visitExpr(Expr *e, SGFAccessKind accessKind, LValueOptions options);
// Nodes that form the root of lvalue paths
LValue visitDiscardAssignmentExpr(DiscardAssignmentExpr *e,
SGFAccessKind accessKind,
LValueOptions options);
LValue visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitOpaqueValueExpr(OpaqueValueExpr *e, SGFAccessKind accessKind,
LValueOptions options);
// Nodes that make up components of lvalue paths
LValue visitMemberRefExpr(MemberRefExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitSubscriptExpr(SubscriptExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitTupleElementExpr(TupleElementExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitForceValueExpr(ForceValueExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitBindOptionalExpr(BindOptionalExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitOpenExistentialExpr(OpenExistentialExpr *e,
SGFAccessKind accessKind,
LValueOptions options);
LValue visitKeyPathApplicationExpr(KeyPathApplicationExpr *e,
SGFAccessKind accessKind,
LValueOptions options);
// Expressions that wrap lvalues
LValue visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind,
LValueOptions options);
LValue visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e,
SGFAccessKind accessKind,
LValueOptions options);
};
/// Read this component.
ManagedValue
LogicalPathComponent::projectForRead(SILGenFunction &SGF, SILLocation loc,
ManagedValue base,
SGFAccessKind accessKind) && {
assert(isReadAccess(accessKind));
const TypeLowering &RValueTL = SGF.getTypeLowering(getTypeOfRValue());
// If the access doesn't require us to make an owned address, don't
// force a materialization.
if (!isReadAccessResultAddress(accessKind)) {
auto rvalue = std::move(*this).get(SGF, loc, base, SGFContext());
return std::move(rvalue).getAsSingleValue(SGF, loc);
}
TemporaryInitializationPtr tempInit;
RValue rvalue;
// If the RValue type has an openedExistential, then the RValue must be
// materialized before allocating a temporary for the RValue type. In that
// case, the RValue cannot be emitted directly into the temporary.
if (getTypeOfRValue().hasOpenedExistential()) {
// Emit a 'get'.
rvalue = std::move(*this).get(SGF, loc, base, SGFContext());
// Create a temporary, whose type may depend on the 'get'.
tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL);
} else {
// Create a temporary for a static (non-dependent) RValue type.
tempInit = SGF.emitFormalAccessTemporary(loc, RValueTL);
// Emit a 'get' directly into the temporary.
rvalue = std::move(*this).get(SGF, loc, base, SGFContext(tempInit.get()));
}
// `this` is now dead.
// Force `value` into a temporary if is wasn't emitted there.
if (!rvalue.isInContext())
std::move(rvalue).forwardInto(SGF, loc, tempInit.get());
return tempInit->getManagedAddress();
}
ManagedValue LogicalPathComponent::project(SILGenFunction &SGF,
SILLocation loc,
ManagedValue base) && {
auto accessKind = getAccessKind();
if (isReadAccess(accessKind))
return std::move(*this).projectForRead(SGF, loc, base, accessKind);
// AccessKind is Write or ReadWrite. We need to emit a get and set.
assert(SGF.isInFormalEvaluationScope() &&
"materializing l-value for modification without writeback scope");
// Clone anything else about the component that we might need in the
// writeback.
auto clonedComponent = clone(SGF, loc);
ManagedValue temp =
std::move(*this).projectForRead(SGF, loc, base,
SGFAccessKind::OwnedAddressRead);
if (SGF.getOptions().VerifyExclusivity) {
// Begin an access of the temporary. It is unenforced because enforcement
// isn't required for RValues.
SILValue accessAddress = UnenforcedFormalAccess::enter(
SGF, loc, temp.getValue(), SILAccessKind::Modify);
temp = std::move(temp).transform(accessAddress);
}
// Push a writeback for the temporary.
pushWriteback(SGF, loc, std::move(clonedComponent), base,
MaterializedLValue(temp));
return ManagedValue::forLValue(temp.getValue());
}
void LogicalPathComponent::writeback(SILGenFunction &SGF, SILLocation loc,
ManagedValue base,
MaterializedLValue materialized,
bool isFinal) {
assert(!materialized.callback &&
"unexpected materialized lvalue with callback!");
// Load the value from the temporary unless the type is address-only
// and this is the final use, in which case we can just consume the
// value as-is.
auto temporary = materialized.temporary;
assert(temporary.getType().isAddress());
auto &tempTL = SGF.getTypeLowering(temporary.getType());
if (!tempTL.isAddressOnly() || !isFinal ||
!SGF.silConv.useLoweredAddresses()) {
if (isFinal) temporary.forward(SGF);
temporary = SGF.emitLoad(loc, temporary.getValue(), tempTL,
SGFContext(), IsTake_t(isFinal));
}
RValue rvalue(SGF, loc, getSubstFormalType(), temporary);
// Don't consume cleanups on the base if this isn't final.
if (base && !isFinal) { base = ManagedValue::forUnmanaged(base.getValue()); }
// Clone the component if this isn't final.
std::unique_ptr<LogicalPathComponent> clonedComponent =
(isFinal ? nullptr : clone(SGF, loc));
LogicalPathComponent *component = (isFinal ? this : &*clonedComponent);
std::move(*component).set(SGF, loc, ArgumentSource(loc, std::move(rvalue)),
base);
}
InOutConversionScope::InOutConversionScope(SILGenFunction &SGF)
: SGF(SGF)
{
assert(SGF.isInFormalEvaluationScope()
&& "inout conversions should happen in writeback scopes");
assert(!SGF.InInOutConversionScope
&& "inout conversions should not be nested");
SGF.InInOutConversionScope = true;
}
InOutConversionScope::~InOutConversionScope() {
assert(SGF.InInOutConversionScope && "already exited conversion scope?!");
SGF.InInOutConversionScope = false;
}
void PathComponent::_anchor() {}
void PhysicalPathComponent::_anchor() {}
void PathComponent::dump() const {
dump(llvm::errs());
}
/// Return the LValueTypeData for a SIL value with the given AST formal type.
static LValueTypeData getValueTypeData(SGFAccessKind accessKind,
CanType formalType, SILValue value) {
return {
accessKind,
AbstractionPattern(formalType),
formalType,
value->getType().getASTType(),
};
}
static LValueTypeData getValueTypeData(SILGenFunction &SGF,
SGFAccessKind accessKind, Expr *e) {
CanType formalType = getSubstFormalRValueType(e);
CanType loweredType = SGF.getLoweredType(formalType).getASTType();
return {
accessKind,
AbstractionPattern(formalType),
formalType,
loweredType
};
}
/// Given the address of an optional value, unsafely project out the
/// address of the value.
static ManagedValue getAddressOfOptionalValue(SILGenFunction &SGF,
SILLocation loc,
ManagedValue optAddr,
const LValueTypeData &valueTypeData) {
// Project out the 'Some' payload.
EnumElementDecl *someDecl = SGF.getASTContext().getOptionalSomeDecl();
// If the base is +1, we want to forward the cleanup.
bool hadCleanup = optAddr.hasCleanup();
// UncheckedTakeEnumDataAddr is safe to apply to Optional, because it is
// a single-payload enum. There will (currently) never be spare bits
// embedded in the payload.
SILValue valueAddr =
SGF.B.createUncheckedTakeEnumDataAddr(loc, optAddr.forward(SGF), someDecl,
SILType::getPrimitiveAddressType(
valueTypeData.TypeOfRValue));
// Return the value as +1 if the optional was +1.
if (hadCleanup) {
return SGF.emitManagedBufferWithCleanup(valueAddr);
} else {
return ManagedValue::forLValue(valueAddr);
}
}
namespace {
/// A helper class for creating writebacks associated with l-value
/// components that don't really need them.
class WritebackPseudoComponent : public LogicalPathComponent {
protected:
WritebackPseudoComponent(const LValueTypeData &typeData)
: LogicalPathComponent(typeData, WritebackPseudoKind) {}
public:
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation l) const override {
llvm_unreachable("called clone on pseudo-component");
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext c) && override {
llvm_unreachable("called get on a pseudo-component");
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&value, ManagedValue base) && override {
llvm_unreachable("called set on a pseudo-component");
}
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
llvm_unreachable("called project on a pseudo-component");
}
Optional<AccessedStorage> getAccessedStorage() const override {
return None;
}
};
class EndAccessPseudoComponent : public WritebackPseudoComponent {
public:
EndAccessPseudoComponent(const LValueTypeData &typeData)
: WritebackPseudoComponent(typeData) {}
private:
void writeback(SILGenFunction &SGF, SILLocation loc,
ManagedValue base,
MaterializedLValue materialized,
bool isFinal) override {
loc.markAutoGenerated();
assert(base.isLValue());
SGF.B.createEndAccess(loc, base.getValue(), /*abort*/ false);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "EndAccessPseudoComponent\n";
}
};
} // end anonymous namespace
static SILValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
SILValue addr, LValueTypeData typeData,
SGFAccessKind accessKind,
SILAccessEnforcement enforcement) {
auto silAccessKind = isReadAccess(accessKind) ? SILAccessKind::Read
: SILAccessKind::Modify;
assert(SGF.isInFormalEvaluationScope() &&
"tried to enter access scope without a writeback scope!");
// Enter the access.
addr = SGF.B.createBeginAccess(loc, addr, silAccessKind, enforcement,
/*hasNoNestedConflict=*/false,
/*fromBuiltin=*/false);
// Push a writeback to end it.
auto accessedMV = ManagedValue::forLValue(addr);
std::unique_ptr<LogicalPathComponent>
component(new EndAccessPseudoComponent(typeData));
pushWriteback(SGF, loc, std::move(component), accessedMV,
MaterializedLValue());
return addr;
}
static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
ManagedValue addr, LValueTypeData typeData,
SGFAccessKind accessKind,
SILAccessEnforcement enforcement) {
return ManagedValue::forLValue(
enterAccessScope(SGF, loc, addr.getLValueAddress(), typeData,
accessKind, enforcement));
}
// Find the base of the formal access at `address`. If the base requires an
// access marker, then create a begin_access on `address`. Return the
// address to be used for the access.
//
// FIXME: In order to generate more consistent and verifiable SIL patterns, or
// subobject projections, create the access on the base address and recreate the
// projection.
SILValue UnenforcedAccess::beginAccess(SILGenFunction &SGF, SILLocation loc,
SILValue address, SILAccessKind kind) {
if (!SGF.getOptions().VerifyExclusivity)
return address;
const AccessedStorage &storage = findAccessedStorage(address);
// Unsafe access may have invalid storage (e.g. a RawPointer).
if (storage && !isPossibleFormalAccessBase(storage, &SGF.F))
return address;
auto BAI =
SGF.B.createBeginAccess(loc, address, kind, SILAccessEnforcement::Unsafe,
/*hasNoNestedConflict=*/false,
/*fromBuiltin=*/false);
beginAccessPtr = BeginAccessPtr(BAI, DeleterCheck());
return BAI;
}
void UnenforcedAccess::endAccess(SILGenFunction &SGF) {
emitEndAccess(SGF);
beginAccessPtr.release();
}
void UnenforcedAccess::emitEndAccess(SILGenFunction &SGF) {
if (!beginAccessPtr)
return;
SGF.B.createEndAccess(beginAccessPtr->getLoc(), beginAccessPtr.get(),
/*abort*/ false);
}
// Emit an end_access marker when executing a cleanup (on a side branch).
void UnenforcedFormalAccess::emitEndAccess(SILGenFunction &SGF) {
access.emitEndAccess(SGF);
}
// End the access when existing the FormalEvaluationScope.
void UnenforcedFormalAccess::finishImpl(SILGenFunction &SGF) {
access.endAccess(SGF);
}
namespace {
struct UnenforcedAccessCleanup : Cleanup {
FormalEvaluationContext::stable_iterator Depth;
UnenforcedAccessCleanup() : Depth() {}
void emit(SILGenFunction &SGF, CleanupLocation loc,
ForUnwind_t forUnwind) override {
auto &evaluation = *SGF.FormalEvalContext.find(Depth);
assert(evaluation.getKind() == FormalAccess::Unenforced);
auto &formalAccess = static_cast<UnenforcedFormalAccess &>(evaluation);
formalAccess.emitEndAccess(SGF);
}
void dump(SILGenFunction &) const override {
#ifndef NDEBUG
llvm::errs() << "UnenforcedAccessCleanup\n"
<< "State: " << getState() << " Depth: " << Depth.getDepth()
<< "\n";
#endif
}
};
} // end anonymous namespace
SILValue UnenforcedFormalAccess::enter(SILGenFunction &SGF, SILLocation loc,
SILValue address, SILAccessKind kind) {
assert(SGF.isInFormalEvaluationScope());
UnenforcedAccess access;
SILValue accessAddress = access.beginAccess(SGF, loc, address, kind);
if (!access.beginAccessPtr)
return address;
auto &cleanup = SGF.Cleanups.pushCleanup<UnenforcedAccessCleanup>();
CleanupHandle handle = SGF.Cleanups.getTopCleanup();
auto &context = SGF.FormalEvalContext;
context.push<UnenforcedFormalAccess>(loc, std::move(access), handle);
cleanup.Depth = context.stable_begin();
return accessAddress;
}
static void copyBorrowedYieldsIntoTemporary(SILGenFunction &SGF,
SILLocation loc,
ArrayRef<ManagedValue> &yields,
AbstractionPattern origFormalType,
CanType substFormalType,
Initialization *init) {
if (!origFormalType.isTuple()) {
auto value = yields.front();
yields = yields.drop_front();
init->copyOrInitValueInto(SGF, loc, value, /*isInit*/ false);
init->finishInitialization(SGF);
return;
}
assert(init->canSplitIntoTupleElements());
SmallVector<InitializationPtr, 4> scratch;
auto eltInits =
init->splitIntoTupleElements(SGF, loc, substFormalType, scratch);
for (size_t i : indices(eltInits)) {
auto origEltType = origFormalType.getTupleElementType(i);
auto substEltType = cast<TupleType>(substFormalType).getElementType(i);
copyBorrowedYieldsIntoTemporary(SGF, loc, yields, origEltType,
substEltType, eltInits[i].get());
}
init->finishInitialization(SGF);
}
namespace {
class RefElementComponent : public PhysicalPathComponent {
VarDecl *Field;
SILType SubstFieldType;
bool IsNonAccessing;
public:
RefElementComponent(VarDecl *field, LValueOptions options,
SILType substFieldType, LValueTypeData typeData)
: PhysicalPathComponent(typeData, RefElementKind),
Field(field), SubstFieldType(substFieldType),
IsNonAccessing(options.IsNonAccessing) {}
virtual bool isLoadingPure() const override { return true; }
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(base.getType().hasReferenceSemantics() &&
"base for ref element component must be a reference type");
// Borrow the ref element addr using formal access. If we need the ref
// element addr, we will load it in this expression.
if (base.getType().isAddress()) {
base = SGF.B.createFormalAccessLoadBorrow(loc, base);
} else {
base = base.formalAccessBorrow(SGF, loc);
}
SILValue result =
SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(),
Field, SubstFieldType);
// Avoid emitting access markers completely for non-accesses or immutable
// declarations. Access marker verification is aware of these cases.
if (!IsNonAccessing && !Field->isLet()) {
if (auto enforcement = SGF.getDynamicEnforcement(Field)) {
result = enterAccessScope(SGF, loc, result, getTypeData(),
getAccessKind(), *enforcement);
}
}
return ManagedValue::forLValue(result);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "RefElementComponent(" << Field->getName() << ")\n";
}
};
class TupleElementComponent : public PhysicalPathComponent {
unsigned ElementIndex;
public:
TupleElementComponent(unsigned elementIndex, LValueTypeData typeData)
: PhysicalPathComponent(typeData, TupleElementKind),
ElementIndex(elementIndex) {}
virtual bool isLoadingPure() const override { return true; }
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(base && "invalid value for element base");
if (base.getType().isObject()) {
return SGF.B.createTupleExtract(loc, base, ElementIndex);
}
// TODO: if the base is +1, break apart its cleanup.
auto Res = SGF.B.createTupleElementAddr(loc, base.getValue(),
ElementIndex,
getTypeOfRValue().getAddressType());
return ManagedValue::forLValue(Res);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "TupleElementComponent(" << ElementIndex << ")\n";
}
};
class StructElementComponent : public PhysicalPathComponent {
VarDecl *Field;
SILType SubstFieldType;
public:
StructElementComponent(VarDecl *field, SILType substFieldType,
LValueTypeData typeData)
: PhysicalPathComponent(typeData, StructElementKind),
Field(field), SubstFieldType(substFieldType) {}
virtual bool isLoadingPure() const override { return true; }
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(base && "invalid value for element base");
if (base.getType().isObject()) {
return SGF.B.createStructExtract(loc, base, Field);
}
// TODO: if the base is +1, break apart its cleanup.
auto Res = SGF.B.createStructElementAddr(loc, base.getValue(),
Field, SubstFieldType);
return ManagedValue::forLValue(Res);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "StructElementComponent("
<< Field->getName() << ")\n";
}
};
/// A physical path component which force-projects the address of
/// the value of an optional l-value.
class ForceOptionalObjectComponent : public PhysicalPathComponent {
bool isImplicitUnwrap;
public:
ForceOptionalObjectComponent(LValueTypeData typeData,
bool isImplicitUnwrap)
: PhysicalPathComponent(typeData, OptionalObjectKind),
isImplicitUnwrap(isImplicitUnwrap) {}
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
// Assert that the optional value is present and return the projected out
// payload.
return SGF.emitPreconditionOptionalHasValue(loc, base, isImplicitUnwrap);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "ForceOptionalObjectComponent(" << isImplicitUnwrap << ")\n";
}
};
/// A physical path component which projects out an opened archetype
/// from an existential.
class OpenOpaqueExistentialComponent : public PhysicalPathComponent {
public:
OpenOpaqueExistentialComponent(CanArchetypeType openedArchetype,
LValueTypeData typeData)
: PhysicalPathComponent(typeData, OpenOpaqueExistentialKind) {
assert(getSubstFormalType() == openedArchetype);
}
virtual bool isLoadingPure() const override { return true; }
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(base.getType().isExistentialType() &&
"base for open existential component must be an existential");
assert((base.getType().isAddress() ||
base.getType().getPreferredExistentialRepresentation(SGF.SGM.M) ==
ExistentialRepresentation::Boxed) &&
"base value of open-existential component was not an address or a "
"boxed existential?");
SILValue addr;
auto rep = base.getType().getPreferredExistentialRepresentation(SGF.SGM.M);
switch (rep) {
case ExistentialRepresentation::Opaque:
addr = SGF.B.createOpenExistentialAddr(
loc, base.getValue(), getTypeOfRValue().getAddressType(),
getOpenedExistentialAccessFor(getFormalAccessKind(getAccessKind())));
break;
case ExistentialRepresentation::Boxed: {
ManagedValue error;
if (base.getType().isObject()) {
error = base;
} else {
auto &TL = SGF.getTypeLowering(base.getType());
error = SGF.emitLoad(loc, base.getValue(), TL,
SGFContext(), IsNotTake);
}
addr = SGF.B.createOpenExistentialBox(
loc, error.getValue(), getTypeOfRValue().getAddressType());
break;
}
default:
llvm_unreachable("Bad existential representation for address-only type");
}
return ManagedValue::forLValue(addr);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "OpenOpaqueExistentialComponent("
<< getSubstFormalType() << ")\n";
}
};
/// A local path component for the payload of a class or metatype existential.
///
/// TODO: Could be physical if we had a way to project out the
/// payload.
class OpenNonOpaqueExistentialComponent : public LogicalPathComponent {
CanArchetypeType OpenedArchetype;
public:
OpenNonOpaqueExistentialComponent(CanArchetypeType openedArchetype,
LValueTypeData typeData)
: LogicalPathComponent(typeData, OpenNonOpaqueExistentialKind),
OpenedArchetype(openedArchetype) {}
virtual bool isLoadingPure() const override { return true; }
Optional<AccessedStorage> getAccessedStorage() const override {
return None;
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext c) && override {
auto refType = base.getType().getObjectType();
auto &TL = SGF.getTypeLowering(refType);
// Load the original value if necessary.
auto result = base.getType().isAddress()
? SGF.emitLoad(loc, base.getValue(), TL,
SGFContext(), IsNotTake)
: base;
assert(refType.isAnyExistentialType() &&
"base for open existential component must be an existential");
ManagedValue ref;
if (refType.is<ExistentialMetatypeType>()) {
assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M)
== ExistentialRepresentation::Metatype);
ref = ManagedValue::forUnmanaged(
SGF.B.createOpenExistentialMetatype(loc,
result.getUnmanagedValue(),
getTypeOfRValue()));
} else {
assert(refType.getPreferredExistentialRepresentation(SGF.SGM.M)
== ExistentialRepresentation::Class);
ref = SGF.B.createOpenExistentialRef(loc, result, getTypeOfRValue());
}
return RValue(SGF, loc, getSubstFormalType(), ref);
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&value, ManagedValue base) && override {
auto payload = std::move(value).getAsSingleValue(SGF).forward(SGF);
SmallVector<ProtocolConformanceRef, 2> conformances;
for (auto proto : OpenedArchetype->getConformsTo())
conformances.push_back(ProtocolConformanceRef(proto));
SILValue ref;
if (base.getType().is<ExistentialMetatypeType>()) {
ref = SGF.B.createInitExistentialMetatype(
loc,
payload,
base.getType().getObjectType(),
SGF.getASTContext().AllocateCopy(conformances));
} else {
assert(getSubstFormalType()->isBridgeableObjectType());
ref = SGF.B.createInitExistentialRef(
loc,
base.getType().getObjectType(),
getSubstFormalType(),
payload,
SGF.getASTContext().AllocateCopy(conformances));
}
auto &TL = SGF.getTypeLowering(base.getType());
SGF.emitSemanticStore(loc, ref,
base.getValue(), TL, IsNotInitialization);
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
LogicalPathComponent *clone =
new OpenNonOpaqueExistentialComponent(OpenedArchetype, getTypeData());
return std::unique_ptr<LogicalPathComponent>(clone);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "OpenNonOpaqueExistentialComponent("
<< OpenedArchetype << ", ...)\n";
}
};
/// A physical path component which returns a literal address.
class ValueComponent : public PhysicalPathComponent {
ManagedValue Value;
Optional<SILAccessEnforcement> Enforcement;
bool IsRValue;
public:
ValueComponent(ManagedValue value,
Optional<SILAccessEnforcement> enforcement,
LValueTypeData typeData,
bool isRValue = false) :
PhysicalPathComponent(typeData, ValueKind),
Value(value),
Enforcement(enforcement),
IsRValue(isRValue) {
assert(IsRValue || value.getType().isAddress());
}
virtual bool isLoadingPure() const override { return true; }
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(!base && "value component must be root of lvalue path");
if (!Enforcement)
return Value;
SILValue addr = Value.getLValueAddress();
addr = enterAccessScope(SGF, loc, addr, getTypeData(),
getAccessKind(), *Enforcement);
return ManagedValue::forLValue(addr);
}
bool isRValue() const override {
return IsRValue;
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS << "ValueComponent(";
if (IsRValue) OS << "rvalue, ";
if (Enforcement) {
OS << getSILAccessEnforcementName(*Enforcement);
} else {
OS << "unenforced";
}
OS << "):\n";
Value.dump(OS, indent + 2);
}
};
} // end anonymous namespace
static bool isReadNoneFunction(const Expr *e) {
// If this is a curried call to an integer literal conversion operations, then
// we can "safely" assume it is readnone (btw, yes this is totally gross).
// This is better to be attribute driven, a la rdar://15587352.
if (auto *dre = dyn_cast<DeclRefExpr>(e)) {
DeclName name = dre->getDecl()->getFullName();
return (name.getArgumentNames().size() == 1 &&
name.getBaseName() == DeclBaseName::createConstructor() &&
!name.getArgumentNames()[0].empty() &&
(name.getArgumentNames()[0].str() == "integerLiteral" ||
name.getArgumentNames()[0].str() == "_builtinIntegerLiteral"));
}
// Look through DotSyntaxCallExpr, since the literal functions are curried.
if (auto *CRCE = dyn_cast<ConstructorRefCallExpr>(e))
return isReadNoneFunction(CRCE->getFn());
return false;
}
/// Given two expressions used as indexes to the same SubscriptDecl (and thus
/// are guaranteed to have the same AST type) check to see if they are going to
/// produce the same value.
static bool areCertainlyEqualIndices(const Expr *e1, const Expr *e2) {
if (e1->getKind() != e2->getKind()) return false;
// Look through ParenExpr's.
if (auto *pe1 = dyn_cast<ParenExpr>(e1)) {
auto *pe2 = cast<ParenExpr>(e2);
return areCertainlyEqualIndices(pe1->getSubExpr(), pe2->getSubExpr());
}
// Calls are identical if the callee and operands are identical and we know
// that the call is something that is "readnone".
if (auto *ae1 = dyn_cast<ApplyExpr>(e1)) {
auto *ae2 = cast<ApplyExpr>(e2);
return areCertainlyEqualIndices(ae1->getFn(), ae2->getFn()) &&
areCertainlyEqualIndices(ae1->getArg(), ae2->getArg()) &&
isReadNoneFunction(ae1->getFn());
}
// TypeExpr's that produce the same metatype type are identical.
if (isa<TypeExpr>(e1))
return true;
if (auto *dre1 = dyn_cast<DeclRefExpr>(e1)) {
auto *dre2 = cast<DeclRefExpr>(e2);
return dre1->getDecl() == dre2->getDecl() &&
dre1->getType()->isEqual(dre2->getType());
}
// Compare a variety of literals.
if (auto *il1 = dyn_cast<IntegerLiteralExpr>(e1)) {
const auto &val1 = il1->getValue();
const auto &val2 = cast<IntegerLiteralExpr>(e2)->getValue();
// If the integers are arbitrary-precision, their bit-widths may differ,
// but only if they represent different values.
return val1.getBitWidth() == val2.getBitWidth() && val1 == val2;
}
if (auto *il1 = dyn_cast<FloatLiteralExpr>(e1))
return il1->getValue().bitwiseIsEqual(
cast<FloatLiteralExpr>(e2)->getValue());
if (auto *bl1 = dyn_cast<BooleanLiteralExpr>(e1))
return bl1->getValue() == cast<BooleanLiteralExpr>(e2)->getValue();
if (auto *sl1 = dyn_cast<StringLiteralExpr>(e1))
return sl1->getValue() == cast<StringLiteralExpr>(e2)->getValue();
// Compare tuple expressions.
if (auto *te1 = dyn_cast<TupleExpr>(e1)) {
auto *te2 = cast<TupleExpr>(e2);
// Easy checks: # of elements, trailing closures, element names.
if (te1->getNumElements() != te2->getNumElements() ||
te1->hasTrailingClosure() != te2->hasTrailingClosure() ||
te1->getElementNames() != te2->getElementNames()) {
return false;
}
for (unsigned i = 0, n = te1->getNumElements(); i != n; ++i) {
if (!areCertainlyEqualIndices(te1->getElement(i), te2->getElement(i)))
return false;
}
return true;
}
// Otherwise, we have no idea if they are identical.
return false;
}
static LValueOptions getBaseOptions(LValueOptions options,
AccessStrategy strategy) {
return (strategy.getKind() == AccessStrategy::Storage
? options.forProjectedBaseLValue()
: options.forComputedBaseLValue());
}
static ArgumentSource emitBaseValueForAccessor(SILGenFunction &SGF,
SILLocation loc, LValue &&dest,
CanType baseFormalType,
SILDeclRef accessor);
static SGFAccessKind getBaseAccessKind(SILGenModule &SGM,
AbstractStorageDecl *member,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType baseFormalType);
namespace {
/// A helper class for implementing components that involve accessing
/// storage.
template <class Base>
class AccessComponent : public Base {
protected:
// The VarDecl or SubscriptDecl being get/set.
AbstractStorageDecl *Storage;
/// The subscript index expression. Useless
Expr *IndexExprForDiagnostics;
PreparedArguments Indices;
/// AST type of the base expression, in case the accessor call
/// requires re-abstraction.
CanType BaseFormalType;
struct AccessorArgs {
ArgumentSource base;
PreparedArguments Indices;
};
/// Returns a tuple of RValues holding the accessor value, base (retained if
/// necessary), and subscript arguments, in that order.
AccessorArgs
prepareAccessorArgs(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SILDeclRef accessor) &&
{
AccessorArgs result;
if (base) {
// Borrow the base, because we may need it again to invoke other
// accessors.
result.base = SGF.prepareAccessorBaseArg(loc,
base.formalAccessBorrow(SGF, loc),
BaseFormalType,
accessor);
}
if (!Indices.isNull())
result.Indices = std::move(Indices);
return result;
}
AccessComponent(PathComponent::KindTy kind,
AbstractStorageDecl *storage,
CanType baseFormalType,
LValueTypeData typeData,
Expr *indexExprForDiagnostics,
PreparedArguments &&indices)
: Base(typeData, kind), Storage(storage),
IndexExprForDiagnostics(indexExprForDiagnostics),
Indices(std::move(indices)),
BaseFormalType(baseFormalType)
{
}
AccessComponent(const AccessComponent &copied,
SILGenFunction &SGF,
SILLocation loc)
: Base(copied.getTypeData(), copied.getKind()),
Storage(copied.Storage),
IndexExprForDiagnostics(copied.IndexExprForDiagnostics),
Indices(copied.Indices.copy(SGF, loc)) ,
BaseFormalType(copied.BaseFormalType) {}
bool doesAccessorMutateSelf(SILGenFunction &SGF,
SILDeclRef accessor) const {
auto accessorSelf = SGF.SGM.Types.getConstantSelfParameter(accessor);
return accessorSelf.getType() && accessorSelf.isIndirectMutating();
}
void printBase(raw_ostream &OS, unsigned indent, StringRef name) const {
OS.indent(indent) << name << "(" << Storage->getBaseName() << ")";
if (IndexExprForDiagnostics) {
OS << " subscript_index:\n";
IndexExprForDiagnostics->dump(OS, 2);
}
OS << '\n';
}
};
/// A helper class for implementing a component that involves
/// calling accessors.
template <class Base>
class AccessorBasedComponent : public AccessComponent<Base> {
using super = AccessComponent<Base>;
protected:
SILDeclRef Accessor;
bool IsSuper;
bool IsDirectAccessorUse;
bool IsOnSelfParameter;
SubstitutionMap Substitutions;
public:
AccessorBasedComponent(PathComponent::KindTy kind,
AbstractStorageDecl *decl, SILDeclRef accessor,
bool isSuper, bool isDirectAccessorUse,
SubstitutionMap substitutions,
CanType baseFormalType, LValueTypeData typeData,
Expr *indexExprForDiagnostics,
PreparedArguments &&indices,
bool isOnSelfParameter = false)
: super(kind, decl, baseFormalType, typeData, indexExprForDiagnostics,
std::move(indices)),
Accessor(accessor), IsSuper(isSuper),
IsDirectAccessorUse(isDirectAccessorUse),
IsOnSelfParameter(isOnSelfParameter), Substitutions(substitutions) {}
AccessorBasedComponent(const AccessorBasedComponent &copied,
SILGenFunction &SGF,
SILLocation loc)
: super(copied, SGF, loc),
Accessor(copied.Accessor),
IsSuper(copied.IsSuper),
IsDirectAccessorUse(copied.IsDirectAccessorUse),
IsOnSelfParameter(copied.IsOnSelfParameter),
Substitutions(copied.Substitutions) {}
AccessorDecl *getAccessorDecl() const {
return cast<AccessorDecl>(Accessor.getFuncDecl());
}
};
class GetterSetterComponent
: public AccessorBasedComponent<LogicalPathComponent> {
public:
GetterSetterComponent(AbstractStorageDecl *decl,
SILDeclRef accessor,
bool isSuper, bool isDirectAccessorUse,
SubstitutionMap substitutions,
CanType baseFormalType,
LValueTypeData typeData,
Expr *subscriptIndexExpr,
PreparedArguments &&indices,
bool isOnSelfParameter)
: AccessorBasedComponent(GetterSetterKind, decl, accessor, isSuper,
isDirectAccessorUse, substitutions,
baseFormalType, typeData, subscriptIndexExpr,
std::move(indices), isOnSelfParameter)
{
assert(getAccessorDecl()->isGetterOrSetter());
}
GetterSetterComponent(const GetterSetterComponent &copied,
SILGenFunction &SGF,
SILLocation loc)
: AccessorBasedComponent(copied, SGF, loc)
{
}
bool hasPropertyWrapper() const {
if (auto *VD = dyn_cast<VarDecl>(Storage)) {
// FIXME: Handle composition of property wrappers.
if (VD->getAttachedPropertyWrappers().size() == 1) {
auto wrapperInfo = VD->getAttachedPropertyWrapperTypeInfo(0);
// If there is no init(wrapperValue:), we cannot rewrite an
// assignment into an initialization.
if (!wrapperInfo.wrappedValueInit)
return false;
// If we have a nonmutating setter on a value type, the call
// captures all of 'self' and we cannot rewrite an assignment
// into an initialization.
if (!VD->isSetterMutating() &&
VD->getDeclContext()->getSelfNominalTypeDecl() &&
VD->isInstanceMember() &&
!VD->getDeclContext()->getDeclaredInterfaceType()
->hasReferenceSemantics()) {
return false;
}
return true;
}
}
return false;
}
void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc,
LValue &&dest, ArgumentSource &&value) {
assert(getAccessorDecl()->isSetter());
SILDeclRef setter = Accessor;
// Pull everything out of this that we'll need, because we're
// about to modify the LValue and delete this component.
auto subs = this->Substitutions;
bool isSuper = this->IsSuper;
bool isDirectAccessorUse = this->IsDirectAccessorUse;
auto indices = std::move(this->Indices);
auto baseFormalType = this->BaseFormalType;
bool isOnSelfParameter = this->IsOnSelfParameter;
// Drop this component from the l-value.
dest.dropLastComponent(*this);
return emitAssignWithSetter(SGF, loc, std::move(dest), baseFormalType,
isSuper, setter, isDirectAccessorUse, subs,
std::move(indices), std::move(value),
isOnSelfParameter);
}
static void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc,
LValue &&baseLV, CanType baseFormalType,
bool isSuper, SILDeclRef setter,
bool isDirectAccessorUse,
SubstitutionMap subs,
PreparedArguments &&indices,
ArgumentSource &&value,
bool isSelfParameter) {
ArgumentSource self = [&] {
if (!baseLV.isValid()) {
return ArgumentSource();
} else if (computeSelfParam(cast<FuncDecl>(setter.getDecl()))
.getParameterFlags().isInOut()) {
return ArgumentSource(loc, std::move(baseLV));
} else {
return emitBaseValueForAccessor(SGF, loc, std::move(baseLV),
baseFormalType, setter);
}
}();
return SGF.emitSetAccessor(loc, setter, subs, std::move(self), isSuper,
isDirectAccessorUse, std::move(indices),
std::move(value), isSelfParameter);
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&value, ManagedValue base) && override {
assert(getAccessorDecl()->isSetter());
SILDeclRef setter = Accessor;
if (hasPropertyWrapper() && IsOnSelfParameter) {
// This is wrapped property. Instead of emitting a setter, emit an
// assign_by_wrapper with the allocating initializer function and the
// setter function as arguments. DefiniteInitializtion will then decide
// between the two functions, depending if it's an initialization or a
// re-assignment.
//
VarDecl *field = dyn_cast<VarDecl>(Storage);
VarDecl *backingVar = field->getPropertyWrapperBackingProperty();
assert(backingVar);
CanType ValType = backingVar->getType()->getCanonicalType();
SILType varStorageType =
SGF.SGM.Types.getSubstitutedStorageType(backingVar, ValType);
auto typeData =
getLogicalStorageTypeData(SGF.SGM, getTypeData().AccessKind, ValType);
// Get the address of the storage property.
ManagedValue proj;
if (BaseFormalType->mayHaveSuperclass()) {
RefElementComponent REC(backingVar, LValueOptions(), varStorageType,
typeData);
proj = std::move(REC).project(SGF, loc, base);
} else {
assert(BaseFormalType->getStructOrBoundGenericStruct());
StructElementComponent SEC(backingVar, varStorageType, typeData);
proj = std::move(SEC).project(SGF, loc, base);
}
// Create the allocating initializer function. It captures the metadata.
// FIXME: Composition.
assert(field->getAttachedPropertyWrappers().size() == 1);
auto wrapperInfo = field->getAttachedPropertyWrapperTypeInfo(0);
auto ctor = wrapperInfo.wrappedValueInit;
SubstitutionMap subs = backingVar->getType()->getMemberSubstitutionMap(
SGF.getModule().getSwiftModule(), ctor);
Type ity = ctor->getInterfaceType();
AnyFunctionType *substIty =
ity.subst(subs)->getCanonicalType()->castTo<AnyFunctionType>();
auto initRef = SILDeclRef(ctor, SILDeclRef::Kind::Allocator)
.asForeign(requiresForeignEntryPoint(ctor));
RValue initFuncRV =
SGF.emitApplyPropertyWrapperAllocator(loc, subs,initRef,
backingVar->getType(),
CanAnyFunctionType(substIty));
ManagedValue initFn = std::move(initFuncRV).getAsSingleValue(SGF, loc);
// Create the allocating setter function. It captures the base address.
auto setterInfo = SGF.getConstantInfo(setter);
SILValue setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo);
auto setterSubs = SGF.getFunction().getForwardingSubstitutionMap();
CanSILFunctionType setterTy = setterFRef->getType().castTo<SILFunctionType>();
SILFunctionConventions setterConv(setterTy, SGF.SGM.M);
SILValue capturedBase;
unsigned argIdx = setterConv.getNumSILArguments() - 1;
if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
capturedBase = base.getValue();
} else {
capturedBase = base.copy(SGF, loc).forward(SGF);
}
PartialApplyInst *setterPAI =
SGF.B.createPartialApply(loc, setterFRef,
setterSubs, { capturedBase },
ParameterConvention::Direct_Guaranteed);
ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI);
// Create the assign_by_wrapper with the allocator and setter.
assert(value.isRValue());
ManagedValue Mval = std::move(value).asKnownRValue(SGF).
getAsSingleValue(SGF, loc);
SGF.B.createAssignByWrapper(loc, Mval.forward(SGF), proj.forward(SGF),
initFn.getValue(), setterFn.getValue(),
AssignOwnershipQualifier::Unknown);
return;
}
FormalEvaluationScope scope(SGF);
// Pass in just the setter.
auto args =
std::move(*this).prepareAccessorArgs(SGF, loc, base, setter);
return SGF.emitSetAccessor(loc, setter, Substitutions,
std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.Indices),
std::move(value), IsOnSelfParameter);
}
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(isReadAccess(getAccessKind()) &&
"shouldn't be using this path to call modify");
return std::move(*this).LogicalPathComponent::project(SGF, loc, base);
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext c) && override {
assert(getAccessorDecl()->isGetter());
SILDeclRef getter = Accessor;
FormalEvaluationScope scope(SGF);
auto args =
std::move(*this).prepareAccessorArgs(SGF, loc, base, getter);
return SGF.emitGetAccessor(
loc, getter, Substitutions, std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.Indices), c, IsOnSelfParameter);
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
LogicalPathComponent *clone = new GetterSetterComponent(*this, SGF, loc);
return std::unique_ptr<LogicalPathComponent>(clone);
}
void dump(raw_ostream &OS, unsigned indent) const override {
printBase(OS, indent, "GetterSetterComponent");
}
/// Compare 'this' lvalue and the 'rhs' lvalue (which is guaranteed to have
/// the same dynamic PathComponent type as the receiver) to see if they are
/// identical. If so, there is a conflicting writeback happening, so emit a
/// diagnostic.
Optional<AccessedStorage> getAccessedStorage() const override {
return AccessedStorage{Storage, IsSuper,
Indices.isNull() ? nullptr : &Indices,
IndexExprForDiagnostics };
}
};
class MaterializeToTemporaryComponent final
: public AccessComponent<LogicalPathComponent> {
SubstitutionMap Substitutions;
AccessStrategy ReadStrategy;
AccessStrategy WriteStrategy;
LValueOptions Options;
bool IsSuper;
bool IsOnSelfParameter;
public:
MaterializeToTemporaryComponent(AbstractStorageDecl *storage,
bool isSuper, SubstitutionMap subs,
LValueOptions options,
AccessStrategy readStrategy,
AccessStrategy writeStrategy,
CanType baseFormalType,
LValueTypeData typeData,
Expr *indexExprForDiagnostics,
PreparedArguments &&indices,
bool isOnSelfParameter)
: AccessComponent(MaterializeToTemporaryKind, storage, baseFormalType,
typeData, indexExprForDiagnostics, std::move(indices)),
Substitutions(subs),
ReadStrategy(readStrategy), WriteStrategy(writeStrategy),
Options(options), IsSuper(isSuper), IsOnSelfParameter(isOnSelfParameter)
{}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
PreparedArguments clonedIndices = Indices.copy(SGF, loc);
LogicalPathComponent *clone =
new MaterializeToTemporaryComponent(Storage, IsSuper, Substitutions,
Options,
ReadStrategy, WriteStrategy,
BaseFormalType, getTypeData(),
IndexExprForDiagnostics,
std::move(clonedIndices),
IsOnSelfParameter);
return std::unique_ptr<LogicalPathComponent>(clone);
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext C) && override {
LValue lv = std::move(*this).prepareLValue(SGF, loc, base,
SGFAccessKind::OwnedObjectRead,
ReadStrategy);
return SGF.emitLoadOfLValue(loc, std::move(lv), C);
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&value, ManagedValue base) && override {
LValue lv = std::move(*this).prepareLValue(SGF, loc, base,
SGFAccessKind::Write,
WriteStrategy);
return SGF.emitAssignToLValue(loc, std::move(value), std::move(lv));
}
Optional<AccessedStorage> getAccessedStorage() const override {
return AccessedStorage{Storage, IsSuper,
Indices.isNull() ? nullptr : &Indices,
IndexExprForDiagnostics};
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "MaterializeToTemporaryComponent";
}
private:
LValue prepareLValue(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFAccessKind accessKind,
AccessStrategy strategy) && {
LValue lv = [&] {
if (!base) return LValue();
auto baseAccessKind =
getBaseAccessKind(SGF.SGM, Storage, accessKind, strategy,
BaseFormalType);
return LValue::forValue(baseAccessKind, base, BaseFormalType);
}();
if (auto subscript = dyn_cast<SubscriptDecl>(Storage)) {
lv.addMemberSubscriptComponent(SGF, loc, subscript, Substitutions,
Options, IsSuper, accessKind, strategy,
getSubstFormalType(),
std::move(Indices),
IndexExprForDiagnostics);
} else {
auto var = cast<VarDecl>(Storage);
if (base) {
lv.addMemberVarComponent(SGF, loc, var, Substitutions, Options,
IsSuper, accessKind, strategy,
getSubstFormalType());
} else {
lv.addNonMemberVarComponent(SGF, loc, var, Substitutions, Options,
accessKind, strategy,
getSubstFormalType());
}
}
return lv;
}
};
/// A physical component which involves calling addressors.
class AddressorComponent
: public AccessorBasedComponent<PhysicalPathComponent> {
SILType SubstFieldType;
public:
AddressorComponent(AbstractStorageDecl *decl, SILDeclRef accessor,
bool isSuper, bool isDirectAccessorUse,
SubstitutionMap substitutions,
CanType baseFormalType, LValueTypeData typeData,
SILType substFieldType,
Expr *indexExprForDiagnostics,
PreparedArguments &&indices, bool isOnSelfParameter)
: AccessorBasedComponent(AddressorKind, decl, accessor, isSuper,
isDirectAccessorUse, substitutions,
baseFormalType, typeData,
indexExprForDiagnostics, std::move(indices),
isOnSelfParameter),
SubstFieldType(substFieldType)
{
assert(getAccessorDecl()->isAnyAddressor());
}
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(SGF.isInFormalEvaluationScope() &&
"offsetting l-value for modification without writeback scope");
ManagedValue addr;
{
FormalEvaluationScope scope(SGF);
auto args =
std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
addr = SGF.emitAddressorAccessor(
loc, Accessor, Substitutions, std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.Indices), SubstFieldType,
IsOnSelfParameter);
}
// Enter an unsafe access scope for the access.
addr = enterAccessScope(SGF, loc, addr, getTypeData(), getAccessKind(),
SILAccessEnforcement::Unsafe);
return addr;
}
void dump(raw_ostream &OS, unsigned indent) const override {
printBase(OS, indent, "AddressorComponent");
}
};
class EndApplyPseudoComponent : public WritebackPseudoComponent {
CleanupHandle EndApplyHandle;
AbstractStorageDecl *Storage;
bool IsSuper;
PreparedArguments PeekedIndices;
Expr *IndexExprForDiagnostics;
public:
EndApplyPseudoComponent(const LValueTypeData &typeData,
CleanupHandle endApplyHandle,
AbstractStorageDecl *storage,
bool isSuper,
PreparedArguments &&peekedIndices,
Expr *indexExprForDiagnostics)
: WritebackPseudoComponent(typeData),
EndApplyHandle(endApplyHandle),
Storage(storage), IsSuper(isSuper),
PeekedIndices(std::move(peekedIndices)),
IndexExprForDiagnostics(indexExprForDiagnostics) {}
private:
void writeback(SILGenFunction &SGF, SILLocation loc,
ManagedValue base,
MaterializedLValue materialized,
bool isFinal) override {
// Just let the cleanup get emitted normally if the writeback is for
// an unwind.
if (!isFinal) return;
SGF.Cleanups.popAndEmitCleanup(EndApplyHandle, CleanupLocation::get(loc),
NotForUnwind);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "EndApplyPseudoComponent";
}
Optional<AccessedStorage> getAccessedStorage() const override {
return AccessedStorage{Storage, IsSuper,
PeekedIndices.isNull() ? nullptr : &PeekedIndices,
IndexExprForDiagnostics};
}
};
}
static void pushEndApplyWriteback(SILGenFunction &SGF, SILLocation loc,
CleanupHandle endApplyHandle,
LValueTypeData typeData,
ManagedValue base = ManagedValue(),
AbstractStorageDecl *storage = nullptr,
bool isSuper = false,
PreparedArguments &&indices
= PreparedArguments(),
Expr *indexExprForDiagnostics = nullptr) {
std::unique_ptr<LogicalPathComponent>
component(new EndApplyPseudoComponent(typeData, endApplyHandle,
storage, isSuper, std::move(indices),
indexExprForDiagnostics));
pushWriteback(SGF, loc, std::move(component), /*for diagnostics*/ base,
MaterializedLValue());
}
namespace {
/// A physical component which involves calling coroutine accessors.
class CoroutineAccessorComponent
: public AccessorBasedComponent<PhysicalPathComponent> {
public:
CoroutineAccessorComponent(AbstractStorageDecl *decl, SILDeclRef accessor,
bool isSuper, bool isDirectAccessorUse,
SubstitutionMap substitutions,
CanType baseFormalType, LValueTypeData typeData,
Expr *indexExprForDiagnostics,
PreparedArguments &&indices,
bool isOnSelfParameter)
: AccessorBasedComponent(
CoroutineAccessorKind, decl, accessor, isSuper,
isDirectAccessorUse, substitutions, baseFormalType, typeData,
indexExprForDiagnostics, std::move(indices), isOnSelfParameter) {}
using AccessorBasedComponent::AccessorBasedComponent;
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(SGF.isInFormalEvaluationScope() &&
"offsetting l-value for modification without writeback scope");
ManagedValue result;
auto args =
std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
auto peekedIndices = args.Indices.copyForDiagnostics();
SmallVector<ManagedValue, 4> yields;
auto endApplyHandle = SGF.emitCoroutineAccessor(
loc, Accessor, Substitutions, std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.Indices), yields,
IsOnSelfParameter);
// Push a writeback that ends the access.
pushEndApplyWriteback(SGF, loc, endApplyHandle, getTypeData(),
base, Storage, IsSuper, std::move(peekedIndices),
IndexExprForDiagnostics);
auto decl = cast<AccessorDecl>(Accessor.getFuncDecl());
// 'modify' always returns an address of the right type.
if (decl->getAccessorKind() == AccessorKind::Modify) {
assert(yields.size() == 1);
return yields[0];
}
// 'read' returns a borrowed r-value, which might or might not be
// an address of the right type.
// Use the yield value directly if it's the right type.
if (!getOrigFormalType().isTuple()) {
assert(yields.size() == 1);
auto value = yields[0];
if (value.getType().isAddress() ||
!isReadAccessResultAddress(getAccessKind()))
return value;
}
// Otherwise, we need to make a temporary.
// TODO: build a scalar tuple if possible.
auto temporary =
SGF.emitTemporary(loc, SGF.getTypeLowering(getTypeOfRValue()));
auto yieldsAsArray = llvm::makeArrayRef(yields);
copyBorrowedYieldsIntoTemporary(SGF, loc, yieldsAsArray,
getOrigFormalType(), getSubstFormalType(),
temporary.get());
assert(yieldsAsArray.empty() && "didn't claim all yields");
return temporary->getManagedAddress();
}
void dump(raw_ostream &OS, unsigned indent) const override {
printBase(OS, indent, "CoroutineAccessorComponent");
}
};
}
static ManagedValue
makeBaseConsumableMaterializedRValue(SILGenFunction &SGF,
SILLocation loc, ManagedValue base) {
if (base.isLValue()) {
auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());
SGF.B.createCopyAddr(loc, base.getValue(), tmp,
IsNotTake, IsInitialization);
return SGF.emitManagedBufferWithCleanup(tmp);
}
bool isBorrowed = base.isPlusZeroRValueOrTrivial()
&& !base.getType().isTrivial(SGF.F);
if (!base.getType().isAddress() || isBorrowed) {
auto tmp = SGF.emitTemporaryAllocation(loc, base.getType());
if (isBorrowed)
base.copyInto(SGF, loc, tmp);
else
base.forwardInto(SGF, loc, tmp);
return SGF.emitManagedBufferWithCleanup(tmp);
}
return base;
}
static ManagedValue
emitUpcastToKeyPath(SILGenFunction &SGF, SILLocation loc,
KeyPathTypeKind typeKind, ManagedValue keyPath) {
if (typeKind == KPTK_KeyPath) return keyPath;
assert(typeKind == KPTK_WritableKeyPath ||
typeKind == KPTK_ReferenceWritableKeyPath);
auto derivedKeyPathTy = keyPath.getType().castTo<BoundGenericType>();
auto baseKeyPathTy =
BoundGenericType::get(SGF.getASTContext().getKeyPathDecl(),
Type(), derivedKeyPathTy->getGenericArgs())
->getCanonicalType();
return SGF.B.createUpcast(loc, keyPath,
SILType::getPrimitiveObjectType(baseKeyPathTy));
}
namespace {
class LogicalKeyPathApplicationComponent final
: public LogicalPathComponent {
KeyPathTypeKind TypeKind;
ManagedValue KeyPath;
Type BaseFormalType;
public:
LogicalKeyPathApplicationComponent(LValueTypeData typeData,
KeyPathTypeKind typeKind,
ManagedValue keyPath,
Type baseFormalType)
: LogicalPathComponent(typeData, LogicalKeyPathApplicationKind),
TypeKind(typeKind), KeyPath(keyPath), BaseFormalType(baseFormalType) {
assert(isReadAccess(getAccessKind()) ||
typeKind == KPTK_WritableKeyPath ||
typeKind == KPTK_ReferenceWritableKeyPath);
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext C) && override {
assert(isReadAccess(getAccessKind()));
FuncDecl *projectFn;
auto keyPathValue = KeyPath;
SmallVector<Type, 2> typeArgs;
typeArgs.push_back(BaseFormalType);
if (TypeKind == KPTK_AnyKeyPath) {
projectFn = SGF.getASTContext().getGetAtAnyKeyPath();
} else if (TypeKind == KPTK_PartialKeyPath) {
projectFn = SGF.getASTContext().getGetAtPartialKeyPath();
} else if (TypeKind == KPTK_KeyPath ||
TypeKind == KPTK_WritableKeyPath ||
TypeKind == KPTK_ReferenceWritableKeyPath) {
projectFn = SGF.getASTContext().getGetAtKeyPath();
auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
assert(keyPathTy->getGenericArgs().size() == 2);
assert(keyPathTy->getGenericArgs()[0]->getCanonicalType() ==
BaseFormalType->getCanonicalType());
typeArgs.push_back(keyPathTy->getGenericArgs()[1]);
keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue);
} else {
llvm_unreachable("bad key path kind for this component");
}
auto subs = SubstitutionMap::get(projectFn->getGenericSignature(),
ArrayRef<Type>(typeArgs),
ArrayRef<ProtocolConformanceRef>());
base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
return SGF.emitApplyOfLibraryIntrinsic(loc, projectFn, subs,
{base, keyPathValue}, C);
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&value, ManagedValue base) && override {
assert(!isReadAccess(getAccessKind()));
auto keyPathValue = KeyPath;
FuncDecl *setFn;
if (TypeKind == KPTK_WritableKeyPath) {
setFn = SGF.getASTContext().getSetAtWritableKeyPath();
assert(base.isLValue());
} else if (TypeKind == KPTK_ReferenceWritableKeyPath) {
setFn = SGF.getASTContext().getSetAtReferenceWritableKeyPath();
base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
} else {
llvm_unreachable("bad writable type kind");
}
auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
auto subs = SubstitutionMap::get(setFn->getGenericSignature(),
keyPathTy->getGenericArgs(),
ArrayRef<ProtocolConformanceRef>());
auto setValue =
std::move(value).getAsSingleValue(SGF, AbstractionPattern::getOpaque());
if (!setValue.getType().isAddress()) {
setValue = setValue.materialize(SGF, loc);
}
SGF.emitApplyOfLibraryIntrinsic(loc, setFn, subs,
{base, keyPathValue, setValue},
SGFContext());
}
Optional<AccessedStorage> getAccessedStorage() const override {
return None;
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation l) const override {
llvm_unreachable("can't be cloned");
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "LogicalKeyPathApplicationComponent\n";
}
};
/// A physical component which involves applying a key path.
class PhysicalKeyPathApplicationComponent final
: public PhysicalPathComponent {
KeyPathTypeKind TypeKind;
ManagedValue KeyPath;
public:
PhysicalKeyPathApplicationComponent(LValueTypeData typeData,
KeyPathTypeKind typeKind,
ManagedValue keyPath)
: PhysicalPathComponent(typeData, PhysicalKeyPathApplicationKind),
TypeKind(typeKind), KeyPath(keyPath) {
assert(typeKind == KPTK_KeyPath ||
typeKind == KPTK_WritableKeyPath ||
typeKind == KPTK_ReferenceWritableKeyPath);
assert(typeKind != KPTK_KeyPath || isReadAccess(getAccessKind()));
}
ManagedValue project(SILGenFunction &SGF, SILLocation loc,
ManagedValue base) && override {
assert(SGF.isInFormalEvaluationScope() &&
"offsetting l-value for modification without writeback scope");
bool isRead = isReadAccess(getAccessKind());
// Set up the base and key path values correctly.
auto keyPathValue = KeyPath;
if (isRead) {
keyPathValue = emitUpcastToKeyPath(SGF, loc, TypeKind, keyPathValue);
base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
} else if (TypeKind == KPTK_WritableKeyPath) {
// nothing to do
} else if (TypeKind == KPTK_ReferenceWritableKeyPath) {
base = makeBaseConsumableMaterializedRValue(SGF, loc, base);
} else {
llvm_unreachable("bad combination");
}
SILFunction *projectFn =
SGF.SGM.getKeyPathProjectionCoroutine(isRead, TypeKind);
auto projectFnRef = SGF.B.createManagedFunctionRef(loc, projectFn);
auto projectFnType = projectFn->getLoweredFunctionType();
auto keyPathTy = keyPathValue.getType().castTo<BoundGenericType>();
auto subs = SubstitutionMap::get(projectFnType->getGenericSignature(),
keyPathTy->getGenericArgs(), {});
auto substFnType = projectFnType->substGenericArgs(SGF.SGM.M, subs);
// Perform the begin_apply.
SmallVector<ManagedValue, 1> yields;
auto cleanup =
SGF.emitBeginApply(loc, projectFnRef, subs, { base, keyPathValue },
substFnType, ApplyOptions(), yields);
// Push an operation to do the end_apply.
pushEndApplyWriteback(SGF, loc, cleanup, getTypeData());
assert(yields.size() == 1);
return yields[0];
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "PhysicalKeyPathApplicationComponent\n";
}
};
} // end anonymous namespace
RValue
TranslationPathComponent::get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext c) && {
// Inline constructor.
RValue baseVal = [&]() -> RValue {
// If our base is an object, just put it into an RValue and return.
if (base.getType().isObject()) {
return RValue(SGF, loc, getSubstFormalType(), base);
}
// Otherwise, load the value and put it into an RValue.
return RValue(SGF, loc, getSubstFormalType(),
SGF.emitLoad(loc, base.getValue(),
SGF.getTypeLowering(base.getType()),
SGFContext(), IsNotTake));
}();
// Map the base value to its substituted representation.
return std::move(*this).translate(SGF, loc, std::move(baseVal), c);
}
void TranslationPathComponent::set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&valueSource,
ManagedValue base) && {
assert(base.getType().isAddress() &&
"Only support setting bases that have addresses");
RValue value = std::move(valueSource).getAsRValue(SGF);
// Map the value to the original pattern.
RValue newValue = std::move(*this).untranslate(SGF, loc, std::move(value));
// Store to the base.
std::move(newValue).assignInto(SGF, loc, base.getValue());
}
namespace {
/// Remap an lvalue referencing a generic type to an lvalue of its
/// substituted type in a concrete context.
class OrigToSubstComponent : public TranslationPathComponent {
AbstractionPattern OrigType;
public:
OrigToSubstComponent(const LValueTypeData &typeData,
AbstractionPattern origType)
: TranslationPathComponent(typeData, OrigToSubstKind),
OrigType(origType)
{}
virtual bool isLoadingPure() const override { return true; }
RValue untranslate(SILGenFunction &SGF, SILLocation loc,
RValue &&rv, SGFContext c) && override {
return SGF.emitSubstToOrigValue(loc, std::move(rv), OrigType,
getSubstFormalType(), c);
}
RValue translate(SILGenFunction &SGF, SILLocation loc,
RValue &&rv, SGFContext c) && override {
return SGF.emitOrigToSubstValue(loc, std::move(rv), OrigType,
getSubstFormalType(), c);
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
LogicalPathComponent *clone
= new OrigToSubstComponent(getTypeData(), OrigType);
return std::unique_ptr<LogicalPathComponent>(clone);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "OrigToSubstComponent("
<< getOrigFormalType() << ", "
<< getSubstFormalType() << ", "
<< getTypeOfRValue() << ")\n";
}
};
/// Remap an lvalue referencing a concrete type to an lvalue of a
/// generically-reabstracted type.
class SubstToOrigComponent : public TranslationPathComponent {
public:
SubstToOrigComponent(const LValueTypeData &typeData)
: TranslationPathComponent(typeData, SubstToOrigKind)
{}
virtual bool isLoadingPure() const override { return true; }
RValue untranslate(SILGenFunction &SGF, SILLocation loc,
RValue &&rv, SGFContext c) && override {
return SGF.emitOrigToSubstValue(loc, std::move(rv), getOrigFormalType(),
getSubstFormalType(), c);
}
RValue translate(SILGenFunction &SGF, SILLocation loc,
RValue &&rv, SGFContext c) && override {
return SGF.emitSubstToOrigValue(loc, std::move(rv), getOrigFormalType(),
getSubstFormalType(), c);
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
LogicalPathComponent *clone
= new SubstToOrigComponent(getTypeData());
return std::unique_ptr<LogicalPathComponent>(clone);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "SubstToOrigComponent("
<< getOrigFormalType() << ", "
<< getSubstFormalType() << ", "
<< getTypeOfRValue() << ")\n";
}
};
/// Remap a weak value to Optional<T>*, or unowned pointer to T*.
class OwnershipComponent : public LogicalPathComponent {
public:
OwnershipComponent(LValueTypeData typeData)
: LogicalPathComponent(typeData, OwnershipKind) {
}
virtual bool isLoadingPure() const override { return true; }
Optional<AccessedStorage> getAccessedStorage() const override {
return None;
}
RValue get(SILGenFunction &SGF, SILLocation loc,
ManagedValue base, SGFContext c) && override {
assert(base && "ownership component must not be root of lvalue path");
auto &TL = SGF.getTypeLowering(getTypeOfRValue());
ManagedValue result;
if (base.getType().isObject()) {
result = SGF.emitConversionToSemanticRValue(loc, base, TL);
} else {
result = SGF.emitLoad(loc, base.getValue(), TL, SGFContext(),
IsNotTake);
}
return RValue(SGF, loc, getSubstFormalType(), result);
}
void set(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&valueSource, ManagedValue base) && override {
assert(base && "ownership component must not be root of lvalue path");
auto &TL = SGF.getTypeLowering(base.getType());
auto value = std::move(valueSource).getAsSingleValue(SGF).forward(SGF);
SGF.emitSemanticStore(loc, value, base.getValue(), TL,
IsNotInitialization);
}
std::unique_ptr<LogicalPathComponent>
clone(SILGenFunction &SGF, SILLocation loc) const override {
LogicalPathComponent *clone = new OwnershipComponent(getTypeData());
return std::unique_ptr<LogicalPathComponent>(clone);
}
void dump(raw_ostream &OS, unsigned indent) const override {
OS.indent(indent) << "OwnershipComponent(...)\n";
}
};
} // end anonymous namespace
LValue LValue::forValue(SGFAccessKind accessKind, ManagedValue value,
CanType substFormalType) {
if (value.getType().isObject()) {
LValueTypeData typeData = getValueTypeData(accessKind, substFormalType,
value.getValue());
LValue lv;
lv.add<ValueComponent>(value, None, typeData, /*isRValue=*/true);
return lv;
} else {
// Treat an address-only value as an lvalue we only read from.
if (!value.isLValue())
value = ManagedValue::forLValue(value.getValue());
return forAddress(accessKind, value, None,
AbstractionPattern(substFormalType), substFormalType);
}
}
LValue LValue::forAddress(SGFAccessKind accessKind, ManagedValue address,
Optional<SILAccessEnforcement> enforcement,
AbstractionPattern origFormalType,
CanType substFormalType) {
assert(address.isLValue());
LValueTypeData typeData = {
accessKind, origFormalType, substFormalType,
address.getType().getASTType()
};
LValue lv;
lv.add<ValueComponent>(address, enforcement, typeData);
return lv;
}
void LValue::addMemberComponent(SILGenFunction &SGF, SILLocation loc,
AbstractStorageDecl *storage,
SubstitutionMap subs,
LValueOptions options,
bool isSuper,
SGFAccessKind accessKind,
AccessStrategy accessStrategy,
CanType formalRValueType,
PreparedArguments &&indices,
Expr *indexExprForDiagnostics) {
if (auto var = dyn_cast<VarDecl>(storage)) {
assert(indices.isNull());
addMemberVarComponent(SGF, loc, var, subs, options, isSuper,
accessKind, accessStrategy, formalRValueType);
} else {
auto subscript = cast<SubscriptDecl>(storage);
addMemberSubscriptComponent(SGF, loc, subscript, subs, options, isSuper,
accessKind, accessStrategy, formalRValueType,
std::move(indices), indexExprForDiagnostics);
}
}
void LValue::addOrigToSubstComponent(SILType loweredSubstType) {
loweredSubstType = loweredSubstType.getObjectType();
assert(getTypeOfRValue() != loweredSubstType &&
"reabstraction component is unnecessary!");
// Peephole away complementary reabstractions.
assert(!Path.empty() && "adding translation component to empty l-value");
if (Path.back()->getKind() == PathComponent::SubstToOrigKind) {
// But only if the lowered type matches exactly.
if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) {
Path.pop_back();
return;
}
// TODO: combine reabstractions; this doesn't matter all that much
// for most things, but it can be dramatically better for function
// reabstraction.
}
auto substFormalType = getSubstFormalType();
LValueTypeData typeData = {
getAccessKind(),
AbstractionPattern(substFormalType),
substFormalType,
loweredSubstType.getASTType()
};
add<OrigToSubstComponent>(typeData, getOrigFormalType());
}
void LValue::addSubstToOrigComponent(AbstractionPattern origType,
SILType loweredSubstType) {
loweredSubstType = loweredSubstType.getObjectType();
assert(getTypeOfRValue() != loweredSubstType &&
"reabstraction component is unnecessary!");
// Peephole away complementary reabstractions.
assert(!Path.empty() && "adding translation component to empty l-value");
if (Path.back()->getKind() == PathComponent::OrigToSubstKind) {
// But only if the lowered type matches exactly.
if (Path[Path.size()-2]->getTypeOfRValue() == loweredSubstType) {
Path.pop_back();
return;
}
// TODO: combine reabstractions; this doesn't matter all that much
// for most things, but it can be dramatically better for function
// reabstraction.
}
LValueTypeData typeData = {
getAccessKind(),
origType,
getSubstFormalType(),
loweredSubstType.getASTType()
};
add<SubstToOrigComponent>(typeData);
}
void LValue::dump() const {
dump(llvm::errs());
}
void LValue::dump(raw_ostream &OS, unsigned indent) const {
for (const auto &component : *this) {
component->dump(OS, indent);
}
}
LValue SILGenFunction::emitLValue(Expr *e, SGFAccessKind accessKind,
LValueOptions options) {
// Some lvalue nodes (namely BindOptionalExprs) require immediate evaluation
// of their subexpression, so we must have a writeback scope open while
// building an lvalue.
assert(isInFormalEvaluationScope() && "must be in a formal evaluation scope");
LValue r = SILGenLValue(*this).visit(e, accessKind, options);
// If the final component has an abstraction change, introduce a
// reabstraction component.
auto substFormalType = r.getSubstFormalType();
auto loweredSubstType = getLoweredType(substFormalType);
if (r.getTypeOfRValue() != loweredSubstType.getObjectType()) {
// Logical components always re-abstract back to the substituted
// type.
assert(r.isLastComponentPhysical());
r.addOrigToSubstComponent(loweredSubstType);
}
return r;
}
static LValue visitRecInOut(SILGenLValue &SGL, Expr *e,
SGFAccessKind accessKind,
LValueOptions options, AbstractionPattern orig) {
auto lv = SGL.visit(e, accessKind, options);
// If necessary, handle reabstraction with a SubstToOrigComponent that handles
// writeback in the original representation.
if (orig.isValid()) {
auto &origTL = SGL.SGF.getTypeLowering(orig, e->getType()->getRValueType());
if (lv.getTypeOfRValue() != origTL.getLoweredType().getObjectType())
lv.addSubstToOrigComponent(orig, origTL.getLoweredType().getObjectType());
}
return lv;
}
// Otherwise we have a non-lvalue type (references, values, metatypes,
// etc). These act as the root of a logical lvalue.
static ManagedValue visitRecNonInOutBase(SILGenLValue &SGL, Expr *e,
SGFAccessKind accessKind,
LValueOptions options,
AbstractionPattern orig) {
auto &SGF = SGL.SGF;
// For an rvalue base, apply the reabstraction (if any) eagerly, since
// there's no need for writeback.
if (orig.isValid()) {
return SGF.emitRValueAsOrig(
e, orig, SGF.getTypeLowering(orig, e->getType()->getRValueType()));
}
// Ok, at this point we know that re-abstraction is not required.
SGFContext ctx;
if (auto *dre = dyn_cast<DeclRefExpr>(e)) {
// Any reference to "self" can be done at +0 so long as it is a direct
// access, since we know it is guaranteed.
//
// TODO: it would be great to factor this even lower into SILGen to the
// point where we can see that the parameter is +0 guaranteed. Note that
// this handles the case in initializers where there is actually a stack
// allocation for it as well.
if (isa<ParamDecl>(dre->getDecl()) &&
dre->getDecl()->getFullName() == SGF.getASTContext().Id_self &&
dre->getDecl()->isImplicit()) {
ctx = SGFContext::AllowGuaranteedPlusZero;
if (SGF.SelfInitDelegationState != SILGenFunction::NormalSelf) {
// This needs to be inlined since there is a Formal Evaluation Scope
// in emitRValueForDecl that causing any borrow for this LValue to be
// popped too soon.
auto *vd = cast<ParamDecl>(dre->getDecl());
CanType formalRValueType = dre->getType()->getCanonicalType();
ManagedValue selfLValue =
SGF.emitAddressOfLocalVarDecl(dre, vd, formalRValueType,
SGFAccessKind::OwnedObjectRead);
selfLValue = SGF.emitFormalEvaluationRValueForSelfInDelegationInit(
e, formalRValueType,
selfLValue.getLValueAddress(), ctx)
.getAsSingleValue(SGF, e);
return selfLValue;
}
}
if (auto *VD = dyn_cast<VarDecl>(dre->getDecl())) {
// All let values are guaranteed to be held alive across their lifetime,
// and won't change once initialized. Any loaded value is good for the
// duration of this expression evaluation.
if (VD->isLet()) {
ctx = SGFContext::AllowGuaranteedPlusZero;
}
}
}
if (SGF.SGM.Types.isIndirectPlusZeroSelfParameter(e->getType())) {
ctx = SGFContext::AllowGuaranteedPlusZero;
}
ManagedValue mv = SGF.emitRValueAsSingleValue(e, ctx);
if (mv.isPlusZeroRValueOrTrivial())
return mv;
// Any temporaries needed to materialize the lvalue must be destroyed when
// at the end of the lvalue's formal evaluation scope.
// e.g. for foo(self.bar)
// %self = load [copy] %ptr_self
// %rvalue = barGetter(%self)
// destroy_value %self // self must be released before calling foo.
// foo(%rvalue)
SILValue value = mv.forward(SGF);
return SGF.emitFormalAccessManagedRValueWithCleanup(CleanupLocation(e),
value);
}
LValue SILGenLValue::visitRec(Expr *e, SGFAccessKind accessKind,
LValueOptions options, AbstractionPattern orig) {
// First see if we have an lvalue type. If we do, then quickly handle that and
// return.
if (e->getType()->is<LValueType>() || e->isSemanticallyInOutExpr()) {
return visitRecInOut(*this, e, accessKind, options, orig);
}
// Otherwise we have a non-lvalue type (references, values, metatypes,
// etc). These act as the root of a logical lvalue. Compute the root value,
// wrap it in a ValueComponent, and return it for our caller.
ManagedValue rv = visitRecNonInOutBase(*this, e, accessKind, options, orig);
CanType formalType = getSubstFormalRValueType(e);
auto typeData = getValueTypeData(accessKind, formalType, rv.getValue());
LValue lv;
lv.add<ValueComponent>(rv, None, typeData, /*isRValue=*/true);
return lv;
}
LValue SILGenLValue::visitExpr(Expr *e, SGFAccessKind accessKind,
LValueOptions options) {
e->dump(llvm::errs());
llvm::errs() << "\n";
llvm_unreachable("unimplemented lvalue expr");
}
namespace {
/// A CRTP class for emitting accesses.
template <class Impl, class StorageType>
struct AccessEmitter {
SILGenFunction &SGF;
StorageType *Storage;
CanType FormalRValueType;
SGFAccessKind AccessKind;
Impl &asImpl() { return static_cast<Impl&>(*this); }
AccessEmitter(SILGenFunction &SGF, StorageType *storage,
SGFAccessKind accessKind, CanType formalRValueType)
: SGF(SGF), Storage(storage), FormalRValueType(formalRValueType),
AccessKind(accessKind) {}
void emitUsingStrategy(AccessStrategy strategy) {
switch (strategy.getKind()) {
case AccessStrategy::Storage: {
auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage,
FormalRValueType);
return asImpl().emitUsingStorage(typeData);
}
case AccessStrategy::DirectToAccessor:
return asImpl().emitUsingAccessor(strategy.getAccessor(), true);
case AccessStrategy::DispatchToAccessor:
return asImpl().emitUsingAccessor(strategy.getAccessor(), false);
case AccessStrategy::MaterializeToTemporary: {
auto typeData =
getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType);
return asImpl().emitUsingMaterialization(strategy.getReadStrategy(),
strategy.getWriteStrategy(),
typeData);
}
}
llvm_unreachable("unknown kind");
}
void emitUsingAccessor(AccessorKind accessorKind, bool isDirect) {
auto accessor =
SGF.SGM.getAccessorDeclRef(Storage->getOpaqueAccessor(accessorKind));
switch (accessorKind) {
case AccessorKind::Get:
case AccessorKind::Set: {
auto typeData =
getLogicalStorageTypeData(SGF.SGM, AccessKind, FormalRValueType);
return asImpl().emitUsingGetterSetter(accessor, isDirect, typeData);
}
case AccessorKind::Address:
case AccessorKind::MutableAddress: {
auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage,
FormalRValueType);
return asImpl().emitUsingAddressor(accessor, isDirect, typeData);
}
case AccessorKind::Read:
case AccessorKind::Modify: {
auto typeData = getPhysicalStorageTypeData(SGF.SGM, AccessKind, Storage,
FormalRValueType);
return asImpl().emitUsingCoroutineAccessor(accessor, isDirect,
typeData);
}
case AccessorKind::WillSet:
case AccessorKind::DidSet:
llvm_unreachable("cannot use accessor directly to perform an access");
}
llvm_unreachable("bad kind");
}
};
}
static LValue emitLValueForNonMemberVarDecl(SILGenFunction &SGF,
SILLocation loc,
ConcreteDeclRef declRef,
CanType formalRValueType,
SGFAccessKind accessKind,
LValueOptions options,
AccessSemantics semantics) {
LValue lv;
auto *var = cast<VarDecl>(declRef.getDecl());
auto subs = declRef.getSubstitutions();
if (!subs)
subs = SGF.F.getForwardingSubstitutionMap();
auto access = getFormalAccessKind(accessKind);
auto strategy = var->getAccessStrategy(semantics, access,
SGF.SGM.M.getSwiftModule(),
SGF.F.getResilienceExpansion());
lv.addNonMemberVarComponent(SGF, loc, var, subs,
options, accessKind, strategy, formalRValueType);
return lv;
}
void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc,
VarDecl *var,
SubstitutionMap subs,
LValueOptions options,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType formalRValueType) {
struct NonMemberVarAccessEmitter :
AccessEmitter<NonMemberVarAccessEmitter, VarDecl> {
LValue &LV;
SILLocation Loc;
SubstitutionMap Subs;
LValueOptions Options;
NonMemberVarAccessEmitter(SILGenFunction &SGF, SILLocation loc,
VarDecl *var, SubstitutionMap subs,
SGFAccessKind accessKind,
CanType formalRValueType,
LValueOptions options, LValue &lv)
: AccessEmitter(SGF, var, accessKind, formalRValueType),
LV(lv), Loc(loc), Subs(subs), Options(options) {}
void emitUsingAddressor(SILDeclRef addressor, bool isDirect,
LValueTypeData typeData) {
SILType storageType =
SGF.getLoweredType(Storage->getType()).getAddressType();
LV.add<AddressorComponent>(Storage, addressor,
/*isSuper=*/false, isDirect, Subs,
CanType(), typeData, storageType, nullptr,
PreparedArguments(),
/* isOnSelfParameter */ false);
}
void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect,
LValueTypeData typeData) {
LV.add<CoroutineAccessorComponent>(
Storage, accessor,
/*isSuper*/ false, isDirect, Subs, CanType(), typeData, nullptr,
PreparedArguments(), /*isOnSelfParameter*/ false);
}
void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect,
LValueTypeData typeData) {
LV.add<GetterSetterComponent>(
Storage, accessor,
/*isSuper=*/false, isDirect, Subs, CanType(), typeData, nullptr,
PreparedArguments(), /* isOnSelfParameter */ false);
}
void emitUsingMaterialization(AccessStrategy readStrategy,
AccessStrategy writeStrategy,
LValueTypeData typeData) {
LV.add<MaterializeToTemporaryComponent>(
Storage, /*super*/ false, Subs, Options, readStrategy,
writeStrategy,
/*base type*/ CanType(), typeData, nullptr, PreparedArguments(),
/* isOnSelfParameter */ false);
}
void emitUsingStorage(LValueTypeData typeData) {
// If it's a physical value (e.g. a local variable in memory), push its
// address.
// Check for a local (possibly captured) variable.
auto address = SGF.maybeEmitValueOfLocalVarDecl(Storage);
// The only other case that should get here is a global variable.
if (!address) {
address = SGF.emitGlobalVariableRef(Loc, Storage);
}
assert(address.isLValue() &&
"physical lvalue decl ref must evaluate to an address");
Optional<SILAccessEnforcement> enforcement;
if (!Storage->isLet()) {
if (Options.IsNonAccessing) {
enforcement = None;
} else if (Storage->getDeclContext()->isLocalContext()) {
enforcement = SGF.getUnknownEnforcement(Storage);
} else if (Storage->getDeclContext()->isModuleScopeContext()) {
enforcement = SGF.getDynamicEnforcement(Storage);
} else {
assert(Storage->getDeclContext()->isTypeContext() &&
!Storage->isInstanceMember());
enforcement = SGF.getDynamicEnforcement(Storage);
}
}
LV.add<ValueComponent>(address, enforcement, typeData);
if (address.getType().is<ReferenceStorageType>())
LV.add<OwnershipComponent>(typeData);
}
} emitter(SGF, loc, var, subs, accessKind, formalRValueType, options, *this);
emitter.emitUsingStrategy(strategy);
}
ManagedValue
SILGenFunction::maybeEmitValueOfLocalVarDecl(VarDecl *var) {
// For local decls, use the address we allocated or the value if we have it.
auto It = VarLocs.find(var);
if (It != VarLocs.end()) {
// If this has an address, return it. By-value let's have no address.
SILValue ptr = It->second.value;
if (ptr->getType().isAddress())
return ManagedValue::forLValue(ptr);
// Otherwise, it is an RValue let. Uses of it are borrows, but we don't
// want to proactively emit a borrow here.
// TODO: integrate this with how callers want these values so we can do
// something more semantic than just forUnmanaged.
return ManagedValue::forUnmanaged(ptr);
}
// Otherwise, it's non-local or not stored.
return ManagedValue();
}
ManagedValue
SILGenFunction::emitAddressOfLocalVarDecl(SILLocation loc, VarDecl *var,
CanType formalRValueType,
SGFAccessKind accessKind) {
assert(var->getDeclContext()->isLocalContext());
assert(var->getImplInfo().isSimpleStored());
auto address = maybeEmitValueOfLocalVarDecl(var);
assert(address);
assert(address.isLValue());
return address;
}
RValue SILGenFunction::emitRValueForNonMemberVarDecl(SILLocation loc,
ConcreteDeclRef declRef,
CanType formalRValueType,
AccessSemantics semantics,
SGFContext C) {
// Any writebacks for this access are tightly scoped.
FormalEvaluationScope scope(*this);
auto *var = cast<VarDecl>(declRef.getDecl());
auto localValue = maybeEmitValueOfLocalVarDecl(var);
// If this VarDecl is represented as an address, emit it as an lvalue, then
// perform a load to get the rvalue.
if (localValue && localValue.isLValue()) {
bool guaranteedValid = false;
IsTake_t shouldTake = IsNotTake;
// We should only end up in this path for local and global variables,
// i.e. ones whose lifetime is assured for the duration of the evaluation.
// Therefore, if the variable is a constant, the value is guaranteed
// valid as well.
if (var->isLet())
guaranteedValid = true;
// Protect the lvalue read with access markers. The !is<LValueType> assert
// above ensures that the "LValue" is actually immutable, so we use an
// unenforced access marker.
SILValue destAddr = localValue.getLValueAddress();
SILValue accessAddr = UnenforcedFormalAccess::enter(*this, loc, destAddr,
SILAccessKind::Read);
auto propagateRValuePastAccess = [&](RValue &&rvalue) {
// Check if a new begin_access was emitted and returned as the
// RValue. This means that the load did not actually load. If so, then
// fix the rvalue to begin_access operand. The end_access cleanup
// doesn't change. FIXME: this can't happen with sil-opaque-values.
if (accessAddr != destAddr && rvalue.isComplete()
&& rvalue.isPlusZero(*this) && !isa<TupleType>(rvalue.getType())) {
auto mv = std::move(rvalue).getScalarValue();
if (mv.getValue() == accessAddr)
mv = std::move(mv).transform(
cast<BeginAccessInst>(accessAddr)->getOperand());
return RValue(*this, loc, formalRValueType, mv);
}
return std::move(rvalue);
};
// If we have self, see if we are in an 'init' delegation sequence. If so,
// call out to the special delegation init routine. Otherwise, use the
// normal RValue emission logic.
if (var->getName() == getASTContext().Id_self &&
SelfInitDelegationState != NormalSelf) {
auto rvalue =
emitRValueForSelfInDelegationInit(loc, formalRValueType, accessAddr, C);
return propagateRValuePastAccess(std::move(rvalue));
}
// Avoid computing an abstraction pattern for local variables.
// This is a slight compile-time optimization, but more importantly
// it avoids problems where locals don't always have interface types.
if (var->getDeclContext()->isLocalContext()) {
auto &rvalueTL = getTypeLowering(formalRValueType);
auto rvalue = RValue(*this, loc, formalRValueType,
emitLoad(loc, accessAddr, rvalueTL,
C, shouldTake, guaranteedValid));
return propagateRValuePastAccess(std::move(rvalue));
}
// Otherwise, do the full thing where we potentially bridge and
// reabstract the declaration.
auto origFormalType = SGM.Types.getAbstractionPattern(var);
auto rvalue = RValue(*this, loc, formalRValueType,
emitLoad(loc, accessAddr, origFormalType,
formalRValueType,
getTypeLowering(formalRValueType),
C, shouldTake, guaranteedValid));
return propagateRValuePastAccess(std::move(rvalue));
}
// For local decls, use the address we allocated or the value if we have it.
if (localValue) {
// Mutable lvalue and address-only 'let's are LValues.
assert(!localValue.getType().isAddress() &&
"LValue cases should be handled above");
SILValue Scalar = localValue.getUnmanagedValue();
// For weak and unowned types, convert the reference to the right
// pointer.
if (Scalar->getType().is<ReferenceStorageType>()) {
Scalar = emitConversionToSemanticRValue(loc, Scalar,
getTypeLowering(formalRValueType));
// emitConversionToSemanticRValue always produces a +1 strong result.
return RValue(*this, loc, formalRValueType,
emitManagedRValueWithCleanup(Scalar));
}
// This is a let, so we can make guarantees, so begin the borrow scope.
ManagedValue Result = emitManagedBeginBorrow(loc, Scalar);
// If the client can't handle a +0 result, retain it to get a +1.
// This is a 'let', so we can make guarantees.
return RValue(*this, loc, formalRValueType,
C.isGuaranteedPlusZeroOk()
? Result : Result.copyUnmanaged(*this, loc));
}
LValue lv = emitLValueForNonMemberVarDecl(*this, loc, declRef,
formalRValueType,
SGFAccessKind::OwnedObjectRead,
LValueOptions(), semantics);
return emitLoadOfLValue(loc, std::move(lv), C);
}
LValue SILGenLValue::visitDiscardAssignmentExpr(DiscardAssignmentExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
LValueTypeData typeData = getValueTypeData(SGF, accessKind, e);
SILValue address = SGF.emitTemporaryAllocation(e,
SILType::getPrimitiveObjectType(
typeData.TypeOfRValue));
address = SGF.B.createMarkUninitialized(e, address,
MarkUninitializedInst::Var);
LValue lv;
lv.add<ValueComponent>(SGF.emitManagedBufferWithCleanup(address),
None, typeData);
return lv;
}
LValue SILGenLValue::visitDeclRefExpr(DeclRefExpr *e, SGFAccessKind accessKind,
LValueOptions options) {
return emitLValueForNonMemberVarDecl(SGF, e, e->getDeclRef(),
getSubstFormalRValueType(e),
accessKind, options,
e->getAccessSemantics());
}
LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
// Handle an opaque lvalue that refers to an opened existential.
auto known = SGF.OpaqueValueExprs.find(e);
if (known != SGF.OpaqueValueExprs.end()) {
// Dig the open-existential expression out of the list.
OpenExistentialExpr *opened = known->second;
SGF.OpaqueValueExprs.erase(known);
// Do formal evaluation of the underlying existential lvalue.
auto lv = visitRec(opened->getExistentialValue(), accessKind, options);
lv = SGF.emitOpenExistentialLValue(
opened, std::move(lv),
CanArchetypeType(opened->getOpenedArchetype()),
e->getType()->getWithoutSpecifierType()->getCanonicalType(),
accessKind);
return lv;
}
assert(SGF.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr");
auto value = SGF.OpaqueValues[e];
RegularLocation loc(e);
LValue lv;
lv.add<ValueComponent>(value.formalAccessBorrow(SGF, loc), None,
getValueTypeData(SGF, accessKind, e));
return lv;
}
LValue SILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
SGF.emitIgnoredExpr(e->getLHS());
return visitRec(e->getRHS(), accessKind, options);
}
static SGFAccessKind getBaseAccessKindForAccessor(SILGenModule &SGM,
AccessorDecl *accessor,
CanType baseFormalType) {
if (accessor->isMutating()) {
return SGFAccessKind::ReadWrite;
} else if (SGM.shouldEmitSelfAsRValue(accessor, baseFormalType)) {
return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
? SGFAccessKind::OwnedAddressRead
: SGFAccessKind::OwnedObjectRead;
} else {
return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
? SGFAccessKind::BorrowedAddressRead
: SGFAccessKind::BorrowedObjectRead;
}
}
static SGFAccessKind getBaseAccessKindForStorage(SGFAccessKind accessKind) {
// Assume that the member only partially projects the enclosing value,
// so a write to the member is a read/write of the base.
return (accessKind == SGFAccessKind::Write
? SGFAccessKind::ReadWrite : accessKind);
}
/// Return the appropriate access kind for the base l-value of a
/// particular member, which is being accessed in a particular way.
static SGFAccessKind getBaseAccessKind(SILGenModule &SGM,
AbstractStorageDecl *member,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType baseFormalType) {
switch (strategy.getKind()) {
case AccessStrategy::Storage:
return getBaseAccessKindForStorage(accessKind);
case AccessStrategy::MaterializeToTemporary: {
assert(accessKind == SGFAccessKind::ReadWrite);
auto writeBaseKind = getBaseAccessKind(SGM, member, SGFAccessKind::Write,
strategy.getWriteStrategy(),
baseFormalType);
// Fast path for the common case that the write will need to mutate
// the base.
if (writeBaseKind == SGFAccessKind::ReadWrite)
return writeBaseKind;
auto readBaseKind = getBaseAccessKind(SGM, member,
SGFAccessKind::OwnedAddressRead,
strategy.getReadStrategy(),
baseFormalType);
// If they're the same kind, just use that.
if (readBaseKind == writeBaseKind)
return readBaseKind;
// If either access is mutating, the total access is a read-write.
if (!isReadAccess(readBaseKind) || !isReadAccess(writeBaseKind))
return SGFAccessKind::ReadWrite;
// Okay, we have two different kinds of read somehow for different
// accessors of the same storage?
return SGFAccessKind::OwnedObjectRead;
}
case AccessStrategy::DirectToAccessor:
case AccessStrategy::DispatchToAccessor: {
auto accessor = member->getOpaqueAccessor(strategy.getAccessor());
return getBaseAccessKindForAccessor(SGM, accessor, baseFormalType);
}
}
llvm_unreachable("bad access strategy");
}
static CanType getBaseFormalType(Expr *baseExpr) {
return baseExpr->getType()->getWithoutSpecifierType()->getCanonicalType();
}
bool isCallToReplacedInDynamicReplacement(SILGenFunction &SGF,
AbstractFunctionDecl *afd,
bool &isObjCReplacementSelfCall);
static bool isCallToSelfOfCurrentFunction(SILGenFunction &SGF, LookupExpr *e) {
return SGF.FunctionDC->getAsDecl() &&
isa<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()) &&
e->getBase()->isSelfExprOf(
cast<AbstractFunctionDecl>(SGF.FunctionDC->getAsDecl()), false);
}
static bool isCurrentFunctionReadAccess(SILGenFunction &SGF) {
auto *contextAccessorDecl =
dyn_cast_or_null<AccessorDecl>(SGF.FunctionDC->getAsDecl());
return contextAccessorDecl &&
contextAccessorDecl->getAccessorKind() == AccessorKind::Read;
}
LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
// MemberRefExpr can refer to type and function members, but the only case
// that can be an lvalue is a VarDecl.
VarDecl *var = cast<VarDecl>(e->getMember().getDecl());
auto accessSemantics = e->getAccessSemantics();
AccessStrategy strategy =
var->getAccessStrategy(accessSemantics,
getFormalAccessKind(accessKind),
SGF.SGM.M.getSwiftModule(),
SGF.F.getResilienceExpansion());
bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e);
bool isContextRead = isCurrentFunctionReadAccess(SGF);
// If we are inside _read, calling self.get, and the _read we are inside of is
// the same as the as self's variable and the current function is a
// dynamic replacement directly call the implementation.
if (isContextRead && isOnSelfParameter && strategy.hasAccessor() &&
strategy.getAccessor() == AccessorKind::Get &&
var->getOpaqueAccessor(AccessorKind::Read)) {
bool isObjC = false;
auto readAccessor =
SGF.SGM.getAccessorDeclRef(var->getOpaqueAccessor(AccessorKind::Read));
if (isCallToReplacedInDynamicReplacement(
SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
accessSemantics = AccessSemantics::DirectToImplementation;
strategy = var->getAccessStrategy(
accessSemantics,
getFormalAccessKind(accessKind),
SGF.SGM.M.getSwiftModule(),
SGF.F.getResilienceExpansion());
}
}
LValue lv = visitRec(e->getBase(),
getBaseAccessKind(SGF.SGM, var, accessKind, strategy,
getBaseFormalType(e->getBase())),
getBaseOptions(options, strategy));
assert(lv.isValid());
CanType substFormalRValueType = getSubstFormalRValueType(e);
lv.addMemberVarComponent(SGF, e, var, e->getMember().getSubstitutions(),
options, e->isSuper(), accessKind, strategy,
substFormalRValueType, isOnSelfParameter);
return lv;
}
namespace {
/// A CRTP class for emitting member accesses.
template <class Impl, class StorageType>
struct MemberStorageAccessEmitter : AccessEmitter<Impl, StorageType> {
using super = AccessEmitter<Impl, StorageType>;
using super::SGF;
using super::Storage;
using super::FormalRValueType;
LValue &LV;
LValueOptions Options;
SILLocation Loc;
bool IsSuper;
bool IsOnSelfParameter; // Is self the self parameter in context.
CanType BaseFormalType;
SubstitutionMap Subs;
Expr *IndexExprForDiagnostics;
PreparedArguments Indices;
MemberStorageAccessEmitter(SILGenFunction &SGF, SILLocation loc,
StorageType *storage, SubstitutionMap subs,
bool isSuper, SGFAccessKind accessKind,
CanType formalRValueType, LValueOptions options,
LValue &lv, Expr *indexExprForDiagnostics,
PreparedArguments &&indices, bool isSelf = false)
: super(SGF, storage, accessKind, formalRValueType), LV(lv),
Options(options), Loc(loc), IsSuper(isSuper), IsOnSelfParameter(isSelf),
BaseFormalType(lv.getSubstFormalType()), Subs(subs),
IndexExprForDiagnostics(indexExprForDiagnostics),
Indices(std::move(indices)) {}
void emitUsingAddressor(SILDeclRef addressor, bool isDirect,
LValueTypeData typeData) {
SILType varStorageType =
SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType);
LV.add<AddressorComponent>(Storage, addressor, IsSuper, isDirect, Subs,
BaseFormalType, typeData, varStorageType,
IndexExprForDiagnostics, std::move(Indices),
IsOnSelfParameter);
}
void emitUsingCoroutineAccessor(SILDeclRef accessor, bool isDirect,
LValueTypeData typeData) {
LV.add<CoroutineAccessorComponent>(
Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData,
IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter);
}
void emitUsingGetterSetter(SILDeclRef accessor, bool isDirect,
LValueTypeData typeData) {
LV.add<GetterSetterComponent>(
Storage, accessor, IsSuper, isDirect, Subs, BaseFormalType, typeData,
IndexExprForDiagnostics, std::move(Indices), IsOnSelfParameter);
}
void emitUsingMaterialization(AccessStrategy readStrategy,
AccessStrategy writeStrategy,
LValueTypeData typeData) {
LV.add<MaterializeToTemporaryComponent>(
Storage, IsSuper, Subs, Options, readStrategy, writeStrategy,
BaseFormalType, typeData, IndexExprForDiagnostics, std::move(Indices),
IsOnSelfParameter);
}
};
} // end anonymous namespace
void LValue::addMemberVarComponent(SILGenFunction &SGF, SILLocation loc,
VarDecl *var,
SubstitutionMap subs,
LValueOptions options,
bool isSuper,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType formalRValueType,
bool isOnSelfParameter) {
struct MemberVarAccessEmitter
: MemberStorageAccessEmitter<MemberVarAccessEmitter, VarDecl> {
using MemberStorageAccessEmitter::MemberStorageAccessEmitter;
void emitUsingStorage(LValueTypeData typeData) {
// For static variables, emit a reference to the global variable backing
// them.
// FIXME: This has to be dynamically looked up for classes, and
// dynamically instantiated for generics.
if (Storage->isStatic()) {
// FIXME: this implicitly drops the earlier components, but maybe
// we ought to evaluate them for side-effects even during the
// formal access?
LV.Path.clear();
LV.addNonMemberVarComponent(SGF, Loc, Storage, Subs, Options,
typeData.getAccessKind(),
AccessStrategy::getStorage(),
FormalRValueType);
return;
}
// Otherwise, it's a physical member.
SILType varStorageType =
SGF.SGM.Types.getSubstitutedStorageType(Storage, FormalRValueType);
if (BaseFormalType->mayHaveSuperclass()) {
LV.add<RefElementComponent>(Storage, Options, varStorageType, typeData);
} else {
assert(BaseFormalType->getStructOrBoundGenericStruct());
LV.add<StructElementComponent>(Storage, varStorageType, typeData);
}
// If the member has weak or unowned storage, convert it away.
if (varStorageType.is<ReferenceStorageType>()) {
LV.add<OwnershipComponent>(typeData);
}
}
} emitter(SGF, loc, var, subs, isSuper, accessKind,
formalRValueType, options, *this,
/*indices for diags*/ nullptr, /*indices*/ PreparedArguments(),
isOnSelfParameter);
emitter.emitUsingStrategy(strategy);
}
LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
auto decl = cast<SubscriptDecl>(e->getDecl().getDecl());
auto subs = e->getDecl().getSubstitutions();
auto accessSemantics = e->getAccessSemantics();
auto strategy =
decl->getAccessStrategy(accessSemantics,
getFormalAccessKind(accessKind),
SGF.SGM.M.getSwiftModule(),
SGF.F.getResilienceExpansion());
bool isOnSelfParameter = isCallToSelfOfCurrentFunction(SGF, e);
bool isContextRead = isCurrentFunctionReadAccess(SGF);
// If we are inside _read, calling self.get, and the _read we are inside of is
// the same as the as self's variable and the current function is a
// dynamic replacement directly call the implementation.
if (isContextRead && isOnSelfParameter && strategy.hasAccessor() &&
strategy.getAccessor() == AccessorKind::Get &&
decl->getOpaqueAccessor(AccessorKind::Read)) {
bool isObjC = false;
auto readAccessor =
SGF.SGM.getAccessorDeclRef(decl->getOpaqueAccessor(AccessorKind::Read));
if (isCallToReplacedInDynamicReplacement(
SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
accessSemantics = AccessSemantics::DirectToImplementation;
strategy = decl->getAccessStrategy(
accessSemantics,
getFormalAccessKind(accessKind),
SGF.SGM.M.getSwiftModule(),
SGF.F.getResilienceExpansion());
}
}
LValue lv = visitRec(e->getBase(),
getBaseAccessKind(SGF.SGM, decl, accessKind, strategy,
getBaseFormalType(e->getBase())),
getBaseOptions(options, strategy));
assert(lv.isValid());
Expr *indexExpr = e->getIndex();
auto indices = SGF.prepareSubscriptIndices(decl, subs, strategy, indexExpr);
CanType formalRValueType = getSubstFormalRValueType(e);
lv.addMemberSubscriptComponent(SGF, e, decl, subs,
options, e->isSuper(), accessKind, strategy,
formalRValueType, std::move(indices),
indexExpr, isOnSelfParameter);
return lv;
}
LValue SILGenLValue::visitKeyPathApplicationExpr(KeyPathApplicationExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
auto keyPathExpr = e->getKeyPath();
auto keyPathKind =
*keyPathExpr->getType()->getAnyNominal()->getKeyPathTypeKind();
// Determine the base access strategy based on the strategy of this access.
SGFAccessKind subAccess;
if (!isReadAccess(accessKind)) {
// The only read-write accesses we allow are with WritableKeyPath and
// ReferenceWritableKeyPath.
assert(keyPathKind == KPTK_WritableKeyPath ||
keyPathKind == KPTK_ReferenceWritableKeyPath);
subAccess = (keyPathKind == KPTK_ReferenceWritableKeyPath
? SGFAccessKind::BorrowedAddressRead
: SGFAccessKind::ReadWrite);
} else {
// For all the other kinds, we want the emit the base as an address
// r-value; we don't support key paths for storage with mutating read
// operations.
subAccess = SGFAccessKind::BorrowedAddressRead;
}
// For now, just ignore any options we were given.
LValueOptions subOptions = LValueOptions();
// Do formal evaluation of the base l-value.
// The base should be reabstracted to the maximal abstraction pattern.
LValue lv = visitRec(e->getBase(), subAccess, subOptions,
AbstractionPattern::getOpaque());
// Emit the key path r-value.
auto keyPath = SGF.emitRValueAsSingleValue(keyPathExpr);
// The result will end up projected at the maximal abstraction level too.
auto substFormalType = e->getType()->getRValueType()->getCanonicalType();
bool useLogical = [&] {
switch (accessKind) {
// Use the physical 'read' pattern for these unless we're working with
// AnyKeyPath or PartialKeyPath, which only have getters.
case SGFAccessKind::BorrowedAddressRead:
case SGFAccessKind::BorrowedObjectRead:
return (keyPathKind == KPTK_AnyKeyPath ||
keyPathKind == KPTK_PartialKeyPath);
case SGFAccessKind::OwnedObjectRead:
case SGFAccessKind::OwnedAddressRead:
case SGFAccessKind::IgnoredRead: // should we use physical for this?
case SGFAccessKind::Write:
return true;
case SGFAccessKind::ReadWrite:
return false;
}
llvm_unreachable("bad access kind");
}();
if (useLogical) {
auto typeData = getLogicalStorageTypeData(SGF.SGM, accessKind,
substFormalType);
Type baseFormalType = e->getBase()->getType()->getRValueType();
lv.add<LogicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath,
baseFormalType);
// TODO: make LogicalKeyPathApplicationComponent expect/produce values
// in the opaque AbstractionPattern and push an OrigToSubstComponent here
// so it can be peepholed.
} else {
auto typeData = getAbstractedTypeData(SGF.SGM, accessKind,
AbstractionPattern::getOpaque(),
substFormalType);
lv.add<PhysicalKeyPathApplicationComponent>(typeData, keyPathKind, keyPath);
// Reabstract to the substituted abstraction level if necessary.
auto substResultSILTy = SGF.getLoweredType(substFormalType);
if (typeData.TypeOfRValue != substResultSILTy.getASTType()) {
lv.addOrigToSubstComponent(substResultSILTy);
}
}
return lv;
}
void LValue::addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc,
SubscriptDecl *decl,
SubstitutionMap subs,
LValueOptions options,
bool isSuper,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType formalRValueType,
PreparedArguments &&indices,
Expr *indexExprForDiagnostics,
bool isOnSelfParameter) {
struct MemberSubscriptAccessEmitter
: MemberStorageAccessEmitter<MemberSubscriptAccessEmitter,
SubscriptDecl> {
using MemberStorageAccessEmitter::MemberStorageAccessEmitter;
void emitUsingStorage(LValueTypeData typeData) {
llvm_unreachable("subscripts never have storage");
}
} emitter(SGF, loc, decl, subs, isSuper, accessKind, formalRValueType,
options, *this, indexExprForDiagnostics, std::move(indices),
isOnSelfParameter);
emitter.emitUsingStrategy(strategy);
}
bool LValue::isObviouslyNonConflicting(const LValue &other,
SGFAccessKind selfAccess,
SGFAccessKind otherAccess) {
// Reads never conflict with reads.
if (isReadAccess(selfAccess) && isReadAccess(otherAccess))
return true;
// We can cover more cases here.
return false;
}
LValue SILGenLValue::visitTupleElementExpr(TupleElementExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
unsigned index = e->getFieldNumber();
LValue lv = visitRec(e->getBase(),
getBaseAccessKindForStorage(accessKind),
options.forProjectedBaseLValue());
auto baseTypeData = lv.getTypeData();
LValueTypeData typeData = {
accessKind,
baseTypeData.OrigFormalType.getTupleElementType(index),
cast<TupleType>(baseTypeData.SubstFormalType).getElementType(index),
cast<TupleType>(baseTypeData.TypeOfRValue).getElementType(index)
};
lv.add<TupleElementComponent>(index, typeData);
return lv;
}
LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
// If the opaque value is not an lvalue, open the existential immediately.
if (!e->getOpaqueValue()->getType()->is<LValueType>()) {
return SGF.emitOpenExistentialExpr<LValue>(e,
[&](Expr *subExpr) -> LValue {
return visitRec(subExpr,
accessKind,
options);
});
}
// Record the fact that we're opening this existential. The actual
// opening operation will occur when we see the OpaqueValueExpr.
bool inserted = SGF.OpaqueValueExprs.insert({e->getOpaqueValue(), e}).second;
(void)inserted;
assert(inserted && "already have this opened existential?");
// Visit the subexpression.
LValue lv = visitRec(e->getSubExpr(), accessKind, options);
// Sanity check that we did see the OpaqueValueExpr.
assert(SGF.OpaqueValueExprs.count(e->getOpaqueValue()) == 0 &&
"opened existential not removed?");
return lv;
}
static LValueTypeData
getOptionalObjectTypeData(SILGenFunction &SGF, SGFAccessKind accessKind,
const LValueTypeData &baseTypeData) {
return {
accessKind,
baseTypeData.OrigFormalType.getOptionalObjectType(),
baseTypeData.SubstFormalType.getOptionalObjectType(),
baseTypeData.TypeOfRValue.getOptionalObjectType(),
};
}
LValue SILGenLValue::visitForceValueExpr(ForceValueExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
// Like BindOptional, this is a read even if we only write to the result.
// (But it's unnecessary to use a force this way!)
LValue lv = visitRec(e->getSubExpr(),
getBaseAccessKindForStorage(accessKind),
options.forComputedBaseLValue());
LValueTypeData typeData =
getOptionalObjectTypeData(SGF, accessKind, lv.getTypeData());
bool isImplicitUnwrap = e->isImplicit() &&
e->isForceOfImplicitlyUnwrappedOptional();
lv.add<ForceOptionalObjectComponent>(typeData, isImplicitUnwrap);
return lv;
}
LValue SILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e,
SGFAccessKind accessKind,
LValueOptions options) {
// Binding reads the base even if we then only write to the result.
auto baseAccessKind = getBaseAccessKindForStorage(accessKind);
// We're going to take the address of the base.
// TODO: deal more efficiently with an object-preferring access.
baseAccessKind = getAddressAccessKind(baseAccessKind);
// Do formal evaluation of the base l-value.
LValue optLV = visitRec(e->getSubExpr(), baseAccessKind,
options.forComputedBaseLValue());
LValueTypeData optTypeData = optLV.getTypeData();
LValueTypeData valueTypeData =
getOptionalObjectTypeData(SGF, accessKind, optTypeData);
// The chaining operator immediately begins a formal access to the
// base l-value. In concrete terms, this means we can immediately
// evaluate the base down to an address.
ManagedValue optAddr = SGF.emitAddressOfLValue(e, std::move(optLV));
// Bind the value, branching to the destination address if there's no
// value there.
SGF.emitBindOptionalAddress(e, optAddr, e->getDepth());
// Project out the payload on the success branch. We can just use a
// naked ValueComponent here; this is effectively a separate l-value.
ManagedValue valueAddr =
getAddressOfOptionalValue(SGF, e, optAddr, valueTypeData);
LValue valueLV;
valueLV.add<ValueComponent>(valueAddr, None, valueTypeData);
return valueLV;
}
LValue SILGenLValue::visitInOutExpr(InOutExpr *e, SGFAccessKind accessKind,
LValueOptions options) {
return visitRec(e->getSubExpr(), accessKind, options);
}
/// Emit an lvalue that refers to the given property. This is
/// designed to work with ManagedValue 'base's that are either +0 or +1.
LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base,
CanType baseFormalType,
VarDecl *ivar,
LValueOptions options,
SGFAccessKind accessKind,
AccessSemantics semantics) {
SILGenLValue sgl(*this);
LValue lv;
auto baseType = base.getType().getASTType();
auto subMap = baseType->getContextSubstitutionMap(
SGM.M.getSwiftModule(), ivar->getDeclContext());
AccessStrategy strategy =
ivar->getAccessStrategy(semantics,
getFormalAccessKind(accessKind),
SGM.M.getSwiftModule(),
F.getResilienceExpansion());
auto baseAccessKind =
getBaseAccessKind(SGM, ivar, accessKind, strategy, baseFormalType);
LValueTypeData baseTypeData =
getValueTypeData(baseAccessKind, baseFormalType, base.getValue());
// Refer to 'self' as the base of the lvalue.
lv.add<ValueComponent>(base, None, baseTypeData,
/*isRValue=*/!base.isLValue());
auto substFormalType = ivar->getValueInterfaceType().subst(subMap)
->getCanonicalType();
lv.addMemberVarComponent(*this, loc, ivar, subMap, options, /*super*/ false,
accessKind, strategy, substFormalType);
return lv;
}
// This is emitLoad that will handle re-abstraction and bridging for the client.
ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr,
AbstractionPattern origFormalType,
CanType substFormalType,
const TypeLowering &rvalueTL,
SGFContext C, IsTake_t isTake,
bool isAddressGuaranteed) {
assert(addr->getType().isAddress());
SILType addrRValueType = addr->getType().getReferenceStorageReferentType();
// Fast path: the types match exactly.
if (addrRValueType == rvalueTL.getLoweredType().getAddressType()) {
return emitLoad(loc, addr, rvalueTL, C, isTake, isAddressGuaranteed);
}
// Otherwise, we need to reabstract or bridge.
auto conversion =
origFormalType.isClangType()
? Conversion::getBridging(Conversion::BridgeFromObjC,
origFormalType.getType(),
substFormalType, rvalueTL.getLoweredType())
: Conversion::getOrigToSubst(origFormalType, substFormalType);
return emitConvertedRValue(loc, conversion, C,
[&](SILGenFunction &SGF, SILLocation loc, SGFContext C) {
return SGF.emitLoad(loc, addr, getTypeLowering(addrRValueType),
C, isTake, isAddressGuaranteed);
});
}
/// Load an r-value out of the given address.
///
/// \param rvalueTL - the type lowering for the type-of-rvalue
/// of the address
/// \param isAddrGuaranteed - true if the value in this address
/// is guaranteed to be valid for the duration of the current
/// evaluation (see SGFContext::AllowGuaranteedPlusZero)
ManagedValue SILGenFunction::emitLoad(SILLocation loc, SILValue addr,
const TypeLowering &rvalueTL,
SGFContext C, IsTake_t isTake,
bool isAddrGuaranteed) {
// Get the lowering for the address type. We can avoid a re-lookup
// in the very common case of this being equivalent to the r-value
// type.
auto &addrTL =
(addr->getType() == rvalueTL.getLoweredType().getAddressType()
? rvalueTL : getTypeLowering(addr->getType()));
// Never do a +0 load together with a take.
bool isPlusZeroOk = (isTake == IsNotTake &&
(isAddrGuaranteed ? C.isGuaranteedPlusZeroOk()
: C.isImmediatePlusZeroOk()));
if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) {
// If the client is cool with a +0 rvalue, the decl has an address-only
// type, and there are no conversions, then we can return this as a +0
// address RValue.
if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType())
return ManagedValue::forUnmanaged(addr);
// Copy the address-only value.
return B.bufferForExpr(
loc, rvalueTL.getLoweredType(), rvalueTL, C,
[&](SILValue newAddr) {
emitSemanticLoadInto(loc, addr, addrTL, newAddr, rvalueTL,
isTake, IsInitialization);
});
}
// Ok, this is something loadable. If this is a non-take access at plus zero,
// we can perform a +0 load of the address instead of materializing a +1
// value.
if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
return B.createLoadBorrow(loc, ManagedValue::forUnmanaged(addr));
}
// Load the loadable value, and retain it if we aren't taking it.
SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
return emitManagedRValueWithCleanup(loadedV);
}
/// Load an r-value out of the given address.
///
/// \param rvalueTL - the type lowering for the type-of-rvalue
/// of the address
/// \param isAddressGuaranteed - true if the value in this address
/// is guaranteed to be valid for the duration of the current
/// evaluation (see SGFContext::AllowGuaranteedPlusZero)
ManagedValue SILGenFunction::emitFormalAccessLoad(SILLocation loc,
SILValue addr,
const TypeLowering &rvalueTL,
SGFContext C, IsTake_t isTake,
bool isAddressGuaranteed) {
// Get the lowering for the address type. We can avoid a re-lookup
// in the very common case of this being equivalent to the r-value
// type.
auto &addrTL = (addr->getType() == rvalueTL.getLoweredType().getAddressType()
? rvalueTL
: getTypeLowering(addr->getType()));
// Never do a +0 load together with a take.
bool isPlusZeroOk =
(isTake == IsNotTake && (isAddressGuaranteed ? C.isGuaranteedPlusZeroOk()
: C.isImmediatePlusZeroOk()));
if (rvalueTL.isAddressOnly() && silConv.useLoweredAddresses()) {
// If the client is cool with a +0 rvalue, the decl has an address-only
// type, and there are no conversions, then we can return this as a +0
// address RValue.
if (isPlusZeroOk && rvalueTL.getLoweredType() == addrTL.getLoweredType())
return ManagedValue::forUnmanaged(addr);
// Copy the address-only value.
return B.formalAccessBufferForExpr(
loc, rvalueTL.getLoweredType(), rvalueTL, C,
[&](SILValue addressForCopy) {
emitSemanticLoadInto(loc, addr, addrTL, addressForCopy, rvalueTL,
isTake, IsInitialization);
});
}
// Ok, this is something loadable. If this is a non-take access at plus zero,
// we can perform a +0 load of the address instead of materializing a +1
// value.
if (isPlusZeroOk && addrTL.getLoweredType() == rvalueTL.getLoweredType()) {
return B.createFormalAccessLoadBorrow(loc,
ManagedValue::forUnmanaged(addr));
}
// Load the loadable value, and retain it if we aren't taking it.
SILValue loadedV = emitSemanticLoad(loc, addr, addrTL, rvalueTL, isTake);
return emitFormalAccessManagedRValueWithCleanup(loc, loadedV);
}
static void emitUnloweredStoreOfCopy(SILGenBuilder &B, SILLocation loc,
SILValue value, SILValue addr,
IsInitialization_t isInit) {
if (isInit) {
B.emitStoreValueOperation(loc, value, addr, StoreOwnershipQualifier::Init);
} else {
B.createAssign(loc, value, addr, AssignOwnershipQualifier::Unknown);
}
}
SILValue SILGenFunction::emitConversionToSemanticRValue(SILLocation loc,
SILValue src,
const TypeLowering &valueTL) {
auto storageType = src->getType();
auto swiftStorageType = storageType.castTo<ReferenceStorageType>();
switch (swiftStorageType->getOwnership()) {
case ReferenceOwnership::Strong:
llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
/* Address-only storage types are handled with their underlying type. */ \
llvm_unreachable("address-only pointers are handled elsewhere");
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
return B.createCopy##Name##Value(loc, src);
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For loadable reference storage types, we need to generate a strong */ \
/* retain and strip the box. */ \
assert(storageType.castTo<Name##StorageType>()->isLoadable( \
ResilienceExpansion::Maximal)); \
return B.createCopy##Name##Value(loc, src); \
}
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For static reference storage types, we need to strip the box and */ \
/* then do an (unsafe) retain. */ \
auto type = storageType.castTo<Name##StorageType>(); \
auto result = B.create##Name##ToRef(loc, src, \
SILType::getPrimitiveObjectType(type.getReferentType())); \
/* SEMANTIC ARC TODO: Does this need a cleanup? */ \
return B.createCopyValue(loc, result); \
}
#include "swift/AST/ReferenceStorage.def"
}
llvm_unreachable("impossible");
}
ManagedValue SILGenFunction::emitConversionToSemanticRValue(
SILLocation loc, ManagedValue src, const TypeLowering &valueTL) {
auto swiftStorageType = src.getType().castTo<ReferenceStorageType>();
switch (swiftStorageType->getOwnership()) {
case ReferenceOwnership::Strong:
llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
/* Address-only storage types are handled with their underlying type. */ \
llvm_unreachable("address-only pointers are handled elsewhere");
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
/* Generate a strong retain and strip the box. */ \
return B.createCopy##Name##Value(loc, src);
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
/* Strip the box and then do an (unsafe) retain. */ \
return B.createUnsafeCopy##Name##Value(loc, src);
#include "swift/AST/ReferenceStorage.def"
}
llvm_unreachable("impossible");
}
/// Given that the type-of-rvalue differs from the type-of-storage,
/// and given that the type-of-rvalue is loadable, produce a +1 scalar
/// of the type-of-rvalue.
static SILValue emitLoadOfSemanticRValue(SILGenFunction &SGF,
SILLocation loc,
SILValue src,
const TypeLowering &valueTL,
IsTake_t isTake) {
auto storageType = src->getType();
auto swiftStorageType = storageType.castTo<ReferenceStorageType>();
switch (swiftStorageType->getOwnership()) {
case ReferenceOwnership::Strong:
llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
return SGF.B.createLoad##Name(loc, src, isTake);
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
{ \
/* For loadable types, we need to strip the box. */ \
/* If we are not performing a take, use a load_borrow. */ \
if (!isTake) { \
SILValue value = SGF.B.createLoadBorrow(loc, src); \
SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \
SGF.B.createEndBorrow(loc, value, src); \
return strongValue; \
} \
/* Otherwise perform a load take and destroy the stored value. */ \
auto value = SGF.B.emitLoadValueOperation(loc, src, \
LoadOwnershipQualifier::Take); \
SILValue strongValue = SGF.B.createCopy##Name##Value(loc, value); \
SGF.B.createDestroyValue(loc, value); \
return strongValue; \
}
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For loadable types, we need to strip the box. */ \
auto type = storageType.castTo<Name##StorageType>(); \
if (!type->isLoadable(ResilienceExpansion::Maximal)) { \
return SGF.B.createLoad##Name(loc, src, isTake); \
} \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
}
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For static reference storage types, we need to strip the box. */ \
auto type = storageType.castTo<Name##StorageType>(); \
auto value = SGF.B.createLoad(loc, src, LoadOwnershipQualifier::Trivial); \
auto result = SGF.B.create##Name##ToRef(loc, value, \
SILType::getPrimitiveObjectType(type.getReferentType())); \
/* SEMANTIC ARC TODO: Does this need a cleanup? */ \
return SGF.B.createCopyValue(loc, result); \
}
#include "swift/AST/ReferenceStorage.def"
#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER
}
llvm_unreachable("unhandled ownership");
}
/// Given that the type-of-rvalue differs from the type-of-storage,
/// store a +1 value (possibly not a scalar) of the type-of-rvalue
/// into the given address.
static void emitStoreOfSemanticRValue(SILGenFunction &SGF,
SILLocation loc,
SILValue value,
SILValue dest,
const TypeLowering &valueTL,
IsInitialization_t isInit) {
auto storageType = dest->getType();
auto swiftStorageType = storageType.castTo<ReferenceStorageType>();
switch (swiftStorageType->getOwnership()) {
case ReferenceOwnership::Strong:
llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
SGF.B.createStore##Name(loc, value, dest, isInit); \
/* store doesn't take ownership of the input, so cancel it out. */ \
SGF.B.emitDestroyValueOperation(loc, value); \
return; \
}
#define ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
{ \
auto typedValue = SGF.B.createRefTo##Name(loc, value, \
storageType.getObjectType()); \
auto copiedVal = SGF.B.createCopyValue(loc, typedValue); \
emitUnloweredStoreOfCopy(SGF.B, loc, copiedVal, dest, isInit); \
SGF.B.emitDestroyValueOperation(loc, value); \
return; \
}
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For loadable types, we need to enter the box by */ \
/* turning the strong retain into an type-specific retain. */ \
auto type = storageType.castTo<Name##StorageType>(); \
/* FIXME: resilience */ \
if (!type->isLoadable(ResilienceExpansion::Maximal)) { \
SGF.B.createStore##Name(loc, value, dest, isInit); \
/* store doesn't take ownership of the input, so cancel it out. */ \
SGF.B.emitDestroyValueOperation(loc, value); \
return; \
} \
ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name) \
}
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For static reference storage types, we need to enter the box and */ \
/* release the strong retain. */ \
auto typedValue = SGF.B.createRefTo##Name(loc, value, \
storageType.getObjectType()); \
emitUnloweredStoreOfCopy(SGF.B, loc, typedValue, dest, isInit); \
SGF.B.emitDestroyValueOperation(loc, value); \
return; \
}
#include "swift/AST/ReferenceStorage.def"
#undef ALWAYS_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER
}
llvm_unreachable("impossible");
}
/// Load a value of the type-of-rvalue out of the given address as a
/// scalar. The type-of-rvalue must be loadable.
SILValue SILGenFunction::emitSemanticLoad(SILLocation loc,
SILValue src,
const TypeLowering &srcTL,
const TypeLowering &rvalueTL,
IsTake_t isTake) {
assert(srcTL.getLoweredType().getAddressType() == src->getType());
assert(rvalueTL.isLoadable() || !silConv.useLoweredAddresses());
// Easy case: the types match.
if (srcTL.getLoweredType() == rvalueTL.getLoweredType()) {
return srcTL.emitLoadOfCopy(B, loc, src, isTake);
}
return emitLoadOfSemanticRValue(*this, loc, src, rvalueTL, isTake);
}
/// Load a value of the type-of-reference out of the given address
/// and into the destination address.
void SILGenFunction::emitSemanticLoadInto(SILLocation loc,
SILValue src,
const TypeLowering &srcTL,
SILValue dest,
const TypeLowering &destTL,
IsTake_t isTake,
IsInitialization_t isInit) {
assert(srcTL.getLoweredType().getAddressType() == src->getType());
assert(destTL.getLoweredType().getAddressType() == dest->getType());
// Easy case: the types match.
if (srcTL.getLoweredType() == destTL.getLoweredType()) {
B.createCopyAddr(loc, src, dest, isTake, isInit);
return;
}
auto rvalue = emitLoadOfSemanticRValue(*this, loc, src, srcTL, isTake);
emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit);
}
/// Store an r-value into the given address as an initialization.
void SILGenFunction::emitSemanticStore(SILLocation loc,
SILValue rvalue,
SILValue dest,
const TypeLowering &destTL,
IsInitialization_t isInit) {
assert(destTL.getLoweredType().getAddressType() == dest->getType());
// Easy case: the types match.
if (rvalue->getType() == destTL.getLoweredType()) {
assert(!silConv.useLoweredAddresses()
|| (destTL.isAddressOnly() == rvalue->getType().isAddress()));
if (rvalue->getType().isAddress()) {
B.createCopyAddr(loc, rvalue, dest, IsTake, isInit);
} else {
emitUnloweredStoreOfCopy(B, loc, rvalue, dest, isInit);
}
return;
}
auto &rvalueTL = getTypeLowering(rvalue->getType());
emitStoreOfSemanticRValue(*this, loc, rvalue, dest, rvalueTL, isInit);
}
/// Convert a semantic rvalue to a value of storage type.
SILValue SILGenFunction::emitConversionFromSemanticValue(SILLocation loc,
SILValue semanticValue,
SILType storageType) {
auto &destTL = getTypeLowering(storageType);
(void)destTL;
// Easy case: the types match.
if (semanticValue->getType() == storageType) {
return semanticValue;
}
auto swiftStorageType = storageType.castTo<ReferenceStorageType>();
switch (swiftStorageType->getOwnership()) {
case ReferenceOwnership::Strong:
llvm_unreachable("strong reference storage type should be impossible");
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: \
llvm_unreachable("address-only types are never loadable");
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
value = B.createCopyValue(loc, value); \
B.emitDestroyValueOperation(loc, semanticValue); \
return value; \
}
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For loadable types, place into a box. */ \
auto type = storageType.castTo<Name##StorageType>(); \
assert(type->isLoadable(ResilienceExpansion::Maximal)); \
(void) type; \
SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
value = B.createCopyValue(loc, value); \
B.emitDestroyValueOperation(loc, semanticValue); \
return value; \
}
#define UNCHECKED_REF_STORAGE(Name, ...) \
case ReferenceOwnership::Name: { \
/* For static reference storage types, place into a box. */ \
SILValue value = B.createRefTo##Name(loc, semanticValue, storageType); \
B.emitDestroyValueOperation(loc, semanticValue); \
return value; \
}
#include "swift/AST/ReferenceStorage.def"
}
llvm_unreachable("impossible");
}
static void emitTsanInoutAccess(SILGenFunction &SGF, SILLocation loc,
ManagedValue address) {
assert(address.getType().isAddress());
SILValue accessFnArgs[] = {address.getValue()};
SGF.B.createBuiltin(loc, SGF.getASTContext().getIdentifier("tsanInoutAccess"),
SGF.SGM.Types.getEmptyTupleType(), {}, accessFnArgs);
}
/// Produce a physical address that corresponds to the given l-value
/// component.
static ManagedValue drillIntoComponent(SILGenFunction &SGF,
SILLocation loc,
PathComponent &&component,
ManagedValue base,
TSanKind tsanKind) {
bool isRValue = component.isRValue();
ManagedValue addr = std::move(component).project(SGF, loc, base);
if (!SGF.getASTContext().LangOpts.DisableTsanInoutInstrumentation &&
(SGF.getModule().getOptions().Sanitizers & SanitizerKind::Thread) &&
tsanKind == TSanKind::InoutAccess && !isRValue) {
emitTsanInoutAccess(SGF, loc, addr);
}
return addr;
}
/// Find the last component of the given lvalue and derive a base
/// location for it.
static PathComponent &&
drillToLastComponent(SILGenFunction &SGF,
SILLocation loc,
LValue &&lv,
ManagedValue &addr,
TSanKind tsanKind = TSanKind::None) {
assert(lv.begin() != lv.end() &&
"lvalue must have at least one component");
for (auto i = lv.begin(), e = lv.end() - 1; i != e; ++i) {
addr = drillIntoComponent(SGF, loc, std::move(**i), addr, tsanKind);
}
return std::move(**(lv.end() - 1));
}
static ArgumentSource emitBaseValueForAccessor(SILGenFunction &SGF,
SILLocation loc, LValue &&lvalue,
CanType baseFormalType,
SILDeclRef accessor) {
ManagedValue base;
PathComponent &&component =
drillToLastComponent(SGF, loc, std::move(lvalue), base);
base = drillIntoComponent(SGF, loc, std::move(component), base,
TSanKind::None);
return SGF.prepareAccessorBaseArg(loc, base, baseFormalType, accessor);
}
RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src,
SGFContext C, bool isBaseGuaranteed) {
assert(isReadAccess(src.getAccessKind()));
// Any writebacks should be scoped to after the load.
FormalEvaluationScope scope(*this);
// We shouldn't need to re-abstract here, but we might have to bridge.
// This should only happen if we have a global variable of NSString type.
auto origFormalType = src.getOrigFormalType();
auto substFormalType = src.getSubstFormalType();
auto &rvalueTL = getTypeLowering(src.getTypeOfRValue());
ManagedValue addr;
PathComponent &&component =
drillToLastComponent(*this, loc, std::move(src), addr);
// If the last component is physical, drill down and load from it.
if (component.isPhysical()) {
auto projection = std::move(component).project(*this, loc, addr);
if (projection.getType().isAddress()) {
projection = emitLoad(loc, projection.getValue(),
origFormalType, substFormalType,
rvalueTL, C, IsNotTake,
isBaseGuaranteed);
} else if (isReadAccessResultOwned(src.getAccessKind()) &&
!projection.isPlusOne(*this)) {
projection = projection.copy(*this, loc);
}
return RValue(*this, loc, substFormalType, projection);
}
// If the last component is logical, emit a get.
return std::move(component.asLogical()).get(*this, loc, addr, C);
}
static AbstractionPattern
getFormalStorageAbstractionPattern(SILGenFunction &SGF, AbstractStorageDecl *field) {
if (auto var = dyn_cast<VarDecl>(field)) {
auto origType = SGF.SGM.Types.getAbstractionPattern(var);
return origType.getReferenceStorageReferentType();
}
auto sub = cast<SubscriptDecl>(field);
return SGF.SGM.Types.getAbstractionPattern(sub);
}
/// Produce a singular RValue for a load from the specified property. This is
/// designed to work with RValue ManagedValue bases that are either +0 or +1.
RValue SILGenFunction::emitRValueForStorageLoad(
SILLocation loc, ManagedValue base, CanType baseFormalType,
bool isSuper, AbstractStorageDecl *storage,
PreparedArguments &&subscriptIndices,
SubstitutionMap substitutions,
AccessSemantics semantics, Type propTy, SGFContext C,
bool isBaseGuaranteed) {
AccessStrategy strategy =
storage->getAccessStrategy(semantics, AccessKind::Read,
SGM.M.getSwiftModule(),
F.getResilienceExpansion());
// If we should call an accessor of some kind, do so.
if (strategy.getKind() != AccessStrategy::Storage) {
auto accessKind = SGFAccessKind::OwnedObjectRead;
LValue lv = [&] {
if (!base) return LValue();
auto baseAccess = getBaseAccessKind(SGM, storage, accessKind,
strategy, baseFormalType);
return LValue::forValue(baseAccess, base, baseFormalType);
}();
lv.addMemberComponent(*this, loc, storage, substitutions, LValueOptions(),
isSuper, accessKind, strategy,
propTy->getCanonicalType(),
std::move(subscriptIndices),
/*index for diagnostics*/ nullptr);
return emitLoadOfLValue(loc, std::move(lv), C, isBaseGuaranteed);
}
assert(isa<VarDecl>(storage) && "only properties should have storage");
auto field = cast<VarDecl>(storage);
assert(field->hasStorage() &&
"Cannot directly access value without storage");
// For static variables, emit a reference to the global variable backing
// them.
// FIXME: This has to be dynamically looked up for classes, and
// dynamically instantiated for generics.
if (field->isStatic()) {
auto baseMeta = base.getType().castTo<MetatypeType>().getInstanceType();
(void)baseMeta;
assert(!baseMeta->is<BoundGenericType>() &&
"generic static stored properties not implemented");
if (field->getDeclContext()->getSelfClassDecl() &&
field->hasStorage())
// FIXME: don't need to check hasStorage, already done above
assert(field->isFinal() && "non-final class stored properties not implemented");
return emitRValueForDecl(loc, field, propTy, semantics, C);
}
// rvalue MemberRefExprs are produced in two cases: when accessing a 'let'
// decl member, and when the base is a (non-lvalue) struct.
assert(baseFormalType->getAnyNominal() &&
base.getType().getASTType()->getAnyNominal() &&
"The base of an rvalue MemberRefExpr should be an rvalue value");
// If the accessed field is stored, emit a StructExtract on the base.
auto substFormalType = propTy->getCanonicalType();
auto &lowering = getTypeLowering(substFormalType);
// Check for an abstraction difference.
AbstractionPattern origFormalType =
getFormalStorageAbstractionPattern(*this, field);
bool hasAbstractionChange = false;
auto &abstractedTL = getTypeLowering(origFormalType, substFormalType);
if (!origFormalType.isExactType(substFormalType)) {
hasAbstractionChange =
(abstractedTL.getLoweredType() != lowering.getLoweredType());
}
// If the base is a reference type, just handle this as loading the lvalue.
ManagedValue result;
if (baseFormalType->hasReferenceSemantics()) {
LValue LV = emitPropertyLValue(loc, base, baseFormalType, field,
LValueOptions(),
SGFAccessKind::OwnedObjectRead,
AccessSemantics::DirectToStorage);
auto loaded = emitLoadOfLValue(loc, std::move(LV), C, isBaseGuaranteed);
// If we don't have to reabstract, the load is sufficient.
if (!hasAbstractionChange)
return loaded;
// Otherwise, bring the component up to +1 so we can reabstract it.
result = std::move(loaded).getAsSingleValue(*this, loc)
.copyUnmanaged(*this, loc);
} else if (!base.getType().isAddress()) {
// For non-address-only structs, we emit a struct_extract sequence.
result = B.createStructExtract(loc, base, field);
if (result.getType().is<ReferenceStorageType>()) {
// For weak and unowned types, convert the reference to the right
// pointer, producing a +1.
result = emitConversionToSemanticRValue(loc, result, lowering);
} else if (hasAbstractionChange ||
(!C.isImmediatePlusZeroOk() &&
!(C.isGuaranteedPlusZeroOk() && isBaseGuaranteed))) {
// If we have an abstraction change or if we have to produce a result at
// +1, then copy the value. If we know that our base will stay alive for
// the entire usage of this value, we can borrow the value at +0 for a
// guaranteed consumer. Otherwise, since we do not have enough information
// to know if the base's lifetime last's as long as our use of the access,
// we can only emit at +0 for immediate clients.
result = result.copyUnmanaged(*this, loc);
}
} else {
// Create a tiny unenforced access scope around a load from local memory. No
// cleanup is necessary since we directly emit the load here. This will
// probably go away with opaque values.
UnenforcedAccess access;
SILValue accessAddress =
access.beginAccess(*this, loc, base.getValue(), SILAccessKind::Read);
// For address-only sequences, the base is in memory. Emit a
// struct_element_addr to get to the field, and then load the element as an
// rvalue.
SILValue ElementPtr = B.createStructElementAddr(loc, accessAddress, field);
result = emitLoad(loc, ElementPtr, abstractedTL,
hasAbstractionChange ? SGFContext() : C, IsNotTake);
access.endAccess(*this);
}
// If we're accessing this member with an abstraction change, perform that
// now.
if (hasAbstractionChange)
result =
emitOrigToSubstValue(loc, result, origFormalType, substFormalType, C);
return RValue(*this, loc, substFormalType, result);
}
ManagedValue SILGenFunction::emitAddressOfLValue(SILLocation loc,
LValue &&src,
TSanKind tsanKind) {
// Write is only included in this list because of property behaviors.
// It's obviously unreasonable, though.
assert(src.getAccessKind() == SGFAccessKind::IgnoredRead ||
src.getAccessKind() == SGFAccessKind::BorrowedAddressRead ||
src.getAccessKind() == SGFAccessKind::OwnedAddressRead ||
src.getAccessKind() == SGFAccessKind::ReadWrite ||
src.getAccessKind() == SGFAccessKind::Write);
ManagedValue addr;
PathComponent &&component =
drillToLastComponent(*this, loc, std::move(src), addr, tsanKind);
addr = drillIntoComponent(*this, loc, std::move(component), addr, tsanKind);
assert(addr.getType().isAddress() &&
"resolving lvalue did not give an address");
return ManagedValue::forLValue(addr.getValue());
}
ManagedValue SILGenFunction::emitBorrowedLValue(SILLocation loc,
LValue &&src,
TSanKind tsanKind) {
assert(src.getAccessKind() == SGFAccessKind::IgnoredRead ||
src.getAccessKind() == SGFAccessKind::BorrowedAddressRead ||
src.getAccessKind() == SGFAccessKind::BorrowedObjectRead);
bool isIgnored = src.getAccessKind() == SGFAccessKind::IgnoredRead;
ManagedValue base;
PathComponent &&component =
drillToLastComponent(*this, loc, std::move(src), base, tsanKind);
auto value =
drillIntoComponent(*this, loc, std::move(component), base, tsanKind);
// If project() returned an owned value, and the caller cares, borrow it.
if (value.hasCleanup() && !isIgnored)
value = value.formalAccessBorrow(*this, loc);
return value;
}
LValue
SILGenFunction::emitOpenExistentialLValue(SILLocation loc,
LValue &&lv,
CanArchetypeType openedArchetype,
CanType formalRValueType,
SGFAccessKind accessKind) {
assert(!formalRValueType->hasLValueType());
LValueTypeData typeData = {
accessKind, AbstractionPattern::getOpaque(), formalRValueType,
getLoweredType(formalRValueType).getASTType()
};
// Open up the existential.
auto rep = lv.getTypeOfRValue()
.getPreferredExistentialRepresentation(SGM.M);
switch (rep) {
case ExistentialRepresentation::Opaque:
case ExistentialRepresentation::Boxed: {
lv.add<OpenOpaqueExistentialComponent>(openedArchetype, typeData);
break;
}
case ExistentialRepresentation::Metatype:
case ExistentialRepresentation::Class: {
lv.add<OpenNonOpaqueExistentialComponent>(openedArchetype, typeData);
break;
}
case ExistentialRepresentation::None:
llvm_unreachable("cannot open non-existential");
}
return std::move(lv);
}
static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc,
ArgumentSource &&src, LValue &&dest) {
// The last component must be a getter/setter.
// TODO: allow reabstraction here, too.
auto &component = **(dest.end() - 1);
if (component.getKind() != PathComponent::GetterSetterKind)
return false;
// We cannot apply the peephole if the l-value includes an
// open-existential component because we need to make sure that
// the opened archetype is available everywhere during emission.
// TODO: should we instead just immediately open the existential
// during emitLValue and simply leave the opened address in the LValue?
// Or is there some reasonable way to detect that this is happening
// and avoid affecting cases where it is not necessary?
for (auto &componentPtr : dest) {
if (componentPtr->isOpenExistential())
return false;
}
auto &setterComponent = static_cast<GetterSetterComponent&>(component);
if (setterComponent.hasPropertyWrapper())
return false;
setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest),
std::move(src));
return true;;
}
void SILGenFunction::emitAssignToLValue(SILLocation loc, RValue &&src,
LValue &&dest) {
emitAssignToLValue(loc, ArgumentSource(loc, std::move(src)), std::move(dest));
}
void SILGenFunction::emitAssignToLValue(SILLocation loc,
ArgumentSource &&src,
LValue &&dest) {
// Enter a FormalEvaluationScope so that formal access to independent LValue
// components do not overlap. Furthermore, use an ArgumentScope to force
// cleanup of materialized LValues immediately, before evaluating the next
// LValue. For example: (x[i], x[j]) = a, b
ArgumentScope argScope(*this, loc);
// If the last component is a getter/setter component, use a special
// generation pattern that allows us to peephole the emission of the RHS.
if (trySetterPeephole(*this, loc, std::move(src), std::move(dest))) {
argScope.pop();
return;
}
// Otherwise, force the RHS now to preserve evaluation order.
auto srcLoc = src.getLocation();
RValue srcValue = std::move(src).getAsRValue(*this);
// Peephole: instead of materializing and then assigning into a
// translation component, untransform the value first.
while (dest.isLastComponentTranslation()) {
srcValue = std::move(dest.getLastTranslationComponent())
.untranslate(*this, loc, std::move(srcValue));
dest.dropLastTranslationComponent();
}
src = ArgumentSource(srcLoc, std::move(srcValue));
// Resolve all components up to the last, keeping track of value-type logical
// properties we need to write back to.
ManagedValue destAddr;
PathComponent &&component =
drillToLastComponent(*this, loc, std::move(dest), destAddr);
// Write to the tail component.
if (component.isPhysical()) {
auto finalDestAddr =
std::move(component).project(*this, loc, destAddr);
assert(finalDestAddr.getType().isAddress());
auto value = std::move(src).getAsRValue(*this).ensurePlusOne(*this, loc);
std::move(value).assignInto(*this, loc, finalDestAddr.getValue());
} else {
std::move(component.asLogical()).set(*this, loc, std::move(src), destAddr);
}
// The writeback scope closing will propagate the value back up through the
// writeback chain.
argScope.pop();
}
void SILGenFunction::emitCopyLValueInto(SILLocation loc, LValue &&src,
Initialization *dest) {
auto skipPeephole = [&]{
auto loaded = emitLoadOfLValue(loc, std::move(src), SGFContext(dest));
if (!loaded.isInContext())
std::move(loaded).forwardInto(*this, loc, dest);
};
// If the source is a physical lvalue, the destination is a single address,
// and there's no semantic conversion necessary, do a copy_addr from the
// lvalue into the destination.
if (!src.isPhysical())
return skipPeephole();
if (!dest->canPerformInPlaceInitialization())
return skipPeephole();
auto destAddr = dest->getAddressForInPlaceInitialization(*this, loc);
assert(src.getTypeOfRValue().getASTType()
== destAddr->getType().getASTType());
auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue();
UnenforcedAccess access;
SILValue accessAddress =
access.beginAccess(*this, loc, destAddr, SILAccessKind::Modify);
B.createCopyAddr(loc, srcAddr, accessAddress, IsNotTake, IsInitialization);
access.endAccess(*this);
dest->finishInitialization(*this);
}
void SILGenFunction::emitAssignLValueToLValue(SILLocation loc, LValue &&src,
LValue &&dest) {
// Only perform the peephole if both operands are physical, there's no
// semantic conversion necessary, and exclusivity enforcement
// is not enabled. The peephole interferes with exclusivity enforcement
// because it causes the formal accesses to the source and destination to
// overlap.
bool peepholeConflict =
!src.isObviouslyNonConflicting(dest, SGFAccessKind::OwnedObjectRead,
SGFAccessKind::Write);
if (peepholeConflict || !src.isPhysical() || !dest.isPhysical()) {
RValue loaded = emitLoadOfLValue(loc, std::move(src), SGFContext());
emitAssignToLValue(loc, std::move(loaded), std::move(dest));
return;
}
auto &rvalueTL = getTypeLowering(src.getTypeOfRValue());
auto srcAddr = emitAddressOfLValue(loc, std::move(src)).getUnmanagedValue();
auto destAddr = emitAddressOfLValue(loc, std::move(dest)).getUnmanagedValue();
if (srcAddr->getType() == destAddr->getType()) {
B.createCopyAddr(loc, srcAddr, destAddr, IsNotTake, IsNotInitialization);
} else {
// If there's a semantic conversion necessary, do a load then assign.
auto loaded = emitLoad(loc, srcAddr, rvalueTL, SGFContext(), IsNotTake);
loaded.assignInto(*this, loc, destAddr);
}
}