Skip to content

[move-only] A few small changes in preparation for a larger patch #63755

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,8 @@ ERROR(sil_moveonlychecker_not_understand_consumable_and_assignable, none,
ERROR(sil_moveonlychecker_not_understand_moveonly, none,
"Usage of a move only type that the move checker does not know how to "
"check!", ())
ERROR(sil_moveonlychecker_missed_copy, none,
"copy of noncopyable typed value. This is a compiler bug. Please file a bug with a small example of the bug", ())

// move kills copyable values checker diagnostics
ERROR(sil_movekillscopyablevalue_value_consumed_more_than_once, none,
Expand Down
13 changes: 13 additions & 0 deletions include/swift/SIL/DebugUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,13 +556,26 @@ inline DebugVarCarryingInst DebugVarCarryingInst::getFromValue(SILValue value) {
return DebugVarCarryingInst();
}

static_assert(sizeof(DebugVarCarryingInst) == sizeof(VarDeclCarryingInst) &&
alignof(DebugVarCarryingInst) == alignof(VarDeclCarryingInst),
"Expected debug var carrying inst to have the same "
"size/alignment/layout as VarDeclCarryingInst!");

/// Attempt to discover a StringRef varName for the value \p value based only
/// off of debug var information. If we fail, we return the name "unknown".
inline StringRef getDebugVarName(SILValue value) {
auto inst = DebugVarCarryingInst::getFromValue(value);
return DebugVarCarryingInst::getName(inst);
}

inline StringRef getDiagnosticName(SILValue value) {
if (auto inst = DebugVarCarryingInst::getFromValue(value))
return inst.getName();
if (auto inst = VarDeclCarryingInst::getFromValue(value))
return inst.getName();
return "unknown";
}

} // end namespace swift

#endif // SWIFT_SIL_DEBUGUTILS_H
2 changes: 2 additions & 0 deletions include/swift/SIL/MemAccessUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,8 @@ struct AccessPathWithBase {
return AccessBase(base, accessPath.getStorage().getKind());
}

bool isValid() const { return base && accessPath.isValid(); }

bool operator==(AccessPathWithBase other) const {
return accessPath == other.accessPath && base == other.base;
}
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,16 @@ class SILBuilder {
ElementTypes, ElementCountOperands));
}

/// Helper function that calls \p createAllocBox after constructing a
/// SILBoxType for \p fieldType.
AllocBoxInst *createAllocBox(SILLocation loc, SILType fieldType,
Optional<SILDebugVariable> Var = None,
bool hasDynamicLifetime = false,
bool reflection = false) {
return createAllocBox(loc, SILBoxType::get(fieldType.getASTType()), Var,
hasDynamicLifetime, reflection);
}

