Skip to content

Commit d9fd6cb

Browse files
authored
Merge pull request #40482 from slavapestov/rqm-verify-protocol-signatures
AST: Use RequirementMachine to verify protocol requirement signatures
2 parents b9e831c + 589fc40 commit d9fd6cb

File tree

4 files changed

+27
-146
lines changed

4 files changed

+27
-146
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,12 @@ class GenericSignature {
217217

218218
/// Check invariants.
219219
void verify() const;
220+
221+
/// Check invariants for a list of requirements that are understood to
222+
/// be valid in the given signature; used to verify a protocol's
223+
/// requirement signature against the protocol generic signature
224+
/// <Self where Self : P>.
225+
void verify(ArrayRef<Requirement> reqts) const;
220226
};
221227

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

lib/AST/GenericSignature.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,20 +1564,20 @@ int swift::compareDependentTypes(Type type1, Type type2) {
15641564
#pragma mark Generic signature verification
15651565

15661566
void GenericSignature::verify() const {
1567+
verify(getRequirements());
1568+
}
1569+
1570+
void GenericSignature::verify(ArrayRef<Requirement> reqts) const {
15671571
auto canSig = getCanonicalSignature();
15681572

15691573
PrettyStackTraceGenericSignature debugStack("checking", canSig);
15701574

1571-
auto canonicalRequirements = canSig.getRequirements();
1572-
15731575
// We collect conformance requirements to check that they're minimal.
15741576
llvm::SmallDenseMap<CanType, SmallVector<ProtocolDecl *, 2>, 2> conformances;
15751577

15761578
// Check that the requirements satisfy certain invariants.
1577-
for (unsigned idx : indices(canonicalRequirements)) {
1578-
debugStack.setRequirement(idx);
1579-
1580-
const auto &reqt = canonicalRequirements[idx];
1579+
for (unsigned idx : indices(reqts)) {
1580+
const auto &reqt = reqts[idx].getCanonical();
15811581

15821582
// Left-hand side must be a canonical type parameter.
15831583
if (reqt.getKind() != RequirementKind::SameType) {
@@ -1661,7 +1661,7 @@ void GenericSignature::verify() const {
16611661
if (idx == 0) continue;
16621662

16631663
// Make sure that the left-hand sides are in nondecreasing order.
1664-
const auto &prevReqt = canonicalRequirements[idx-1];
1664+
const auto &prevReqt = reqts[idx-1];
16651665
int compareLHS =
16661666
compareDependentTypes(prevReqt.getFirstType(), reqt.getFirstType());
16671667
if (compareLHS > 0) {

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 2 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -8025,132 +8025,6 @@ void GenericSignatureBuilder::addGenericSignature(GenericSignature sig) {
80258025
addRequirement(reqt, FloatingRequirementSource::forAbstract(), nullptr);
80268026
}
80278027

8028-
#ifndef NDEBUG
8029-
8030-
static void checkGenericSignature(CanGenericSignature canSig,
8031-
GenericSignatureBuilder &builder) {
8032-
PrettyStackTraceGenericSignature debugStack("checking", canSig);
8033-
8034-
auto canonicalRequirements = canSig.getRequirements();
8035-
8036-
// We collect conformance requirements to check that they're minimal.
8037-
llvm::SmallDenseMap<CanType, SmallVector<ProtocolDecl *, 2>, 2> conformances;
8038-
8039-
// Check that the signature is canonical.
8040-
for (unsigned idx : indices(canonicalRequirements)) {
8041-
debugStack.setRequirement(idx);
8042-
8043-
const auto &reqt = canonicalRequirements[idx];
8044-
8045-
// Left-hand side must be canonical in its context.
8046-
// Check canonicalization of requirement itself.
8047-
switch (reqt.getKind()) {
8048-
case RequirementKind::Superclass:
8049-
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
8050-
"Left-hand side is not canonical");
8051-
assert(canSig->isCanonicalTypeInContext(reqt.getSecondType(), builder) &&
8052-
"Superclass type isn't canonical in its own context");
8053-
break;
8054-
8055-
case RequirementKind::Layout:
8056-
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
8057-
"Left-hand side is not canonical");
8058-
break;
8059-
8060-
case RequirementKind::SameType: {
8061-
auto isCanonicalAnchor = [&](Type type) {
8062-
if (auto *dmt = type->getAs<DependentMemberType>())
8063-
return canSig->isCanonicalTypeInContext(dmt->getBase(), builder);
8064-
return type->is<GenericTypeParamType>();
8065-
};
8066-
8067-
auto firstType = reqt.getFirstType();
8068-
auto secondType = reqt.getSecondType();
8069-
assert(isCanonicalAnchor(firstType));
8070-
8071-
if (reqt.getSecondType()->isTypeParameter()) {
8072-
assert(isCanonicalAnchor(secondType));
8073-
assert(compareDependentTypes(firstType, secondType) < 0 &&
8074-
"Out-of-order type parameters in same-type constraint");
8075-
} else {
8076-
assert(canSig->isCanonicalTypeInContext(secondType, builder) &&
8077-
"Concrete same-type isn't canonical in its own context");
8078-
}
8079-
break;
8080-
}
8081-
8082-
case RequirementKind::Conformance:
8083-
assert(canSig->isCanonicalTypeInContext(reqt.getFirstType(), builder) &&
8084-
"Left-hand side is not canonical");
8085-
assert(reqt.getFirstType()->isTypeParameter() &&
8086-
"Left-hand side must be a type parameter");
8087-
assert(isa<ProtocolType>(reqt.getSecondType().getPointer()) &&
8088-
"Right-hand side of conformance isn't a protocol type");
8089-
8090-
// Collect all conformance requirements on each type parameter.
8091-
conformances[CanType(reqt.getFirstType())].push_back(
8092-
reqt.getProtocolDecl());
8093-
break;
8094-
}
8095-
8096-
// From here on, we're only interested in requirements beyond the first.
8097-
if (idx == 0) continue;
8098-
8099-
// Make sure that the left-hand sides are in nondecreasing order.
8100-
const auto &prevReqt = canonicalRequirements[idx-1];
8101-
int compareLHS =
8102-
compareDependentTypes(prevReqt.getFirstType(), reqt.getFirstType());
8103-
assert(compareLHS <= 0 && "Out-of-order left-hand sides");
8104-
8105-
// If we have two same-type requirements where the left-hand sides differ
8106-
// but fall into the same equivalence class, we can check the form.
8107-
if (compareLHS < 0 && reqt.getKind() == RequirementKind::SameType &&
8108-
prevReqt.getKind() == RequirementKind::SameType &&
8109-
canSig->areSameTypeParameterInContext(prevReqt.getFirstType(),
8110-
reqt.getFirstType(),
8111-
builder)) {
8112-
// If it's a it's a type parameter, make sure the equivalence class is
8113-
// wired together sanely.
8114-
if (prevReqt.getSecondType()->isTypeParameter()) {
8115-
assert(prevReqt.getSecondType()->isEqual(reqt.getFirstType()) &&
8116-
"same-type constraints within an equiv. class are out-of-order");
8117-
} else {
8118-
// Otherwise, the concrete types must match up.
8119-
assert(prevReqt.getSecondType()->isEqual(reqt.getSecondType()) &&
8120-
"inconsistent concrete same-type constraints in equiv. class");
8121-
}
8122-
}
8123-
8124-
// If we have a concrete same-type requirement, we shouldn't have any
8125-
// other requirements on the same type.
8126-
if (reqt.getKind() == RequirementKind::SameType &&
8127-
!reqt.getSecondType()->isTypeParameter()) {
8128-
assert(compareLHS < 0 &&
8129-
"Concrete subject type should not have any other requirements");
8130-
}
8131-
8132-
assert(prevReqt.compare(reqt) < 0 &&
8133-
"Out-of-order requirements");
8134-
}
8135-
8136-
// Make sure we don't have redundant protocol conformance requirements.
8137-
for (auto pair : conformances) {
8138-
const auto &protos = pair.second;
8139-
auto canonicalProtos = protos;
8140-
8141-
// canonicalizeProtocols() will sort them and filter out any protocols that
8142-
// are refined by other protocols in the list. It should be a no-op at this
8143-
// point.
8144-
ProtocolType::canonicalizeProtocols(canonicalProtos);
8145-
8146-
assert(protos.size() == canonicalProtos.size() &&
8147-
"redundant conformance requirements");
8148-
assert(std::equal(protos.begin(), protos.end(), canonicalProtos.begin()) &&
8149-
"out-of-order conformance requirements");
8150-
}
8151-
}
8152-
#endif
8153-
81548028
static Type stripBoundDependentMemberTypes(Type t) {
81558029
if (auto *depMemTy = t->getAs<DependentMemberType>()) {
81568030
return DependentMemberType::get(
@@ -8444,15 +8318,8 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
84448318
// Form the generic signature.
84458319
auto sig = GenericSignature::get(getGenericParams(), requirements);
84468320

8447-
#ifndef NDEBUG
84488321
bool hadAnyError = Impl->HadAnyError;
84498322

8450-
if (requirementSignatureSelfProto &&
8451-
!hadAnyError) {
8452-
checkGenericSignature(sig.getCanonicalSignature(), *this);
8453-
}
8454-
#endif
8455-
84568323
// When we can, move this generic signature builder to make it the canonical
84578324
// builder, rather than constructing a new generic signature builder that
84588325
// will produce the same thing.
@@ -8461,7 +8328,7 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
84618328
//
84628329
// Also, we cannot do this when building a requirement signature.
84638330
if (requirementSignatureSelfProto == nullptr &&
8464-
!Impl->HadAnyError) {
8331+
!hadAnyError) {
84658332
// Register this generic signature builder as the canonical builder for the
84668333
// given signature.
84678334
Context.registerGenericSignatureBuilder(sig, std::move(*this));
@@ -8472,7 +8339,7 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature(
84728339
Impl.reset();
84738340

84748341
#ifndef NDEBUG
8475-
if (!requirementSignatureSelfProto &&
8342+
if (requirementSignatureSelfProto == nullptr &&
84768343
!hadAnyError) {
84778344
sig.verify();
84788345
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2618,10 +2618,12 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26182618
if (!SF || SF->Kind != SourceFileKind::Interface)
26192619
TypeChecker::inferDefaultWitnesses(PD);
26202620

2621+
// Explicity compute the requirement signature to detect errors.
2622+
auto reqSig = PD->getRequirementSignature();
2623+
26212624
if (PD->getASTContext().TypeCheckerOpts.DebugGenericSignatures) {
26222625
auto requirementsSig =
2623-
GenericSignature::get({PD->getProtocolSelfType()},
2624-
PD->getRequirementSignature());
2626+
GenericSignature::get({PD->getProtocolSelfType()}, reqSig);
26252627

26262628
llvm::errs() << "\n";
26272629
llvm::errs() << "Protocol requirement signature:\n";
@@ -2639,8 +2641,14 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
26392641
llvm::errs() << "\n";
26402642
}
26412643

2642-
// Explicity compute the requirement signature to detect errors.
2643-
(void) PD->getRequirementSignature();
2644+
2645+
#ifndef NDEBUG
2646+
// In asserts builds, also verify some invariants of the requirement
2647+
// signature.
2648+
PD->getGenericSignature().verify(reqSig);
2649+
#endif
2650+
2651+
(void) reqSig;
26442652

26452653
checkExplicitAvailability(PD);
26462654
}

0 commit comments

Comments
 (0)