AST: Fix up SubstitutionMap::verify()

This commit is contained in:
Slava Pestov
2024-11-14 12:28:03 -05:00
parent 2c3ee09678
commit 080f3d2950
2 changed files with 87 additions and 57 deletions

View File

@@ -60,10 +60,8 @@ SubstitutionMap::SubstitutionMap(
ArrayRef<Type> replacementTypes,
ArrayRef<ProtocolConformanceRef> conformances)
: storage(Storage::get(genericSig, replacementTypes, conformances)) {
#ifndef NDEBUG
if (genericSig->getASTContext().LangOpts.VerifyAllSubstitutionMaps)
verify();
#endif
}
ArrayRef<ProtocolConformanceRef> SubstitutionMap::getConformances() const {
@@ -534,78 +532,109 @@ SubstitutionMap::getOverrideSubstitutions(const NominalTypeDecl *baseNominal,
LookUpConformanceInOverrideSubs(info));
}
void SubstitutionMap::verify() const {
#ifndef NDEBUG
void SubstitutionMap::verify(bool allowInvalid) const {
if (empty())
return;
auto genericSig = getGenericSignature();
auto &ctx = genericSig->getASTContext();
if (ctx.isRecursivelyConstructingRequirementMachine(
genericSig.getCanonicalSignature()))
return;
unsigned conformanceIndex = 0;
for (const auto &req : getGenericSignature().getRequirements()) {
for (const auto &req : genericSig.getRequirements()) {
if (req.getKind() != RequirementKind::Conformance)
continue;
SWIFT_DEFER { ++conformanceIndex; };
auto substType = req.getFirstType().subst(*this);
if (substType->isTypeParameter() ||
substType->is<ArchetypeType>() ||
substType->isTypeVariableOrMember() ||
substType->is<UnresolvedType>() ||
substType->hasError())
continue;
auto conformance = getConformances()[conformanceIndex];
if (conformance.isInvalid())
continue;
auto substType = req.getFirstType().subst(*this);
// Unwrap various strange things.
substType = substType->getReferenceStorageReferent();
if (auto *selfType = substType->getAs<DynamicSelfType>())
substType = selfType->getSelfType();
// Don't bother validating these cases.
if (allowInvalid && substType->hasUnboundGenericType())
return;
if (conformance.isInvalid()) {
if (!allowInvalid) {
llvm::errs() << "Unexpected invalid conformance in substitution map:\n";
dump(llvm::dbgs());
llvm::errs() << "\n";
abort();
}
// All of the conformances should be concrete.
if (!conformance.isConcrete()) {
llvm::dbgs() << "Concrete type cannot have abstract conformance:\n";
substType->dump(llvm::dbgs());
llvm::dbgs() << "SubstitutionMap:\n";
dump(llvm::dbgs());
llvm::dbgs() << "\n";
llvm::dbgs() << "Requirement:\n";
req.dump(llvm::dbgs());
llvm::dbgs() << "\n";
}
assert(conformance.isConcrete() && "Conformance should be concrete");
if (substType->is<UnboundGenericType>())
continue;
auto conformanceTy = conformance.getConcrete()->getType();
if (conformanceTy->hasTypeParameter()
&& !substType->hasTypeParameter()) {
conformanceTy = conformance.getConcrete()->getDeclContext()
->mapTypeIntoContext(conformanceTy);
}
if (!substType->isEqual(conformanceTy)) {
llvm::dbgs() << "Conformance must match concrete replacement type:\n";
substType->dump(llvm::dbgs());
llvm::dbgs() << "Conformance type:\n";
conformance.getConcrete()->getType()->dump(llvm::dbgs());
llvm::dbgs() << "Conformance:\n";
conformance.dump(llvm::dbgs());
llvm::dbgs() << "\n";
llvm::dbgs() << "SubstitutionMap:\n";
dump(llvm::dbgs());
llvm::dbgs() << "\n";
llvm::dbgs() << "Requirement:\n";
req.dump(llvm::dbgs());
llvm::dbgs() << "\n";
if (conformance.isAbstract()) {
if (!substType->isTypeParameter() &&
!substType->is<PackElementType>() &&
!substType->is<ArchetypeType>() &&
!substType->isTypeVariableOrMember() &&
!substType->is<UnresolvedType>() &&
!substType->is<PlaceholderType>() &&
!substType->is<ErrorType>()) {
llvm::errs() << "Unexpected abstract conformance in substitution map:\n";
dump(llvm::errs());
llvm::errs() << "\n";
abort();
}
continue;
}
assert(substType->isEqual(conformanceTy)
&& "conformance should match corresponding type");
if (conformance.isPack()) {
// FIXME: Implement some kind of check here.
continue;
}
auto *concrete = conformance.getConcrete();
if (substType->isExistentialType()) {
assert(isa<SelfProtocolConformance>(conformance.getConcrete()) &&
"Existential type cannot have normal conformance");
if (req.getProtocolDecl()->isSpecificProtocol(KnownProtocolKind::Sendable) &&
isa<BuiltinProtocolConformance>(concrete)) {
continue;
}
if (!isa<SelfProtocolConformance>(concrete)) {
// A superclass-constrained self-conforming existential might conform
// concretely.
if (substType->getSuperclass())
continue;
llvm::errs() << "Expected to find a self conformance:\n";
substType->dump(llvm::errs());
llvm::errs() << "Substitution map:\n";
dump(llvm::errs());
llvm::errs() << "\n";
abort();
}
continue;
}
if (substType->isTypeParameter())
continue;
if (!concrete->getType()->isEqual(substType)) {
llvm::errs() << "Conformance with wrong conforming type:\n";
concrete->getType()->dump(llvm::errs());
llvm::errs() << "Should be:\n";
substType->dump(llvm::errs());
llvm::errs() << "Substitution map:\n";
dump(llvm::errs());
llvm::errs() << "\n";
abort();
}
}
#endif
}
void SubstitutionMap::profile(llvm::FoldingSetNodeID &id) const {