Skip to content

Commit 7e582b0

Browse files
committed
[move-only] Split error emission code from testing code.
1 parent 07f9384 commit 7e582b0

File tree

3 files changed

+122
-57
lines changed

3 files changed

+122
-57
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,52 +1498,51 @@ struct CopiedLoadBorrowEliminationVisitor final
14981498
} // namespace
14991499

15001500
//===----------------------------------------------------------------------===//
1501-
// MARK: DestructureThroughDeinit Checking
1501+
// MARK: Partial Consume/Reinit Checking
15021502
//===----------------------------------------------------------------------===//
15031503

1504+
namespace {
1505+
15041506
/// When partial consumption is enabled, we only allow for destructure through
15051507
/// deinits. When partial consumption is disabled, we error on /all/ partial
15061508
/// consumption.
1507-
static void checkForDestructure(MarkMustCheckInst *rootAddress, Operand *use,
1508-
TypeTreeLeafTypeRange usedBits,
1509-
DiagnosticEmitter &diagnosticEmitter) {
1510-
LLVM_DEBUG(llvm::dbgs() << " DestructureNeedingUse: " << *use->getUser());
1509+
enum class IsPartialConsumeOrReinit_t {
1510+
IsPartialConsume,
1511+
IsPartialReinit,
1512+
};
1513+
1514+
} // namespace
15111515

1512-
SILFunction *fn = rootAddress->getFunction();
1516+
static std::pair<SILType, NominalTypeDecl *>
1517+
shouldEmitPartialError(UseState &useState, SILInstruction *user,
1518+
SILType useType, TypeTreeLeafTypeRange usedBits) {
1519+
SILFunction *fn = useState.getFunction();
1520+
1521+
// We walk down from our ancestor to our projection, emitting an error if
1522+
// any of our types have a deinit.
1523+
auto iterType = useState.address->getType();
1524+
if (iterType.isMoveOnlyWrapped())
1525+
return {SILType(), nullptr};
15131526

1514-
// We walk down from our ancestor to our projection, emitting an error if any
1515-
// of our types have a deinit.
15161527
TypeOffsetSizePair pair(usedBits);
1517-
auto targetType = use->get()->getType();
1518-
auto iterType = rootAddress->getType();
1528+
auto targetType = useType;
15191529
TypeOffsetSizePair iterPair(iterType, fn);
15201530

1521-
// If our rootAddress is moveonlywrapped, then we know that it must be
1522-
// copyable under the hood meanign that we copy its fields rather than
1523-
// destructure the fields.
1524-
if (iterType.isMoveOnlyWrapped())
1525-
return;
1531+
LLVM_DEBUG(llvm::dbgs() << " Iter Type: " << iterType << '\n'
1532+
<< " Target Type: " << targetType << '\n');
15261533

1527-
// If we are not allowing for any partial consumption, just emit an error
1528-
// immediately.
1529-
if (!rootAddress->getModule().getASTContext().LangOpts.hasFeature(
1534+
if (!fn->getModule().getASTContext().LangOpts.hasFeature(
15301535
Feature::MoveOnlyPartialConsumption)) {
1536+
LLVM_DEBUG(llvm::dbgs() << " MoveOnlyPartialConsumption disabled!\n");
15311537
// If the types equal, just bail early.
1532-
if (iterType == targetType)
1533-
return;
1534-
1535-
// Otherwise, build up the path string and emit the error.
1536-
SmallString<128> pathString;
1537-
auto rootType = rootAddress->getType();
1538-
if (iterType != rootType) {
1539-
llvm::raw_svector_ostream os(pathString);
1540-
pair.constructPathString(iterType, {rootType, fn}, rootType, fn, os);
1538+
if (iterType == targetType) {
1539+
LLVM_DEBUG(llvm::dbgs() << " IterType is TargetType! Exiting early "
1540+
"without emitting error!\n");
1541+
return {SILType(), nullptr};
15411542
}
15421543

1543-
diagnosticEmitter.emitCannotDestructureNominalError(
1544-
rootAddress, pathString, nullptr /*nominal*/, use->getUser(),
1545-
false /*is for deinit error*/);
1546-
return;
1544+
// Emit the error.
1545+
return {iterType, nullptr};
15471546
}
15481547

15491548
// Otherwise, walk the type looking for the deinit.
@@ -1558,17 +1557,7 @@ static void checkForDestructure(MarkMustCheckInst *rootAddress, Operand *use,
15581557
// through the deinit. Emit a nice error saying what it is. Since we
15591558
// are emitting an error, we do a bit more work and construct the
15601559
// actual projection string.
1561-
SmallString<128> pathString;
1562-
auto rootType = rootAddress->getType();
1563-
if (iterType != rootType) {
1564-
llvm::raw_svector_ostream os(pathString);
1565-
pair.constructPathString(iterType, {rootType, fn}, rootType, fn, os);
1566-
}
1567-
1568-
diagnosticEmitter.emitCannotDestructureNominalError(
1569-
rootAddress, pathString, nom, use->getUser(),
1570-
true /*is for deinit error*/);
1571-
break;
1560+
return {iterType, nom};
15721561
}
15731562
}
15741563

@@ -1577,6 +1566,63 @@ static void checkForDestructure(MarkMustCheckInst *rootAddress, Operand *use,
15771566
std::tie(iterPair, iterType) =
15781567
*pair.walkOneLevelTowardsChild(iterPair, iterType, fn);
15791568
}
1569+
1570+
return {SILType(), nullptr};
1571+
}
1572+
1573+
static void
1574+
checkForPartialConsume(UseState &useState, DiagnosticEmitter &diagnosticEmitter,
1575+
SILInstruction *user, SILType useType,
1576+
TypeTreeLeafTypeRange usedBits,
1577+
IsPartialConsumeOrReinit_t isPartialConsumeOrReinit) {
1578+
SILFunction *fn = useState.getFunction();
1579+
1580+
// We walk down from our ancestor to our projection, emitting an error if
1581+
// any of our types have a deinit.
1582+
TypeOffsetSizePair pair(usedBits);
1583+
SILType errorIterType;
1584+
NominalTypeDecl *nom;
1585+
std::tie(errorIterType, nom) =
1586+
shouldEmitPartialError(useState, user, useType, usedBits);
1587+
if (!errorIterType)
1588+
return;
1589+
1590+
if (!fn->getModule().getASTContext().LangOpts.hasFeature(
1591+
Feature::MoveOnlyPartialConsumption)) {
1592+
// Otherwise, build up the path string and emit the error.
1593+
SmallString<128> pathString;
1594+
auto rootType = useState.address->getType();
1595+
if (errorIterType != rootType) {
1596+
llvm::raw_svector_ostream os(pathString);
1597+
pair.constructPathString(errorIterType, {rootType, fn}, rootType, fn, os);
1598+
}
1599+
1600+
diagnosticEmitter.emitCannotPartiallyConsumeError(
1601+
useState.address, pathString, nullptr /*nominal*/, user,
1602+
false /*deinit only*/);
1603+
return;
1604+
}
1605+
1606+
LLVM_DEBUG(llvm::dbgs() << " MoveOnlyPartialConsumption enabled!\n");
1607+
1608+
SmallString<128> pathString;
1609+
auto rootType = useState.address->getType();
1610+
if (errorIterType != rootType) {
1611+
llvm::raw_svector_ostream os(pathString);
1612+
pair.constructPathString(errorIterType, {rootType, fn}, rootType, fn, os);
1613+
}
1614+
1615+
diagnosticEmitter.emitCannotPartiallyConsumeError(
1616+
useState.address, pathString, nom, user, true /*deinit only*/);
1617+
}
1618+
1619+
static void
1620+
checkForPartialConsume(UseState &useState, DiagnosticEmitter &diagnosticEmitter,
1621+
Operand *op, TypeTreeLeafTypeRange usedBits,
1622+
IsPartialConsumeOrReinit_t isPartialConsumeOrReinit) {
1623+
return checkForPartialConsume(useState, diagnosticEmitter, op->getUser(),
1624+
op->get()->getType(), usedBits,
1625+
isPartialConsumeOrReinit);
15801626
}
15811627

15821628
//===----------------------------------------------------------------------===//
@@ -1778,7 +1824,8 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
17781824
// If we have a copy_addr, we are either going to have a take or a
17791825
// copy... in either case, this copy_addr /is/ going to be a consuming
17801826
// operation. Make sure to check if we semantically destructure.
1781-
checkForDestructure(markedValue, op, *leafRange, diagnosticEmitter);
1827+
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
1828+
IsPartialConsumeOrReinit_t::IsPartialConsume);
17821829

17831830
if (copyAddr->isTakeOfSrc()) {
17841831
LLVM_DEBUG(llvm::dbgs() << "Found take: " << *user);
@@ -1965,7 +2012,8 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
19652012
} else {
19662013
// Now that we know that we are going to perform a take, perform a
19672014
// checkForDestructure.
1968-
checkForDestructure(markedValue, op, *leafRange, diagnosticEmitter);
2015+
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
2016+
IsPartialConsumeOrReinit_t::IsPartialConsume);
19692017

19702018
// If we emitted an error diagnostic, do not transform further and instead
19712019
// mark that we emitted an early diagnostic and return true.
@@ -2020,7 +2068,8 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
20202068
// error.
20212069
unsigned numDiagnostics =
20222070
moveChecker.diagnosticEmitter.getDiagnosticCount();
2023-
checkForDestructure(markedValue, op, *leafRange, diagnosticEmitter);
2071+
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
2072+
IsPartialConsumeOrReinit_t::IsPartialConsume);
20242073
if (numDiagnostics != moveChecker.diagnosticEmitter.getDiagnosticCount()) {
20252074
LLVM_DEBUG(llvm::dbgs()
20262075
<< "Emitting destructure through deinit error!\n");

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -840,9 +840,9 @@ void DiagnosticEmitter::emitPromotedBoxArgumentError(
840840
}
841841
}
842842

843-
void DiagnosticEmitter::emitCannotDestructureDeinitNominalError(
843+
void DiagnosticEmitter::emitCannotPartiallyConsumeError(
844844
MarkMustCheckInst *markedValue, StringRef pathString,
845-
NominalTypeDecl *deinitedNominal, SILInstruction *consumingUser) {
845+
NominalTypeDecl *nominal, SILInstruction *consumingUser, bool isForDeinit) {
846846
auto &astContext = fn->getASTContext();
847847

848848
SmallString<64> varName;
@@ -851,15 +851,30 @@ void DiagnosticEmitter::emitCannotDestructureDeinitNominalError(
851851
if (!pathString.empty())
852852
varName.append(pathString);
853853

854-
diagnose(
855-
astContext, consumingUser,
856-
diag::sil_movechecking_cannot_destructure_has_deinit,
857-
varName);
854+
bool hasPartialConsumption =
855+
astContext.LangOpts.hasFeature(Feature::MoveOnlyPartialConsumption);
856+
(void)hasPartialConsumption;
857+
858+
if (isForDeinit) {
859+
assert(hasPartialConsumption);
860+
diagnose(astContext, consumingUser,
861+
diag::sil_movechecking_cannot_destructure_has_deinit, varName);
862+
863+
} else {
864+
assert(!hasPartialConsumption);
865+
diagnose(astContext, consumingUser,
866+
diag::sil_movechecking_cannot_destructure, varName);
867+
}
868+
858869
registerDiagnosticEmitted(markedValue);
859870

860-
// point to the deinit if we know where it is.
871+
if (!isForDeinit)
872+
return;
873+
874+
// Point to the deinit if we know where it is.
875+
assert(nominal);
861876
if (auto deinitLoc =
862-
deinitedNominal->getValueTypeDestructor()->getLoc(/*SerializedOK=*/false))
877+
nominal->getValueTypeDestructor()->getLoc(/*SerializedOK=*/false))
863878
astContext.Diags.diagnose(deinitLoc, diag::sil_movechecking_deinit_here);
864879
}
865880

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,16 @@ class DiagnosticEmitter {
176176
void emitPromotedBoxArgumentError(MarkMustCheckInst *markedValue,
177177
SILFunctionArgument *arg);
178178

179-
void emitCannotDestructureDeinitNominalError(MarkMustCheckInst *markedValue,
180-
StringRef pathString,
181-
NominalTypeDecl *deinitedNominal,
182-
SILInstruction *consumingUser);
183179
void emitCannotDestructureNominalError(MarkMustCheckInst *markedValue,
184180
StringRef pathString,
185181
NominalTypeDecl *nominal,
186182
SILInstruction *consumingUser,
187183
bool isDueToDeinit);
184+
void emitCannotPartiallyConsumeError(MarkMustCheckInst *markedValue,
185+
StringRef pathString,
186+
NominalTypeDecl *nominal,
187+
SILInstruction *consumingUser,
188+
bool dueToDeinit);
188189

189190
private:
190191
/// Emit diagnostics for the final consuming uses and consuming uses needing

0 commit comments

Comments
 (0)