AllocBoxInst *createAllocBox(SILLocation Loc, CanSILBoxType BoxType,
Optional<SILDebugVariable> Var = None,
bool hasDynamicLifetime = false,
Expand Down
13 changes: 12 additions & 1 deletion include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ class SILType {
///
/// \p field Return the type of the ith field of the box. Default set to 0
/// since we only support one field today. This is just future proofing.
SILType getSILBoxFieldType(const SILFunction *f, unsigned field = 0);
SILType getSILBoxFieldType(const SILFunction *f, unsigned field = 0) const;

/// Returns the hash code for the SILType.
llvm::hash_code getHashCode() const {
Expand All @@ -708,6 +708,17 @@ class SILType {
SILType getSingletonAggregateFieldType(SILModule &M,
ResilienceExpansion expansion) const;

/// \returns true if this is a SILBoxType containing a noncopyable type.
bool isBoxedNonCopyableType(const SILFunction *fn) const {
if (!this->is<SILBoxType>())
return false;
return getSILBoxFieldType(fn).isMoveOnly();
}

bool isBoxedNonCopyableType(const SILFunction &fn) const {
return isBoxedNonCopyableType(&fn);
}

//
// Accessors for types used in SIL instructions:
//
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ struct SILConstantInfo {
enum class CaptureKind {
/// A local value captured as a mutable box.
Box,
/// A local value captured as an immutable box.
ImmutableBox,
/// A local value captured as a single pointer to storage (formed with
/// @noescape closures).
StorageAddress,
Expand All @@ -674,7 +676,6 @@ enum class CaptureKind {
Immutable
};


/// TypeConverter - helper class for creating and managing TypeLowerings.
class TypeConverter {
friend class TypeLowering;
Expand Down
15 changes: 15 additions & 0 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,21 @@ lowerCaptureContextParameters(TypeConverter &TC, SILDeclRef function,
inputs.push_back(param);
break;
}
case CaptureKind::ImmutableBox: {
// The type in the box is lowered in the minimal context.
auto minimalLoweredTy =
TC.getTypeLowering(AbstractionPattern(genericSig, canType), canType,
TypeExpansionContext::minimal())
.getLoweredType();
// Lvalues are captured as a box that owns the captured value.
auto boxTy =
TC.getInterfaceBoxTypeForCapture(VD, minimalLoweredTy.getASTType(),
/*mutable*/ false);
auto convention = ParameterConvention::Direct_Guaranteed;
auto param = SILParameterInfo(boxTy, convention);
inputs.push_back(param);
break;
}
case CaptureKind::StorageAddress: {
// Non-escaping lvalues are captured as the address of the value.
SILType ty = loweredTy.getAddressType();
Expand Down
2 changes: 1 addition & 1 deletion lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ bool SILType::isEffectivelyExhaustiveEnumType(SILFunction *f) {
f->getResilienceExpansion());
}

SILType SILType::getSILBoxFieldType(const SILFunction *f, unsigned field) {
SILType SILType::getSILBoxFieldType(const SILFunction *f, unsigned field) const {
auto *boxTy = getASTType()->getAs<SILBoxType>();
if (!boxTy)
return SILType();
Expand Down
3 changes: 2 additions & 1 deletion lib/SIL/Utils/OwnershipUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,8 @@ swift::findTransitiveUsesForAddress(SILValue projectedAddress,
isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user) ||
isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user) ||
isa<IsUniqueInst>(user) || isa<ValueMetatypeInst>(user) ||
isa<DebugValueInst>(user) || isa<EndBorrowInst>(user)) {
isa<DebugValueInst>(user) || isa<EndBorrowInst>(user) ||
isa<ExplicitCopyAddrInst>(user)) {
leafUse(op);
continue;
}
Expand Down
14 changes: 14 additions & 0 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,15 @@ struct ImmutableAddressUseVerifier {
return false;
}

bool isConsumingOrMutatingExplicitCopyAddrUse(Operand *use) {
auto *copyAddr = cast<ExplicitCopyAddrInst>(use->getUser());
if (copyAddr->getDest() == use->get())
return true;
if (copyAddr->getSrc() == use->get() && copyAddr->isTakeOfSrc() == IsTake)
return true;
return false;
}

bool isAddrCastToNonConsuming(SingleValueInstruction *i) {
// Check if any of our uses are consuming. If none of them are consuming, we
// are good to go.
Expand Down Expand Up @@ -604,6 +613,11 @@ struct ImmutableAddressUseVerifier {
// mutation can happen. The checker will prove eventually that we can
// convert it to a copy_addr [take] [init].
break;
case SILInstructionKind::ExplicitCopyAddrInst:
if (isConsumingOrMutatingExplicitCopyAddrUse(use))
return true;
else
break;
case SILInstructionKind::CopyAddrInst:
if (isConsumingOrMutatingCopyAddrUse(use))
return true;
Expand Down
5 changes: 5 additions & 0 deletions lib/SILGen/ManagedValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ class ManagedValue {
return bool(getValue()) || valueAndFlag.getInt();
}

SILFunction *getFunction() const {
assert(getValue());
return getValue()->getFunction();
}

void dump() const;
void dump(raw_ostream &os, unsigned indent = 0) const;
void print(raw_ostream &os) const;
Expand Down
7 changes: 7 additions & 0 deletions lib/SILGen/SILGenBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,3 +1002,10 @@ ManagedValue SILGenBuilder::emitCopyValueOperation(SILLocation loc,
return value;
return SGF.emitManagedRValueWithCleanup(cvi);
}

void SILGenBuilder::emitCopyAddrOperation(SILLocation loc, SILValue srcAddr,
SILValue destAddr, IsTake_t isTake,
IsInitialization_t isInitialize) {
auto &lowering = getTypeLowering(srcAddr->getType());
lowering.emitCopyInto(*this, loc, srcAddr, destAddr, isTake, isInitialize);
}
4 changes: 4 additions & 0 deletions lib/SILGen/SILGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ class SILGenBuilder : public SILBuilder {

using SILBuilder::emitCopyValueOperation;
ManagedValue emitCopyValueOperation(SILLocation Loc, ManagedValue v);

void emitCopyAddrOperation(SILLocation loc, SILValue srcAddr,
SILValue destAddr, IsTake_t isTake,
IsInitialization_t isInitialize);
};

} // namespace Lowering
Expand Down
58 changes: 56 additions & 2 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ void SILGenFunction::emitCaptures(SILLocation loc,
Diags.diagnose(capture.getLoc(), diag::value_captured_here);

// Emit an 'undef' of the correct type.
switch (SGM.Types.getDeclCaptureKind(capture, expansion)) {
auto captureKind = SGM.Types.getDeclCaptureKind(capture, expansion);
switch (captureKind) {
case CaptureKind::Constant:
capturedArgs.push_back(emitUndef(getLoweredType(type)));
break;
Expand All @@ -338,13 +339,15 @@ void SILGenFunction::emitCaptures(SILLocation loc,
capturedArgs.push_back(emitUndef(ty));
break;
}
case CaptureKind::ImmutableBox:
case CaptureKind::Box: {
bool isMutable = captureKind == CaptureKind::Box;
auto boxTy = SGM.Types.getContextBoxTypeForCapture(
vd,
SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
type),
FunctionDC->getGenericEnvironmentOfContext(),
/*mutable*/ true);
/*mutable*/ isMutable);
capturedArgs.push_back(emitUndef(boxTy));
break;
}
Expand Down Expand Up @@ -497,6 +500,57 @@ void SILGenFunction::emitCaptures(SILLocation loc,

break;
}
case CaptureKind::ImmutableBox: {
auto entryValue = getAddressValue(Entry.value);
// LValues are captured as both the box owning the value and the
// address of the value.
assert(entryValue->getType().isAddress() &&
"no address for captured var!");
// Boxes of opaque return values stay opaque.
auto minimalLoweredType = SGM.Types.getLoweredRValueType(
TypeExpansionContext::minimal(), type->getCanonicalType());
// If this is a boxed variable, we can use it directly.
if (Entry.box &&
entryValue->getType().getASTType() == minimalLoweredType) {
// We can guarantee our own box to the callee.
if (canGuarantee) {
capturedArgs.push_back(
ManagedValue::forUnmanaged(Entry.box).borrow(*this, loc));
} else {
capturedArgs.push_back(emitManagedRetain(loc, Entry.box));
}
if (captureCanEscape)
escapesToMark.push_back(entryValue);
} else {
// Address only 'let' values are passed by box. This isn't great, in
// that a variable captured by multiple closures will be boxed for each
// one. This could be improved by doing an "isCaptured" analysis when
// emitting address-only let constants, and emit them into an alloc_box
// like a variable instead of into an alloc_stack.
//
// TODO: This might not be profitable anymore with guaranteed captures,
// since we could conceivably forward the copied value into the
// closure context and pass it down to the partially applied function
// in-place.
// TODO: Use immutable box for immutable captures.
auto boxTy = SGM.Types.getContextBoxTypeForCapture(
vd, minimalLoweredType,
FunctionDC->getGenericEnvironmentOfContext(),
/*mutable*/ false);

AllocBoxInst *allocBox = B.createAllocBox(loc, boxTy);
ProjectBoxInst *boxAddress = B.createProjectBox(loc, allocBox, 0);
B.createCopyAddr(loc, entryValue, boxAddress, IsNotTake,
IsInitialization);
if (canGuarantee)
capturedArgs.push_back(
emitManagedRValueWithCleanup(allocBox).borrow(*this, loc));
else
capturedArgs.push_back(emitManagedRValueWithCleanup(allocBox));
}

break;
}
}
}

Expand Down
18 changes: 13 additions & 5 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,8 @@ static void emitCaptureArguments(SILGenFunction &SGF,
};

auto expansion = SGF.getTypeExpansionContext();
switch (SGF.SGM.Types.getDeclCaptureKind(capture, expansion)) {
auto captureKind = SGF.SGM.Types.getDeclCaptureKind(capture, expansion);
switch (captureKind) {
case CaptureKind::Constant: {
auto type = getVarTypeInCaptureContext();
auto &lowering = SGF.getTypeLowering(type);
Expand Down Expand Up @@ -569,24 +570,31 @@ static void emitCaptureArguments(SILGenFunction &SGF,
break;
}

case CaptureKind::ImmutableBox:
case CaptureKind::Box: {
// LValues are captured as a retained @box that owns
// the captured value.
auto type = getVarTypeInCaptureContext();
// Get the content for the box in the minimal resilience domain because we
// are declaring a type.
bool isMutable = captureKind != CaptureKind::ImmutableBox;
auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(
VD,
SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
type),
SGF.F.getGenericEnvironment(), /*mutable*/ true);
SGF.F.getGenericEnvironment(), /*mutable*/ isMutable);
auto *box = SGF.F.begin()->createFunctionArgument(
SILType::getPrimitiveObjectType(boxTy), VD);
box->setClosureCapture(true);
SILValue addr = SGF.B.createProjectBox(VD, box, 0);
if (addr->getType().isMoveOnly())
addr = SGF.B.createMarkMustCheckInst(
VD, addr, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
if (addr->getType().isMoveOnly()) {
if (isMutable)
addr = SGF.B.createMarkMustCheckInst(
VD, addr, MarkMustCheckInst::CheckKind::ConsumableAndAssignable);
else
addr = SGF.B.createMarkMustCheckInst(
VD, addr, MarkMustCheckInst::CheckKind::NoConsumeOrAssign);
}
SGF.VarLocs[VD] = SILGenFunction::VarLoc::get(addr, box);
SILDebugVariable DbgVar(VD->isLet(), ArgNo);
SGF.B.createDebugValueAddr(Loc, addr, DbgVar);
Expand Down
Loading