Skip to content

Commit b33535c

Browse files
committed
[move-only] Add a new CheckKind::AssignableButNotConsumable.
This is used to model global_addr/ref_element_addr/escaping closure captures where we do not want to allow the user to consume the memory (leaving the memory in a potentially uninitialized state), but we do want to allow for the user to assign over the memory all at once. Consider a situation like the following: ``` @_moveOnly struct FileDescriptor { var state: CInt = ... } final class Klass { var descriptor = ... } func consumeDescriptor(_ x: __owned FileDescriptor) {} // Eventually both of these will point at the same class. var globalKlass = ... var globalKlass2 = ... func memoryUnsafe() { consumeDescriptor(globalKlass.descriptor) consumeDescriptor(globalKlass2.descriptor) } func callMemoryUnsafe() { globalKlass2 = globalKlass() memoryUnsafe() } ``` Notice how in the above in memoryUnsafe, locally the compiler has no way of knowing that globalKlass and globalKlass2 actually point at the same class and thus we are attempting to consume the same descriptor twice. This is even allowed by exclusivity. If descriptor was not move only, this would be safe since we would just copy the value when we consume it. But b/c we ar3e using move only, we must take from the memory.
1 parent 85ea8b5 commit b33535c

File tree

3 files changed

+22
-9
lines changed

3 files changed

+22
-9
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8208,18 +8208,26 @@ class MarkMustCheckInst
82088208
enum class CheckKind : unsigned {
82098209
Invalid = 0,
82108210

8211-
// A signal to the move only checker to perform checking that allows for
8212-
// this value to be consumed along its boundary (in the case of let/var
8213-
// semantics) and also written over in the case of var semantics. NOTE: Of
8214-
// course this still implies the value cannot be copied and can be consumed
8215-
// only once along all program paths.
8211+
/// A signal to the move only checker to perform checking that allows for
8212+
/// this value to be consumed along its boundary (in the case of let/var
8213+
/// semantics) and also written over in the case of var semantics. NOTE: Of
8214+
/// course this still implies the value cannot be copied and can be consumed
8215+
/// only once along all program paths.
82168216
ConsumableAndAssignable,
82178217

8218-
// A signal to the move only checker to perform no consume or assign
8219-
// checking. This forces the result of this instruction owned value to never
8220-
// be consumed (for let/var semantics) or assigned over (for var
8221-
// semantics). Of course, we still allow for non-consuming uses.
8218+
/// A signal to the move only checker to perform no consume or assign
8219+
/// checking. This forces the result of this instruction owned value to never
8220+
/// be consumed (for let/var semantics) or assigned over (for var
8221+
/// semantics). Of course, we still allow for non-consuming uses.
82228222
NoConsumeOrAssign,
8223+
8224+
/// A signal to the move checker that the given value cannot be consumed,
8225+
/// but is allowed to be assigned over. This is used for situations like
8226+
/// global_addr/ref_element_addr/closure escape where we do not want to
8227+
/// allow for the user to take the value (leaving the memory in an
8228+
/// uninitialized state), but we are ok with the user assigning a new value,
8229+
/// completely assigning over the value at once.
8230+
AssignableButNotConsumable,
82238231
};
82248232

82258233
private:
@@ -8244,6 +8252,7 @@ class MarkMustCheckInst
82448252
return false;
82458253
case CheckKind::ConsumableAndAssignable:
82468254
case CheckKind::NoConsumeOrAssign:
8255+
case CheckKind::AssignableButNotConsumable:
82478256
return true;
82488257
}
82498258
}

lib/SIL/IR/SILPrinter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,9 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
19981998
case CheckKind::NoConsumeOrAssign:
19991999
*this << "[no_consume_or_assign] ";
20002000
break;
2001+
case CheckKind::AssignableButNotConsumable:
2002+
*this << "[assignable_but_not_consumable] ";
2003+
break;
20012004
}
20022005
*this << getIDAndType(I->getOperand());
20032006
}

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3666,6 +3666,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
36663666
.Case("consumable_and_assignable",
36673667
CheckKind::ConsumableAndAssignable)
36683668
.Case("no_consume_or_assign", CheckKind::NoConsumeOrAssign)
3669+
.Case("assignable_but_not_consumable", CheckKind::AssignableButNotConsumable)
36693670
.Default(CheckKind::Invalid);
36703671

36713672
if (CKind == CheckKind::Invalid) {

0 commit comments

Comments
 (0)