Skip to content

Commit 4ea2440

Browse files
committed
[NFC] MoveChecker: Merged partial mutation checks.
Simplified diagnostic emission to use a single code path.
1 parent 5f3d293 commit 4ea2440

File tree

4 files changed

+195
-219
lines changed

4 files changed

+195
-219
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 41 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,28 +1615,18 @@ struct CopiedLoadBorrowEliminationVisitor
16151615
// MARK: Partial Consume/Reinit Checking
16161616
//===----------------------------------------------------------------------===//
16171617

1618-
namespace {
1619-
1620-
/// When partial consumption is enabled, we only allow for destructure through
1621-
/// deinits. When partial consumption is disabled, we error on /all/ partial
1622-
/// consumption.
1623-
enum class IsPartialConsumeOrReinit_t {
1624-
IsPartialConsume,
1625-
IsPartialReinit,
1626-
};
1627-
1628-
} // namespace
1629-
1630-
static std::pair<SILType, NominalTypeDecl *>
1631-
shouldEmitPartialError(UseState &useState, SILInstruction *user,
1632-
SILType useType, TypeTreeLeafTypeRange usedBits) {
1618+
/// Whether an error should be emitted in response to a partial consumption.
1619+
static llvm::Optional<PartialMutationError>
1620+
shouldEmitPartialMutationError(UseState &useState, SILInstruction *user,
1621+
SILType useType,
1622+
TypeTreeLeafTypeRange usedBits) {
16331623
SILFunction *fn = useState.getFunction();
16341624

16351625
// We walk down from our ancestor to our projection, emitting an error if
16361626
// any of our types have a deinit.
16371627
auto iterType = useState.address->getType();
16381628
if (iterType.isMoveOnlyWrapped())
1639-
return {SILType(), nullptr};
1629+
return {};
16401630

16411631
TypeOffsetSizePair pair(usedBits);
16421632
auto targetType = useType;
@@ -1652,13 +1642,15 @@ shouldEmitPartialError(UseState &useState, SILInstruction *user,
16521642
if (iterType == targetType) {
16531643
LLVM_DEBUG(llvm::dbgs() << " IterType is TargetType! Exiting early "
16541644
"without emitting error!\n");
1655-
return {SILType(), nullptr};
1645+
return {};
16561646
}
16571647

16581648
// Emit the error.
1659-
return {iterType, nullptr};
1649+
return {PartialMutationError::featureDisabled(iterType)};
16601650
}
16611651

1652+
LLVM_DEBUG(llvm::dbgs() << " MoveOnlyPartialConsumption enabled!\n");
1653+
16621654
// Otherwise, walk the type looking for the deinit.
16631655
while (iterType != targetType) {
16641656
// If we have a nominal type as our parent type, see if it has a
@@ -1671,7 +1663,7 @@ shouldEmitPartialError(UseState &useState, SILInstruction *user,
16711663
// through the deinit. Emit a nice error saying what it is. Since we
16721664
// are emitting an error, we do a bit more work and construct the
16731665
// actual projection string.
1674-
return {iterType, nom};
1666+
return {PartialMutationError::hasDeinit(iterType, *nom)};
16751667
}
16761668
}
16771669

@@ -1681,103 +1673,24 @@ shouldEmitPartialError(UseState &useState, SILInstruction *user,
16811673
*pair.walkOneLevelTowardsChild(iterPair, iterType, fn);
16821674
}
16831675

1684-
return {SILType(), nullptr};
1685-
}
1686-
1687-
static void
1688-
checkForPartialConsume(UseState &useState, DiagnosticEmitter &diagnosticEmitter,
1689-
SILInstruction *user, SILType useType,
1690-
TypeTreeLeafTypeRange usedBits,
1691-
IsPartialConsumeOrReinit_t isPartialConsumeOrReinit) {
1692-
SILFunction *fn = useState.getFunction();
1693-
1694-
// We walk down from our ancestor to our projection, emitting an error if
1695-
// any of our types have a deinit.
1696-
TypeOffsetSizePair pair(usedBits);
1697-
SILType errorIterType;
1698-
NominalTypeDecl *nom;
1699-
std::tie(errorIterType, nom) =
1700-
shouldEmitPartialError(useState, user, useType, usedBits);
1701-
if (!errorIterType)
1702-
return;
1703-
1704-
if (!fn->getModule().getASTContext().LangOpts.hasFeature(
1705-
Feature::MoveOnlyPartialConsumption)) {
1706-
// Otherwise, build up the path string and emit the error.
1707-
SmallString<128> pathString;
1708-
auto rootType = useState.address->getType();
1709-
if (errorIterType != rootType) {
1710-
llvm::raw_svector_ostream os(pathString);
1711-
pair.constructPathString(errorIterType, {rootType, fn}, rootType, fn, os);
1712-
}
1713-
1714-
diagnosticEmitter.emitCannotPartiallyConsumeError(
1715-
useState.address, pathString, nullptr /*nominal*/, user,
1716-
false /*deinit only*/);
1717-
return;
1718-
}
1719-
1720-
LLVM_DEBUG(llvm::dbgs() << " MoveOnlyPartialConsumption enabled!\n");
1721-
1722-
SmallString<128> pathString;
1723-
auto rootType = useState.address->getType();
1724-
if (errorIterType != rootType) {
1725-
llvm::raw_svector_ostream os(pathString);
1726-
pair.constructPathString(errorIterType, {rootType, fn}, rootType, fn, os);
1727-
}
1728-
1729-
diagnosticEmitter.emitCannotPartiallyConsumeError(
1730-
useState.address, pathString, nom, user, true /*deinit only*/);
1731-
}
1732-
1733-
static void
1734-
checkForPartialConsume(UseState &useState, DiagnosticEmitter &diagnosticEmitter,
1735-
Operand *op, TypeTreeLeafTypeRange usedBits,
1736-
IsPartialConsumeOrReinit_t isPartialConsumeOrReinit) {
1737-
return checkForPartialConsume(useState, diagnosticEmitter, op->getUser(),
1738-
op->get()->getType(), usedBits,
1739-
isPartialConsumeOrReinit);
1676+
return {};
17401677
}
17411678

1742-
static void diagnosePartialReinitError(UseState &useState,
1743-
DiagnosticEmitter &diagnosticEmitter,
1744-
SILInstruction *user, SILType errorType,
1745-
NominalTypeDecl *nom,
1746-
SILInstruction *earlierConsumingUse,
1747-
TypeTreeLeafTypeRange usedBits) {
1748-
SILFunction *fn = useState.getFunction();
1749-
1679+
static bool checkForPartialMutation(UseState &useState,
1680+
DiagnosticEmitter &diagnosticEmitter,
1681+
SILInstruction *user, SILType useType,
1682+
TypeTreeLeafTypeRange usedBits,
1683+
PartialMutation partialMutateKind) {
17501684
// We walk down from our ancestor to our projection, emitting an error if
17511685
// any of our types have a deinit.
1752-
TypeOffsetSizePair pair(usedBits);
1753-
if (!fn->getModule().getASTContext().LangOpts.hasFeature(
1754-
Feature::MoveOnlyPartialConsumption)) {
1755-
// Otherwise, build up the path string and emit the error.
1756-
SmallString<128> pathString;
1757-
auto rootType = useState.address->getType();
1758-
if (errorType != rootType) {
1759-
llvm::raw_svector_ostream os(pathString);
1760-
pair.constructPathString(errorType, {rootType, fn}, rootType, fn, os);
1761-
}
1762-
1763-
diagnosticEmitter.emitCannotPartiallyReinitError(
1764-
useState.address, pathString, nullptr /*nominal*/, user,
1765-
earlierConsumingUse, false /*deinit only*/);
1766-
return;
1767-
}
1768-
1769-
LLVM_DEBUG(llvm::dbgs() << " MoveOnlyPartialConsumption enabled!\n");
1770-
1771-
SmallString<128> pathString;
1772-
auto rootType = useState.address->getType();
1773-
if (errorType != rootType) {
1774-
llvm::raw_svector_ostream os(pathString);
1775-
pair.constructPathString(errorType, {rootType, fn}, rootType, fn, os);
1776-
}
1686+
auto error =
1687+
shouldEmitPartialMutationError(useState, user, useType, usedBits);
1688+
if (!error)
1689+
return false;
17771690

1778-
diagnosticEmitter.emitCannotPartiallyReinitError(
1779-
useState.address, pathString, nom, user, earlierConsumingUse,
1780-
true /*deinit only*/);
1691+
diagnosticEmitter.emitCannotPartiallyMutateError(
1692+
useState.address, error.value(), user, usedBits, partialMutateKind);
1693+
return true;
17811694
}
17821695

17831696
namespace {
@@ -1791,14 +1704,6 @@ struct PartialReinitChecker {
17911704

17921705
void
17931706
performPartialReinitChecking(FieldSensitiveMultiDefPrunedLiveRange &liveness);
1794-
1795-
private:
1796-
void checkForPartialConsumeOrInitError(
1797-
SILInstruction *user, SILType useType, TypeTreeLeafTypeRange usedBits,
1798-
IsPartialConsumeOrReinit_t isPartialConsumeOrReinit) {
1799-
::checkForPartialConsume(useState, diagnosticEmitter, user, useType,
1800-
usedBits, isPartialConsumeOrReinit);
1801-
}
18021707
};
18031708

18041709
} // namespace
@@ -1819,18 +1724,10 @@ void PartialReinitChecker::performPartialReinitChecking(
18191724
emittedError = !liveness.findEarlierConsumingUse(
18201725
initToValues.first, index,
18211726
[&](SILInstruction *consumingInst) -> bool {
1822-
SILType errorType;
1823-
NominalTypeDecl *nom;
1824-
std::tie(errorType, nom) = shouldEmitPartialError(
1825-
useState, initToValues.first, value->getType(),
1826-
TypeTreeLeafTypeRange(index, index + 1));
1827-
if (!errorType)
1828-
return true;
1829-
1830-
diagnosePartialReinitError(
1831-
useState, diagnosticEmitter, initToValues.first, errorType,
1832-
nom, consumingInst, TypeTreeLeafTypeRange(index, index + 1));
1833-
return false;
1727+
return !checkForPartialMutation(
1728+
useState, diagnosticEmitter, initToValues.first,
1729+
value->getType(), TypeTreeLeafTypeRange(index, index + 1),
1730+
PartialMutation::reinit(*consumingInst));
18341731
});
18351732

18361733
// If we emitted an error for this index break. We only want to emit one
@@ -1862,18 +1759,10 @@ void PartialReinitChecker::performPartialReinitChecking(
18621759
emittedError = !liveness.findEarlierConsumingUse(
18631760
reinitToValues.first, index,
18641761
[&](SILInstruction *consumingInst) -> bool {
1865-
SILType errorType;
1866-
NominalTypeDecl *nom;
1867-
std::tie(errorType, nom) = shouldEmitPartialError(
1868-
useState, reinitToValues.first, value->getType(),
1869-
TypeTreeLeafTypeRange(index, index + 1));
1870-
if (!errorType)
1871-
return true;
1872-
1873-
diagnosePartialReinitError(
1874-
useState, diagnosticEmitter, reinitToValues.first, errorType,
1875-
nom, consumingInst, TypeTreeLeafTypeRange(index, index + 1));
1876-
return false;
1762+
return !checkForPartialMutation(
1763+
useState, diagnosticEmitter, reinitToValues.first,
1764+
value->getType(), TypeTreeLeafTypeRange(index, index + 1),
1765+
PartialMutation::reinit(*consumingInst));
18771766
});
18781767
if (emittedError)
18791768
break;
@@ -2102,8 +1991,9 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
21021991
// If we have a copy_addr, we are either going to have a take or a
21031992
// copy... in either case, this copy_addr /is/ going to be a consuming
21041993
// operation. Make sure to check if we semantically destructure.
2105-
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
2106-
IsPartialConsumeOrReinit_t::IsPartialConsume);
1994+
checkForPartialMutation(useState, diagnosticEmitter, op->getUser(),
1995+
op->get()->getType(), *leafRange,
1996+
PartialMutation::consume());
21071997

21081998
if (copyAddr->isTakeOfSrc()) {
21091999
LLVM_DEBUG(llvm::dbgs() << "Found take: " << *user);
@@ -2296,8 +2186,9 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
22962186
} else {
22972187
// Now that we know that we are going to perform a take, perform a
22982188
// checkForDestructure.
2299-
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
2300-
IsPartialConsumeOrReinit_t::IsPartialConsume);
2189+
checkForPartialMutation(useState, diagnosticEmitter, op->getUser(),
2190+
op->get()->getType(), *leafRange,
2191+
PartialMutation::consume());
23012192

23022193
// If we emitted an error diagnostic, do not transform further and instead
23032194
// mark that we emitted an early diagnostic and return true.
@@ -2352,8 +2243,9 @@ bool GatherUsesVisitor::visitUse(Operand *op) {
23522243
// error.
23532244
unsigned numDiagnostics =
23542245
moveChecker.diagnosticEmitter.getDiagnosticCount();
2355-
checkForPartialConsume(useState, diagnosticEmitter, op, *leafRange,
2356-
IsPartialConsumeOrReinit_t::IsPartialConsume);
2246+
checkForPartialMutation(useState, diagnosticEmitter, op->getUser(),
2247+
op->get()->getType(), *leafRange,
2248+
PartialMutation::consume());
23572249
if (numDiagnostics != moveChecker.diagnosticEmitter.getDiagnosticCount()) {
23582250
LLVM_DEBUG(llvm::dbgs()
23592251
<< "Emitting destructure through deinit error!\n");

0 commit comments

Comments
 (0)