Skip to content

Commit cb4cf0c

Browse files
authored
Merge pull request #39196 from slavapestov/more-gsb-asserts
GSB: Add some more assertions
2 parents a382f58 + 26fa4b3 commit cb4cf0c

File tree

3 files changed

+214
-1
lines changed

3 files changed

+214
-1
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ class GenericSignature {
200200
/// (archetypes) that correspond to the interface types in this generic
201201
/// signature.
202202
GenericEnvironment *getGenericEnvironment() const;
203+
204+
/// Check invariants.
205+
void verify() const;
203206
};
204207

205208
/// A reference to a canonical generic signature.

lib/AST/GenericSignature.cpp

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,4 +1539,181 @@ int swift::compareDependentTypes(Type type1, Type type2) {
15391539
return result;
15401540

15411541
return 0;
1542+
}
1543+
1544+
void GenericSignature::verify() const {
1545+
auto canSig = getCanonicalSignature();
1546+
1547+
PrettyStackTraceGenericSignature debugStack("checking", canSig);
1548+
1549+
auto canonicalRequirements = canSig.getRequirements();
1550+
1551+
// We collect conformance requirements to check that they're minimal.
1552+
llvm::SmallDenseMap<CanType, SmallVector<ProtocolDecl *, 2>, 2> conformances;
1553+
1554+
// Check that the requirements satisfy certain invariants.
1555+
for (unsigned idx : indices(canonicalRequirements)) {
1556+
debugStack.setRequirement(idx);
1557+
1558+
const auto &reqt = canonicalRequirements[idx];
1559+
1560+
// Left-hand side must be a canonical type parameter.
1561+
if (reqt.getKind() != RequirementKind::SameType) {
1562+
if (!reqt.getFirstType()->isTypeParameter()) {
1563+
llvm::errs() << "Left-hand side must be a type parameter: ";
1564+
reqt.dump(llvm::errs());
1565+
llvm::errs() << "\n";
1566+
abort();
1567+
}
1568+
1569+
if (!canSig->isCanonicalTypeInContext(reqt.getFirstType())) {
1570+
llvm::errs() << "Left-hand side is not canonical: ";
1571+
reqt.dump(llvm::errs());
1572+
llvm::errs() << "\n";
1573+
abort();
1574+
}
1575+
}
1576+
1577+
// Check canonicalization of requirement itself.
1578+
switch (reqt.getKind()) {
1579+
case RequirementKind::Superclass:
1580+
if (!canSig->isCanonicalTypeInContext(reqt.getSecondType())) {
1581+
llvm::errs() << "Right-hand side is not canonical: ";
1582+
reqt.dump(llvm::errs());
1583+
llvm::errs() << "\n";
1584+
abort();
1585+
}
1586+
break;
1587+
1588+
case RequirementKind::Layout:
1589+
break;
1590+
1591+
case RequirementKind::SameType: {
1592+
auto isCanonicalAnchor = [&](Type type) {
1593+
if (auto *dmt = type->getAs<DependentMemberType>())
1594+
return canSig->isCanonicalTypeInContext(dmt->getBase());
1595+
return type->is<GenericTypeParamType>();
1596+
};
1597+
1598+
auto firstType = reqt.getFirstType();
1599+
auto secondType = reqt.getSecondType();
1600+
if (!isCanonicalAnchor(firstType)) {
1601+
llvm::errs() << "Left hand side does not have a canonical parent: ";
1602+
reqt.dump(llvm::errs());
1603+
llvm::errs() << "\n";
1604+
abort();
1605+
}
1606+
1607+
if (reqt.getSecondType()->isTypeParameter()) {
1608+
if (!isCanonicalAnchor(secondType)) {
1609+
llvm::errs() << "Right hand side does not have a canonical parent: ";
1610+
reqt.dump(llvm::errs());
1611+
llvm::errs() << "\n";
1612+
abort();
1613+
}
1614+
if (compareDependentTypes(firstType, secondType) >= 0) {
1615+
llvm::errs() << "Out-of-order type parameters: ";
1616+
reqt.dump(llvm::errs());
1617+
llvm::errs() << "\n";
1618+
abort();
1619+
}
1620+
} else {
1621+
if (!canSig->isCanonicalTypeInContext(secondType)) {
1622+
llvm::errs() << "Right hand side is not canonical: ";
1623+
reqt.dump(llvm::errs());
1624+
llvm::errs() << "\n";
1625+
abort();
1626+
}
1627+
}
1628+
break;
1629+
}
1630+
1631+
case RequirementKind::Conformance:
1632+
// Collect all conformance requirements on each type parameter.
1633+
conformances[CanType(reqt.getFirstType())].push_back(
1634+
reqt.getProtocolDecl());
1635+
break;
1636+
}
1637+
1638+
// From here on, we're only interested in requirements beyond the first.
1639+
if (idx == 0) continue;
1640+
1641+
// Make sure that the left-hand sides are in nondecreasing order.
1642+
const auto &prevReqt = canonicalRequirements[idx-1];
1643+
int compareLHS =
1644+
compareDependentTypes(prevReqt.getFirstType(), reqt.getFirstType());
1645+
if (compareLHS > 0) {
1646+
llvm::errs() << "Out-of-order left-hand side: ";
1647+
reqt.dump(llvm::errs());
1648+
llvm::errs() << "\n";
1649+
abort();
1650+
}
1651+
1652+
// If we have two same-type requirements where the left-hand sides differ
1653+
// but fall into the same equivalence class, we can check the form.
1654+
if (compareLHS < 0 && reqt.getKind() == RequirementKind::SameType &&
1655+
prevReqt.getKind() == RequirementKind::SameType &&
1656+
canSig->areSameTypeParameterInContext(prevReqt.getFirstType(),
1657+
reqt.getFirstType())) {
1658+
// If it's a it's a type parameter, make sure the equivalence class is
1659+
// wired together sanely.
1660+
if (prevReqt.getSecondType()->isTypeParameter()) {
1661+
if (!prevReqt.getSecondType()->isEqual(reqt.getFirstType())) {
1662+
llvm::errs() << "Same-type requirement within an equiv. class "
1663+
<< "is out-of-order: ";
1664+
reqt.dump(llvm::errs());
1665+
llvm::errs() << "\n";
1666+
abort();
1667+
}
1668+
} else {
1669+
// Otherwise, the concrete types must match up.
1670+
if (!prevReqt.getSecondType()->isEqual(reqt.getSecondType())) {
1671+
llvm::errs() << "Inconsistent concrete requirement in equiv. class: ";
1672+
reqt.dump(llvm::errs());
1673+
llvm::errs() << "\n";
1674+
abort();
1675+
}
1676+
}
1677+
}
1678+
1679+
// If we have a concrete same-type requirement, we shouldn't have any
1680+
// other requirements on the same type.
1681+
if (reqt.getKind() == RequirementKind::SameType &&
1682+
!reqt.getSecondType()->isTypeParameter()) {
1683+
if (compareLHS >= 0) {
1684+
llvm::errs() << "Concrete subject type should not have "
1685+
<< "any other requirements: ";
1686+
reqt.dump(llvm::errs());
1687+
llvm::errs() << "\n";
1688+
abort();
1689+
}
1690+
}
1691+
1692+
if (prevReqt.compare(reqt) >= 0) {
1693+
llvm::errs() << "Out-of-order requirement: ";
1694+
reqt.dump(llvm::errs());
1695+
llvm::errs() << "\n";
1696+
abort();
1697+
}
1698+
}
1699+
1700+
// Make sure we don't have redundant protocol conformance requirements.
1701+
for (auto pair : conformances) {
1702+
const auto &protos = pair.second;
1703+
auto canonicalProtos = protos;
1704+
1705+
// canonicalizeProtocols() will sort them and filter out any protocols that
1706+
// are refined by other protocols in the list. It should be a no-op at this
1707+
// point.
1708+
ProtocolType::canonicalizeProtocols(canonicalProtos);
1709+
1710+
if (protos.size() != canonicalProtos.size()) {
1711+
llvm::errs() << "Redundant conformance requirements in signature\n";
1712+
abort();
1713+
}
1714+
if (!std::equal(protos.begin(), protos.end(), canonicalProtos.begin())) {
1715+
llvm::errs() << "Out-of-order conformance requirements\n";
1716+
abort();
1717+
}
1718+
}
15421719
}

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7997,6 +7997,9 @@ static void checkGenericSignature(CanGenericSignature canSig,
79977997

79987998
auto canonicalRequirements = canSig.getRequirements();
79997999

8000+
// We collect conformance requirements to check that they're minimal.
8001+
llvm::SmallDenseMap<CanType, SmallVector<ProtocolDecl *, 2>, 2> conformances;
8002+
80008003
// Check that the signature is canonical.
80018004
for (unsigned idx : indices(canonicalRequirements)) {
80028005
debugStack.setRequirement(idx);
@@ -8047,6 +8050,10 @@ static void checkGenericSignature(CanGenericSignature canSig,
80478050
"Left-hand side must be a type parameter");
80488051
assert(isa<ProtocolType>(reqt.getSecondType().getPointer()) &&
80498052
"Right-hand side of conformance isn't a protocol type");
8053+
8054+
// Collect all conformance requirements on each type parameter.
8055+
conformances[CanType(reqt.getFirstType())].push_back(
8056+
reqt.getProtocolDecl());
80508057
break;
80518058
}
80528059

@@ -8089,6 +8096,22 @@ static void checkGenericSignature(CanGenericSignature canSig,
80898096
assert(prevReqt.compare(reqt) < 0 &&
80908097
"Out-of-order requirements");
80918098
}
8099+
8100+
// Make sure we don't have redundant protocol conformance requirements.
8101+
for (auto pair : conformances) {
8102+
const auto &protos = pair.second;
8103+
auto canonicalProtos = protos;
8104+
8105+
// canonicalizeProtocols() will sort them and filter out any protocols that
8106+
// are refined by other protocols in the list. It should be a no-op at this
8107+
// point.
8108+
ProtocolType::canonicalizeProtocols(canonicalProtos);
8109+
8110+
assert(protos.size() == canonicalProtos.size() &&
8111+
"redundant conformance requirements");
8112+
assert(std::equal(protos.begin(), protos.end(), canonicalProtos.begin()) &&
8113+
"out-of-order conformance requirements");
8114+
}
80928115
}
80938116
#endif
80948117

@@ -8382,7 +8405,10 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
83828405
auto sig = GenericSignature::get(getGenericParams(), requirements);
83838406

83848407
#ifndef NDEBUG
8385-
if (!Impl->HadAnyError) {
8408+
bool hadAnyError = Impl->HadAnyError;
8409+
8410+
if (requirementSignatureSelfProto &&
8411+
!hadAnyError) {
83868412
checkGenericSignature(sig.getCanonicalSignature(), *this);
83878413
}
83888414
#endif
@@ -8405,6 +8431,13 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
84058431
// anything more.
84068432
Impl.reset();
84078433

8434+
#ifndef NDEBUG
8435+
if (!requirementSignatureSelfProto &&
8436+
!hadAnyError) {
8437+
sig.verify();
8438+
}
8439+
#endif
8440+
84088441
return sig;
84098442
}
84108443

0 commit comments

Comments
 (0)