Skip to content

Commit 0dd1e36

Browse files
authored
Merge pull request #63607 from gottesmm/pr-07ebd2119844b7d0b1be15f9a72f66f06c7cc9cc
[move-only] Teach the checker how to handle ref_element_addr and global_addr with assignable_but_not_consumable semantics
2 parents c1a50cf + c832b41 commit 0dd1e36

12 files changed

+683
-286
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,25 @@ ERROR(sil_moveonlychecker_exclusivity_violation, none,
748748
ERROR(sil_moveonlychecker_moveonly_field_consumed, none,
749749
"'%0' has a move only field that was consumed before later uses", (StringRef))
750750

751+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_classfield_let, none,
752+
"'%0' was consumed but it is illegal to consume a noncopyable class let field. One can only read from it",
753+
(StringRef))
754+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_classfield_var, none,
755+
"'%0' was consumed but it is illegal to consume a noncopyable class var field. One can only read from it or assign to it",
756+
(StringRef))
757+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_global_var, none,
758+
"'%0' was consumed but it is illegal to consume a noncopyable global var. One can only read from it or assign to it",
759+
(StringRef))
760+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_global_let, none,
761+
"'%0' was consumed but it is illegal to consume a noncopyable global let. One can only read from it",
762+
(StringRef))
763+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_escaping_let, none,
764+
"'%0' was consumed but it is illegal to consume a noncopyable escaping immutable capture. One can only read from it",
765+
(StringRef))
766+
ERROR(sil_moveonlychecker_notconsumable_but_assignable_was_consumed_escaping_var, none,
767+
"'%0' was consumed but it is illegal to consume a noncopyable escaping mutable capture. One can only read from it or assign over it",
768+
(StringRef))
769+
751770
NOTE(sil_moveonlychecker_moveonly_field_consumed_here, none,
752771
"move only field consumed here", ())
753772
NOTE(sil_moveonlychecker_boundary_use, none,

lib/SILGen/SILGenGlobalVariable.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,22 @@ SILGenFunction::emitGlobalVariableRef(SILLocation loc, VarDecl *var,
100100
return ManagedValue::forLValue(addr);
101101
}
102102

103-
// Global variables can be accessed directly with global_addr. Emit this
104-
// instruction into the prolog of the function so we can memoize/CSE it in
105-
// VarLocs.
103+
// Global variables can be accessed directly with global_addr. If we have a
104+
// noncopyable type, just emit the global_addr so each individual access has
105+
// its own base projection. This is important so that the checker can
106+
// distinguish in between different accesses to the same global.
107+
auto *silG = SGM.getSILGlobalVariable(var, NotForDefinition);
108+
if (silG->getLoweredType().isMoveOnly()) {
109+
SILValue addr = B.createGlobalAddr(var, silG);
110+
return ManagedValue::forLValue(addr);
111+
}
112+
113+
// If we have a copyable type, emit this instruction into the prolog of the
114+
// function so we can memoize/CSE it via the VarLocs map.
106115
auto *entryBB = &*getFunction().begin();
107116
SILGenBuilder prologueB(*this, entryBB, entryBB->begin());
108117
prologueB.setTrackingList(B.getTrackingList());
109118

110-
auto *silG = SGM.getSILGlobalVariable(var, NotForDefinition);
111119
SILValue addr = prologueB.createGlobalAddr(var, silG);
112120

113121
VarLocs[var] = SILGenFunction::VarLoc::get(addr);

lib/SILGen/SILGenLValue.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,17 @@ namespace {
788788
SGF.B.createRefElementAddr(loc, base.getUnmanagedValue(),
789789
Field, SubstFieldType);
790790

791+
// If we have a move only type...
792+
if (result->getType().isMoveOnly()) {
793+
auto checkKind =
794+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable;
795+
if (isReadAccess(getAccessKind())) {
796+
// Add a mark_must_check [no_consume_or_assign].
797+
checkKind = MarkMustCheckInst::CheckKind::NoConsumeOrAssign;
798+
}
799+
result = SGF.B.createMarkMustCheckInst(loc, result, checkKind);
800+
}
801+
791802
// Avoid emitting access markers completely for non-accesses or immutable
792803
// declarations. Access marker verification is aware of these cases.
793804
if (!IsNonAccessing && !Field->isLet()) {
@@ -2977,6 +2988,14 @@ void LValue::addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc,
29772988
// The only other case that should get here is a global variable.
29782989
if (!address) {
29792990
address = SGF.emitGlobalVariableRef(Loc, Storage, ActorIso);
2991+
if (address.getType().isMoveOnly()) {
2992+
auto checkKind =
2993+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable;
2994+
if (isReadAccess(AccessKind)) {
2995+
checkKind = MarkMustCheckInst::CheckKind::NoConsumeOrAssign;
2996+
}
2997+
address = SGF.B.createMarkMustCheckInst(Loc, address, checkKind);
2998+
}
29802999
} else {
29813000
assert((!ActorIso || Storage->isTopLevelGlobal()) &&
29823001
"local var should not be actor isolated!");

lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -541,16 +541,19 @@ void UseState::initializeLiveness(
541541
FieldSensitiveMultiDefPrunedLiveRange &liveness) {
542542
// We begin by initializing all of our init uses.
543543
for (auto initInstAndValue : initInsts) {
544+
LLVM_DEBUG(llvm::dbgs() << "Found def: " << *initInstAndValue.first);
544545
liveness.initializeDef(initInstAndValue.first, initInstAndValue.second);
545546
}
546547

547548
// If we have a reinitInstAndValue that we are going to be able to convert
548549
// into a simple init, add it as an init. We are going to consider the rest of
549550
// our reinit uses to be liveness uses.
550551
for (auto reinitInstAndValue : reinitInsts) {
551-
if (isReinitToInitConvertibleInst(reinitInstAndValue.first))
552+
if (isReinitToInitConvertibleInst(reinitInstAndValue.first)) {
553+
LLVM_DEBUG(llvm::dbgs() << "Found def: " << *reinitInstAndValue.first);
552554
liveness.initializeDef(reinitInstAndValue.first,
553555
reinitInstAndValue.second);
556+
}
554557
}
555558

556559
// Then check if our markedValue is from an argument that is in,
@@ -603,6 +606,24 @@ void UseState::initializeLiveness(
603606
}
604607
}
605608

609+
// Check if our address is from a ref_element_addr. In such a case, we treat
610+
// the mark_must_check as the initialization.
611+
if (auto *refEltAddr = dyn_cast<RefElementAddrInst>(address->getOperand())) {
612+
LLVM_DEBUG(llvm::dbgs() << "Found ref_element_addr use... "
613+
"adding mark_must_check as init!\n");
614+
initInsts.insert({address, liveness.getTopLevelSpan()});
615+
liveness.initializeDef(address, liveness.getTopLevelSpan());
616+
}
617+
618+
// Check if our address is from a global_addr. In such a case, we treat the
619+
// mark_must_check as the initialization.
620+
if (auto *globalAddr = dyn_cast<GlobalAddrInst>(address->getOperand())) {
621+
LLVM_DEBUG(llvm::dbgs() << "Found global_addr use... "
622+
"adding mark_must_check as init!\n");
623+
initInsts.insert({address, liveness.getTopLevelSpan()});
624+
liveness.initializeDef(address, liveness.getTopLevelSpan());
625+
}
626+
606627
// Now that we have finished initialization of defs, change our multi-maps
607628
// from their array form to their map form.
608629
liveness.finishedInitializationOfDefs();
@@ -1157,15 +1178,17 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
11571178
// Canonicalize the lifetime of the load [take], load [copy].
11581179
moveChecker.changed |= moveChecker.canonicalizer.canonicalize(li);
11591180

1160-
// If we are asked to perform guaranteed checking, emit an error if we
1161-
// have /any/ consuming uses. This is a case that can always be converted
1162-
// to a load_borrow if we pass the check.
1181+
// If we are asked to perform no_consume_or_assign checking or
1182+
// assignable_but_not_consumable checking, if we found any consumes of our
1183+
// load, then we need to emit an error.
11631184
if (markedValue->getCheckKind() ==
1164-
MarkMustCheckInst::CheckKind::NoConsumeOrAssign) {
1165-
if (!moveChecker.canonicalizer.foundAnyConsumingUses()) {
1185+
MarkMustCheckInst::CheckKind::NoConsumeOrAssign ||
1186+
markedValue->getCheckKind() ==
1187+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable) {
1188+
if (moveChecker.canonicalizer.foundAnyConsumingUses()) {
11661189
LLVM_DEBUG(llvm::dbgs()
11671190
<< "Found mark must check [nocopy] error: " << *user);
1168-
moveChecker.diagnosticEmitter.emitObjectGuaranteedDiagnostic(
1191+
moveChecker.diagnosticEmitter.emitAddressInstLoadedAndConsumed(
11691192
markedValue);
11701193
emittedEarlyDiagnostic = true;
11711194
return true;
@@ -1856,14 +1879,18 @@ void MoveOnlyChecker::rewriteUses(
18561879
// destroy_value and use then to create a new load_borrow scope.
18571880
SILBuilderWithScope builder(li);
18581881
auto *lbi = builder.createLoadBorrow(li->getLoc(), li->getOperand());
1859-
1882+
// We use this auxillary list to avoid iterator invalidation of
1883+
// li->getConsumingUse();
1884+
StackList<DestroyValueInst *> toDelete(lbi->getFunction());
18601885
for (auto *consumeUse : li->getConsumingUses()) {
18611886
auto *dvi = cast<DestroyValueInst>(consumeUse->getUser());
18621887
SILBuilderWithScope destroyBuilder(dvi);
18631888
destroyBuilder.createEndBorrow(dvi->getLoc(), lbi);
1864-
dvi->eraseFromParent();
1889+
toDelete.push_back(dvi);
18651890
changed = true;
18661891
}
1892+
while (!toDelete.empty())
1893+
toDelete.pop_back_val()->eraseFromParent();
18671894

18681895
li->replaceAllUsesWith(lbi);
18691896
li->eraseFromParent();
@@ -1903,7 +1930,7 @@ bool MoveOnlyChecker::performSingleCheck(MarkMustCheckInst *markedAddress) {
19031930
diagnosticEmitter, gatherUsesLiveness);
19041931
SWIFT_DEFER { visitor.clear(); };
19051932
visitor.reset(markedAddress);
1906-
if (!visitAccessPathUses(visitor, accessPath, fn)) {
1933+
if (!visitAccessPathBaseUses(visitor, accessPathWithBase, fn)) {
19071934
LLVM_DEBUG(llvm::dbgs() << "Failed access path visit: " << *markedAddress);
19081935
return false;
19091936
}

0 commit comments

Comments
 (0)