//===--- ConformanceLookupTable - Conformance Lookup Table ----------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements the ConformanceLookupTable class. // //===----------------------------------------------------------------------===// #include "ConformanceLookupTable.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericParamList.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/SourceFile.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ProtocolConformanceRef.h" #include "llvm/Support/SaveAndRestore.h" using namespace swift; DeclContext *ConformanceLookupTable::ConformanceSource::getDeclContext() const { switch (getKind()) { case ConformanceEntryKind::Inherited: return getInheritingClass(); case ConformanceEntryKind::Explicit: return getExplicitDeclContext(); case ConformanceEntryKind::Implied: return getImpliedSource()->Source.getDeclContext(); case ConformanceEntryKind::Synthesized: return getSynthesizedDeclContext(); } llvm_unreachable("Unhandled ConformanceEntryKind in switch."); } ProtocolDecl *ConformanceLookupTable::ConformanceEntry::getProtocol() const { if (auto protocol = Conformance.dyn_cast()) return protocol; return Conformance.get()->getProtocol(); } void ConformanceLookupTable::ConformanceEntry::markSupersededBy( ConformanceLookupTable &table, ConformanceEntry *entry, bool diagnose) { assert(!isSuperseded() && "Already superseded"); // Note that we've been superseded. SupersededBy = entry; if (diagnose) { // Record the problem in the conformance table. We'll // diagnose these in semantic analysis. table.AllSupersededDiagnostics[getDeclContext()].push_back(this); } } void ConformanceLookupTable::ConformanceEntry::dump() const { dump(llvm::errs()); } void ConformanceLookupTable::ConformanceEntry::dump(raw_ostream &os, unsigned indent) const { os.indent(indent) << "(conformance @" << static_cast(this); os << " protocol="; getProtocol()->dumpRef(os); if (Loc.isValid()) { os << " loc="; Loc.print(os, getProtocol()->getASTContext().SourceMgr); } switch (getKind()) { case ConformanceEntryKind::Implied: os << " implied_by=@" << static_cast(Source.getImpliedSource()); break; case ConformanceEntryKind::Explicit: os << " explicit"; break; case ConformanceEntryKind::Inherited: os << " inherited"; break; case ConformanceEntryKind::Synthesized: os << " synthesized"; break; } if (auto conf = getConformance()) { os << " fixed_conformance=@" << static_cast(conf); } if (SupersededBy) os << " superseded_by=@" << static_cast(SupersededBy); os << ")\n"; } ConformanceLookupTable::ConformanceLookupTable(ASTContext &ctx) { // Register a cleanup with the ASTContext to call the conformance // table destructor. ctx.addCleanup([this]() { this->destroy(); }); } void ConformanceLookupTable::destroy() { this->~ConformanceLookupTable(); } namespace { struct ConformanceConstructionInfo : public Located { /// The location of the "unchecked" attribute, if this const SourceLoc uncheckedLoc; ConformanceConstructionInfo() { } ConformanceConstructionInfo( ProtocolDecl *item, SourceLoc loc, SourceLoc uncheckedLoc ) : Located(item, loc), uncheckedLoc(uncheckedLoc) { } }; } template void ConformanceLookupTable::forEachInStage(ConformanceStage stage, NominalTypeDecl *nominal, NominalFunc nominalFunc, ExtensionFunc extensionFunc) { assert(static_cast(stage) < NumConformanceStages && "NumConformanceStages has not been updated"); LastProcessedEntry &lastProcessed = LastProcessed[nominal][static_cast(stage)]; // Handle the nominal type. if (!lastProcessed.getInt()) { lastProcessed.setInt(true); // If we have conformances we can load, do so. // FIXME: This could be lazier. auto loader = nominal->takeConformanceLoader(); if (loader.first) { SmallVector conformances; loader.first->loadAllConformances(nominal, loader.second, conformances); loadAllConformances(nominal, conformances); } nominalFunc(nominal); } // Protocol extensions do not contribute protocol conformances. This // is enforced by semantic analysis, so the early exit here is a // performance optimization and also prevents us from erroneously // including those protocols before they get diagnosed. if (isa(nominal)) return; // Handle the extensions that we have not yet visited. nominal->prepareExtensions(); while (auto next = lastProcessed.getPointer() ? lastProcessed.getPointer()->NextExtension.getPointer() : nominal->FirstExtension) { lastProcessed.setPointer(next); SmallVector protocols; // If we have conformances we can load, do so. // FIXME: This could be lazier. auto loader = next->takeConformanceLoader(); if (loader.first) { SmallVector conformances; loader.first->loadAllConformances(next, loader.second, conformances); loadAllConformances(next, conformances); for (auto conf : conformances) { protocols.push_back({conf->getProtocol(), SourceLoc(), SourceLoc()}); } } else if (next->getParentSourceFile()) { bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(next, anyObject)) { if (auto proto = dyn_cast(found.Item)) protocols.push_back({proto, found.Loc, found.uncheckedLoc}); } } extensionFunc(next, protocols); } } void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl, ClassDecl *superclassDecl, ExtensionDecl *superclassExt) { // Local function to return the location of the superclass. This // takes a little digging, so compute on first use and cache it. SourceLoc superclassLoc; auto getSuperclassLoc = [&] { if (superclassLoc.isValid()) return superclassLoc; for (const auto &inherited : classDecl->getInherited()) { if (auto inheritedType = inherited.getType()) { if (inheritedType->getClassOrBoundGenericClass()) { superclassLoc = inherited.getSourceRange().Start; return superclassLoc; } if (inheritedType->isExistentialType()) { auto layout = inheritedType->getExistentialLayout(); if (layout.explicitSuperclass) { superclassLoc = inherited.getSourceRange().Start; return superclassLoc; } } } } superclassLoc = superclassDecl->getLoc(); return superclassLoc; }; llvm::SmallPtrSet protocols; auto addInheritedConformance = [&](ConformanceEntry *entry) { auto protocol = entry->getProtocol(); // Don't add unavailable conformances. if (auto dc = entry->Source.getDeclContext()) { if (auto ext = dyn_cast(dc)) { if (AvailableAttr::isUnavailable(ext)) return; } } // Don't add redundant conformances here. This is merely an // optimization; resolveConformances() would zap the duplicates // anyway. if (!protocols.insert(protocol).second) return; // Add the inherited entry. (void)addProtocol(protocol, getSuperclassLoc(), ConformanceSource::forInherited(classDecl)); }; // Add inherited conformances. DeclContext *superDC = superclassExt; if (!superclassExt) superDC = superclassDecl; for (auto entry : superclassDecl->ConformanceTable->AllConformances[superDC]){ addInheritedConformance(entry); } } void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, ConformanceStage stage) { switch (stage) { case ConformanceStage::RecordedExplicit: // Record all of the explicit conformances. forEachInStage( stage, nominal, [&](NominalTypeDecl *nominal) { addInheritedProtocols(nominal, ConformanceSource::forExplicit(nominal)); }, [&](ExtensionDecl *ext, ArrayRef protos) { // The extension decl may not be validated, so we can't use // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); for (auto locAndProto : protos) addProtocol( locAndProto.Item, locAndProto.Loc, source.withUncheckedLoc(locAndProto.uncheckedLoc)); }); break; case ConformanceStage::Inherited: updateLookupTable(nominal, ConformanceStage::RecordedExplicit); // For classes, expand implied conformances of the superclass, // because an implied conformance in the superclass is considered // "fixed" in the subclass. if (auto classDecl = dyn_cast(nominal)) { if (auto superclassDecl = classDecl->getSuperclassDecl()) { // Break infinite recursion when visiting ill-formed classes // with circular inheritance. if (VisitingSuperclass) return; llvm::SaveAndRestore visiting(VisitingSuperclass, true); // Don't update our own lookup table if we inherit from ourselves. if (classDecl == superclassDecl) break; // Resolve the conformances of the superclass. superclassDecl->prepareConformanceTable(); superclassDecl->ConformanceTable->updateLookupTable( superclassDecl, ConformanceStage::Resolved); // Expand inherited conformances from all superclasses. // We may have circular inheritance in ill-formed classes, so keep an // eye out for that. auto circularSuperclass = superclassDecl->getSuperclassDecl(); do { forEachInStage( stage, superclassDecl, [&](NominalTypeDecl *superclass) { inheritConformances(classDecl, superclassDecl, nullptr); }, [&](ExtensionDecl *ext, ArrayRef protos) { (void)protos; inheritConformances(classDecl, superclassDecl, ext); }); superclassDecl = superclassDecl->getSuperclassDecl(); if (circularSuperclass) circularSuperclass = circularSuperclass->getSuperclassDecl(); if (circularSuperclass) circularSuperclass = circularSuperclass->getSuperclassDecl(); } while (superclassDecl != circularSuperclass); } } break; case ConformanceStage::ExpandedImplied: // Record explicit conformances and import inherited conformances // before expanding. updateLookupTable(nominal, ConformanceStage::Inherited); // Expand inherited conformances. forEachInStage( stage, nominal, [&](NominalTypeDecl *nominal) { expandImpliedConformances(nominal, nominal); }, [&](ExtensionDecl *ext, ArrayRef protos) { (void)protos; expandImpliedConformances(nominal, ext); }); break; case ConformanceStage::Resolved: // Expand inherited conformances so we have the complete set of // conformances. updateLookupTable(nominal, ConformanceStage::ExpandedImplied); /// Determine whether any extensions were added that might require /// us to compute conformances again. bool anyChanged = false; forEachInStage(stage, nominal, [&](NominalTypeDecl *nominal) { anyChanged = true; }, [&](ExtensionDecl *ext, ArrayRef) { anyChanged = true; }); if (anyChanged) { // Compute the conformances for each protocol. bool anySuperseded = false; for (const auto &entry : Conformances) { if (resolveConformances(entry.first)) anySuperseded = true; } if (anySuperseded) { // Update the lists of all conformances to remove superseded // conformances. for (auto &conformances : AllConformances) { conformances.second.erase( std::remove_if(conformances.second.begin(), conformances.second.end(), [&](ConformanceEntry *entry) { return entry->isSuperseded(); }), conformances.second.end()); } } } break; } } void ConformanceLookupTable::loadAllConformances( DeclContext *dc, ArrayRef conformances) { // If this declaration context came from source, there's nothing to // do here. if (dc->getParentSourceFile()) return; // Add entries for each loaded conformance. for (auto conformance : conformances) { registerProtocolConformance(conformance); } } bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc, ConformanceSource source) { DeclContext *dc = source.getDeclContext(); ASTContext &ctx = dc->getASTContext(); // Determine the kind of conformance. ConformanceEntryKind kind = source.getKind(); // If this entry is synthesized or implied, scan to determine // whether there are any explicit better conformances that make this // conformance trivially superseded (and, therefore, not worth // recording). auto &conformanceEntries = Conformances[protocol]; if (kind == ConformanceEntryKind::Implied || kind == ConformanceEntryKind::Synthesized) { for (const auto *existingEntry : conformanceEntries) { switch (existingEntry->getKind()) { case ConformanceEntryKind::Explicit: case ConformanceEntryKind::Inherited: return false; case ConformanceEntryKind::Implied: // Ignore implied circular protocol inheritance if (existingEntry->getDeclContext() == dc) return false; // An implied conformance is better than a synthesized one, unless // the implied conformance was deserialized. if (kind == ConformanceEntryKind::Synthesized && existingEntry->getDeclContext()->getParentSourceFile() == nullptr) return false; break; case ConformanceEntryKind::Synthesized: // An implied conformance is better unless it was deserialized. if (dc->getParentSourceFile() == nullptr) return false; break; } } } /// Build the conformance entry (if it hasn't been built before). ConformanceEntry *entry = new (ctx) ConformanceEntry(loc, protocol, source); conformanceEntries.push_back(entry); // Record this as a conformance within the given declaration // context. AllConformances[dc].push_back(entry); return true; } void ConformanceLookupTable::addInheritedProtocols( llvm::PointerUnion decl, ConformanceSource source) { // Find all of the protocols in the inheritance list. bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { if (auto proto = dyn_cast(found.Item)) { addProtocol( proto, found.Loc, source.withUncheckedLoc(found.uncheckedLoc)); } } } void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc) { // Note: recursive type-checking implies that AllConformances // may be reallocated during this traversal, so pay the lookup cost // during each iteration. for (unsigned i = 0; i != AllConformances[dc].size(); ++i) { /// FIXME: Avoid the possibility of an infinite loop by fixing the root /// cause instead (incomplete circularity detection). assert(i <= 16384 && "Infinite loop due to circular protocol inheritance?"); ConformanceEntry *conformanceEntry = AllConformances[dc][i]; ProtocolDecl *conformingProtocol = conformanceEntry->getProtocol(); // An @objc enum that explicitly conforms to the Error protocol // also implicitly conforms to _ObjectiveCBridgeableError, via the // known protocol _BridgedNSError. if (conformingProtocol->isSpecificProtocol( KnownProtocolKind::Error) && isa(nominal) && nominal->isObjC() && cast(nominal)->hasCases() && cast(nominal)->hasOnlyCasesWithoutAssociatedValues()) { ASTContext &ctx = nominal->getASTContext(); if (auto bridgedNSError = ctx.getProtocol(KnownProtocolKind::BridgedNSError)) { addProtocol(bridgedNSError, SourceLoc(), ConformanceSource::forImplied(conformanceEntry)); } } addInheritedProtocols(conformingProtocol, ConformanceSource::forImplied(conformanceEntry)); } } /// Determine whether the given conformance entry kind can be replaced. static bool isReplaceable(ConformanceEntryKind kind) { switch (kind) { case ConformanceEntryKind::Implied: case ConformanceEntryKind::Synthesized: return true; case ConformanceEntryKind::Explicit: case ConformanceEntryKind::Inherited: return false; } llvm_unreachable("Unhandled ConformanceEntryKind in switch."); } ConformanceLookupTable::Ordering ConformanceLookupTable::compareConformances( ConformanceEntry *lhs, ConformanceEntry *rhs, bool &diagnoseSuperseded) { // If only one of the conformances is unconditionally available on the // current deployment target, pick that one. // // FIXME: Conformance lookup should really depend on source location for // this to be 100% correct. // FIXME: When a class and an extension with the same availability declare the // same conformance, this silently takes the class and drops the extension. if (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() != rhs->getDeclContext()->isAlwaysAvailableConformanceContext()) { return (lhs->getDeclContext()->isAlwaysAvailableConformanceContext() ? Ordering::Before : Ordering::After); } // If one entry is fixed and the other is not, we have our answer. if (lhs->isFixed() != rhs->isFixed()) { auto isReplaceableOrMarker = [](ConformanceEntry *entry) -> bool { ConformanceEntryKind kind = entry->getRankingKind(); if (isReplaceable(kind)) return true; // Allow replacement of an explicit conformance to a marker protocol. // (This permits redundant explicit declarations of `Sendable`.) return (kind == ConformanceEntryKind::Explicit && entry->getProtocol()->isMarkerProtocol()); }; // If the non-fixed conformance is not replaceable, we have a failure to // diagnose. // FIXME: We should probably diagnose if they have different constraints. diagnoseSuperseded = (lhs->isFixed() && !isReplaceableOrMarker(rhs)) || (rhs->isFixed() && !isReplaceableOrMarker(lhs)); return lhs->isFixed() ? Ordering::Before : Ordering::After; } ConformanceEntryKind lhsKind = lhs->getRankingKind(); ConformanceEntryKind rhsKind = rhs->getRankingKind(); if (lhsKind != ConformanceEntryKind::Implied || rhsKind != ConformanceEntryKind::Implied) { // If both conformances are non-replaceable, diagnose the // superseded one. diagnoseSuperseded = !isReplaceable(lhsKind) && !isReplaceable(rhsKind) && !(lhsKind == ConformanceEntryKind::Inherited && rhsKind == ConformanceEntryKind::Inherited); // If we can order by kind, do so. if (lhsKind != rhsKind) { return (static_cast(lhsKind) < static_cast(rhsKind)) ? Ordering::Before : Ordering::After; } // We shouldn't get two synthesized conformances. It's not harmful // per se, but it's indicative of redundant logic in the frontend. assert((lhs->getKind() != ConformanceEntryKind::Synthesized || rhs->getKind() != ConformanceEntryKind::Synthesized) && "Shouldn't ever get two truly synthesized conformances"); // FIXME: Deterministic ordering. return Ordering::Before; } // Both the left- and right-hand sides are implied, so determine where the // conformance should go. assert(lhsKind == ConformanceEntryKind::Implied && "Expected implied conformance"); assert(rhsKind == ConformanceEntryKind::Implied && "Expected implied conformance"); diagnoseSuperseded = false; // First, try to use the stated explicit conformances to determine where the // conformance should go. auto lhsExplicit = lhs->getDeclaredConformance(); auto lhsExplicitProtocol = lhsExplicit->getProtocol(); auto rhsExplicit = rhs->getDeclaredConformance(); auto rhsExplicitProtocol = rhsExplicit->getProtocol(); if (lhsExplicitProtocol != rhsExplicitProtocol) { // If the explicit protocol for the left-hand side is implied by // the explicit protocol for the right-hand side, the left-hand // side supersedes the right-hand side. if (rhsExplicitProtocol->inheritsFrom(lhsExplicitProtocol)) return Ordering::Before; // If the explicit protocol for the right-hand side is implied by // the explicit protocol for the left-hand side, the right-hand // side supersedes the left-hand side. if (lhsExplicitProtocol->inheritsFrom(rhsExplicitProtocol)) return Ordering::After; } // Prefer the least conditional implier, which we approximate by seeing if one // of the contexts syntactically has no generic requirements. This misses // redundant cases like `struct Foo {} extension Foo: P where T: P {}` // (Foo : P is unconditional), but isConstrainedExtension doesn't fly as it // requires the generic signature of the extension to exist, which requires // conformances to exist, which is what we're doing here. auto hasAdditionalRequirements = [&](ConformanceEntry *entry) { if (auto ED = dyn_cast(entry->getDeclContext())) if (auto TWC = ED->getTrailingWhereClause()) return !TWC->getRequirements().empty(); return false; }; bool lhsHasReqs = hasAdditionalRequirements(lhs); bool rhsHasReqs = hasAdditionalRequirements(rhs); if (lhsHasReqs != rhsHasReqs) return lhsHasReqs ? Ordering::After : Ordering::Before; // If the two conformances come from the same file, pick the first context // in the file. auto lhsSF = lhs->getDeclContext()->getParentSourceFile(); auto rhsSF = rhs->getDeclContext()->getParentSourceFile(); if (lhsSF && lhsSF == rhsSF) { ASTContext &ctx = lhsSF->getASTContext(); return ctx.SourceMgr.isBeforeInBuffer(lhs->getDeclaredLoc(), rhs->getDeclaredLoc()) ? Ordering::Before : Ordering::After; } // If one of the conformances comes from the same file as the type // declaration, pick that one; this is so that conformance synthesis works if // there's any implied conformance in the same file as the type. auto NTD = lhs->getDeclContext()->getSelfNominalTypeDecl(); auto typeSF = NTD->getParentSourceFile(); if (typeSF) { if (typeSF == lhsSF) return Ordering::Before; if (typeSF == rhsSF) return Ordering::After; } // Otherwise, pick the earlier file unit. auto lhsFileUnit = dyn_cast(lhs->getDeclContext()->getModuleScopeContext()); auto rhsFileUnit = dyn_cast(rhs->getDeclContext()->getModuleScopeContext()); assert(lhsFileUnit && rhsFileUnit && "Not from a file unit?"); if (lhsFileUnit == rhsFileUnit) { // If the file units are the same, just pick arbitrarily; we're not // actually emitting anything. // FIXME: Only because we're synthesizing conformances for deserialized // protocols. Once that's no longer true (because we're serializing // everything appropriately in the module), we should assert that this // does not happen. assert(!lhsSF && !rhsSF && "Source files shouldn't conflict"); return Ordering::Before; } auto module = lhs->getDeclContext()->getParentModule(); assert(lhs->getDeclContext()->getParentModule() == rhs->getDeclContext()->getParentModule() && "conformances should be in the same module"); for (auto file : module->getFiles()) { if (file == lhsFileUnit) return Ordering::Before; if (file == rhsFileUnit) return Ordering::After; } llvm_unreachable("files weren't in the parent module?"); } bool ConformanceLookupTable::resolveConformances(ProtocolDecl *protocol) { // Find any entries that are superseded by other entries. ConformanceEntries &entries = Conformances[protocol]; llvm::SmallPtrSet knownConformances; bool anySuperseded = false; for (auto entry : entries) { // If this entry has a conformance associated with it, note that. if (entry->getConformance()) knownConformances.insert(entry->getDeclContext()); // If this entry was superseded, move on. if (entry->isSuperseded()) { anySuperseded = true; continue; } // Determine whether this entry is superseded by (or supersedes) // some other entry. for (auto otherEntry : entries) { if (entry == otherEntry) continue; if (otherEntry->isSuperseded()) { anySuperseded = true; continue; } bool diagnoseSuperseded = false; bool doneWithEntry = false; switch (compareConformances(entry, otherEntry, diagnoseSuperseded)) { case Ordering::Equivalent: break; case Ordering::Before: otherEntry->markSupersededBy(*this, entry, diagnoseSuperseded); anySuperseded = true; break; case Ordering::After: entry->markSupersededBy(*this, otherEntry, diagnoseSuperseded); anySuperseded = true; doneWithEntry = true; break; } if (doneWithEntry) break; } } // If any entries were superseded, remove them now. if (anySuperseded) { entries.erase(std::remove_if(entries.begin(), entries.end(), [&](ConformanceEntry *entry) { return entry->isSuperseded(); }), entries.end()); } return anySuperseded; } DeclContext *ConformanceLookupTable::getConformingContext( NominalTypeDecl *nominal, ConformanceEntry *entry) { ProtocolDecl *protocol = entry->getProtocol(); // Dig through the inherited entries to find a non-inherited one. // Handle recursive inheritance. SmallPtrSet visited; while (entry->getKind() == ConformanceEntryKind::Inherited) { // Make sure we have an up-to-date conformance table for the // superclass. auto classDecl = cast(nominal); if (!visited.insert(classDecl).second) return nullptr; // If we had a circular dependency, the superclass may not exist. auto superclassDecl = classDecl->getSuperclassDecl(); if (!superclassDecl) return nullptr; if (!classDecl->ConformanceTable->VisitingSuperclass) { llvm::SaveAndRestore visiting( classDecl->ConformanceTable ->VisitingSuperclass, true); superclassDecl->prepareConformanceTable(); superclassDecl->ConformanceTable->resolveConformances(protocol); } // Grab the superclass entry and continue searching for a // non-inherited conformance. // FIXME: Ambiguity detection and resolution. const auto &superclassConformances = superclassDecl->ConformanceTable->Conformances[protocol]; if (superclassConformances.empty()) { assert(protocol->isSpecificProtocol(KnownProtocolKind::Sendable)); // Go dig for a superclass that does conform to Sendable. // FIXME: This is a hack because the inherited conformances aren't // getting updated properly. Type classTy = nominal->getDeclaredInterfaceType(); ModuleDecl *module = nominal->getParentModule(); do { Type superclassTy = classTy->getSuperclassForDecl(superclassDecl); if (superclassTy->is()) return nullptr; auto inheritedConformance = module->lookupConformance( superclassTy, protocol, /*allowMissing=*/false); if (inheritedConformance.hasUnavailableConformance()) inheritedConformance = ProtocolConformanceRef::forInvalid(); if (inheritedConformance) return superclassDecl; } while ((superclassDecl = superclassDecl->getSuperclassDecl())); return nullptr; } entry = superclassConformances.front(); nominal = superclassDecl; } return entry->getDeclContext(); } ProtocolConformance * ConformanceLookupTable::getConformance(NominalTypeDecl *nominal, ConformanceEntry *entry) { // If we already have a conformance, we're done. if (auto conformance = entry->getConformance()) return conformance; ProtocolDecl *protocol = entry->getProtocol(); // Determine where the explicit conformance actually lives. // FIXME: This is a hack to ensure that inherited conformances are // always "single step", which is bad for resilience but is assumed // elsewhere in the compiler. DeclContext *conformingDC = getConformingContext(nominal, entry); if (!conformingDC) return nullptr; auto *conformingNominal = conformingDC->getSelfNominalTypeDecl(); // Form the conformance. Type type = entry->getDeclContext()->getDeclaredInterfaceType(); ASTContext &ctx = nominal->getASTContext(); if (entry->getKind() == ConformanceEntryKind::Inherited) { // For an inherited conformance, the conforming nominal type will // be different from the nominal type. assert(conformingNominal != nominal && "Broken inherited conformance"); // Find the superclass type that matches where the conformance was // declared. auto *conformingClass = cast(conformingNominal); Type superclassTy = type->getSuperclassForDecl(conformingClass); if (superclassTy->is()) return nullptr; // Look up the inherited conformance. ModuleDecl *module = entry->getDeclContext()->getParentModule(); auto inheritedConformance = module->lookupConformance( superclassTy, protocol, /*allowMissing=*/true); // Form the inherited conformance. entry->Conformance = ctx.getInheritedConformance(type, inheritedConformance.getConcrete()); } else { // Protocols don't have conformance lookup tables. Self-conformance is // handled directly in lookupConformance(). assert(!isa(conformingNominal)); assert(!isa(conformingDC->getSelfNominalTypeDecl())); Type conformingType = conformingDC->getSelfInterfaceType(); SourceLoc conformanceLoc = conformingNominal == conformingDC ? conformingNominal->getLoc() : cast(conformingDC)->getLoc(); // Create or find the normal conformance. auto normalConf = ctx.getConformance(conformingType, protocol, conformanceLoc, conformingDC, ProtocolConformanceState::Incomplete, entry->Source.getUncheckedLoc().isValid()); // Invalid code may cause the getConformance call below to loop, so break // the infinite recursion by setting this eagerly to shortcircuit with the // early return at the start of this function. entry->Conformance = normalConf; NormalProtocolConformance *implyingConf = nullptr; if (entry->Source.getKind() == ConformanceEntryKind::Implied) { auto implyingEntry = entry->Source.getImpliedSource(); implyingConf = getConformance(conformingNominal, implyingEntry) ->getRootNormalConformance(); } normalConf->setSourceKindAndImplyingConformance(entry->Source.getKind(), implyingConf); // If the conformance was synthesized by the ClangImporter, give it a // lazy loader that will be used to populate the conformance. // First, if this is a conformance to a base protocol of a derived // protocol, find the most derived protocol. auto *impliedEntry = entry; while (impliedEntry->getKind() == ConformanceEntryKind::Implied) impliedEntry = impliedEntry->Source.getImpliedSource(); // Check if this was a synthesized conformance. if (impliedEntry->getKind() == ConformanceEntryKind::Synthesized) { auto *impliedProto = impliedEntry->getProtocol(); // Find a SynthesizedProtocolAttr corresponding to the protocol. for (auto attr : conformingNominal->getAttrs() .getAttributes()) { auto otherProto = attr->getProtocol(); if (otherProto == impliedProto) { // Set the conformance loader to the loader stashed inside // the attribute. normalConf->setLazyLoader(attr->getLazyLoader(), /*context=*/0); if (attr->isUnchecked()) normalConf->setUnchecked(); break; } } } } return entry->Conformance.get(); } void ConformanceLookupTable::addSynthesizedConformance( NominalTypeDecl *nominal, ProtocolDecl *protocol, DeclContext *conformanceDC) { addProtocol(protocol, nominal->getLoc(), ConformanceSource::forSynthesized(conformanceDC)); } void ConformanceLookupTable::registerProtocolConformance( ProtocolConformance *conformance, bool synthesized) { auto protocol = conformance->getProtocol(); auto dc = conformance->getDeclContext(); auto nominal = dc->getSelfNominalTypeDecl(); // If there is an entry to update, do so. auto &dcConformances = AllConformances[dc]; for (auto entry : dcConformances) { if (entry->getProtocol() == protocol) { assert(!entry->getConformance() || entry->getConformance() == conformance && "Mismatched conformances"); entry->Conformance = conformance; return; } } // Otherwise, add a new entry. auto inherited = dyn_cast(conformance); ConformanceSource source = inherited ? ConformanceSource::forInherited(cast(nominal)) : synthesized ? ConformanceSource::forSynthesized(dc) : ConformanceSource::forExplicit(dc); ASTContext &ctx = nominal->getASTContext(); ConformanceEntry *entry = new (ctx) ConformanceEntry(SourceLoc(), protocol, source); entry->Conformance = conformance; // Record that this type conforms to the given protocol. Conformances[protocol].push_back(entry); // Record this as a conformance within the given declaration // context. dcConformances.push_back(entry); } bool ConformanceLookupTable::lookupConformance( NominalTypeDecl *nominal, ProtocolDecl *protocol, SmallVectorImpl &conformances) { // Update to record all explicit and inherited conformances. updateLookupTable(nominal, ConformanceStage::Inherited); // Look for conformances to this protocol. auto known = Conformances.find(protocol); if (known == Conformances.end()) { // If we didn't find anything, expand implied conformances. updateLookupTable(nominal, ConformanceStage::ExpandedImplied); known = Conformances.find(protocol); // We didn't find anything. if (known == Conformances.end()) return false; } // Resolve the conformances for this protocol. resolveConformances(protocol); for (auto entry : Conformances[protocol]) { if (auto conformance = getConformance(nominal, entry)) { conformances.push_back(conformance); } } return !conformances.empty(); } void ConformanceLookupTable::lookupConformances( NominalTypeDecl *nominal, DeclContext *dc, std::vector *conformances, SmallVectorImpl *diagnostics) { // We need to expand all implied conformances before we can find // those conformances that pertain to this declaration context. updateLookupTable(nominal, ConformanceStage::ExpandedImplied); /// Resolve conformances for each of the protocols to which this /// declaration may provide a conformance. Only some of these will /// result in conformances that are attributed to this declaration /// context. auto &potentialConformances = AllConformances[dc]; for (const auto &potential : potentialConformances) { resolveConformances(potential->getProtocol()); } // Remove any superseded conformances from AllConformances. potentialConformances.erase( std::remove_if(potentialConformances.begin(), potentialConformances.end(), [&](ConformanceEntry *entry) { if (entry->isSuperseded()) return true; // Record the conformance. if (conformances) { if (auto conformance = getConformance(nominal, entry)) conformances->push_back(conformance); } return false; }), potentialConformances.end()); // Gather any diagnostics we've produced. if (diagnostics) { auto knownDiags = AllSupersededDiagnostics.find(dc); if (knownDiags != AllSupersededDiagnostics.end()) { for (const auto *entry : knownDiags->second) { ConformanceEntry *supersededBy = entry->getSupersededBy(); diagnostics->push_back({entry->getProtocol(), entry->getDeclaredLoc(), entry->getKind(), entry->getDeclaredConformance()->getProtocol(), supersededBy->getDeclContext(), supersededBy->getKind(), supersededBy->getDeclaredConformance() ->getProtocol()}); } // We have transferred these diagnostics; erase them. AllSupersededDiagnostics.erase(knownDiags); } } } void ConformanceLookupTable::getAllProtocols( NominalTypeDecl *nominal, SmallVectorImpl &scratch, bool sorted) { // We need to expand all implied conformances to find the complete // set of protocols to which this nominal type conforms. updateLookupTable(nominal, ConformanceStage::ExpandedImplied); // Gather all of the protocols. for (const auto &conformance : Conformances) { if (conformance.second.empty()) continue; scratch.push_back(conformance.first); } if (sorted) { llvm::array_pod_sort(scratch.begin(), scratch.end(), TypeDecl::compare); } } int ConformanceLookupTable::compareProtocolConformances( ProtocolConformance * const *lhsPtr, ProtocolConformance * const *rhsPtr) { ProtocolConformance *lhs = *lhsPtr; ProtocolConformance *rhs = *rhsPtr; // If the two conformances are normal conformances with locations, // sort by location. if (auto lhsNormal = dyn_cast(lhs)) { if (auto rhsNormal = dyn_cast(rhs)) { if (lhsNormal->getLoc().isValid() && rhsNormal->getLoc().isValid()) { ASTContext &ctx = lhs->getDeclContext()->getASTContext(); unsigned lhsBuffer = ctx.SourceMgr.findBufferContainingLoc(lhsNormal->getLoc()); unsigned rhsBuffer = ctx.SourceMgr.findBufferContainingLoc(rhsNormal->getLoc()); // If the buffers are the same, use source location ordering. if (lhsBuffer == rhsBuffer) { return ctx.SourceMgr.isBeforeInBuffer(lhsNormal->getLoc(), rhsNormal->getLoc()); } // Otherwise, order by buffer identifier. return ctx.SourceMgr.getIdentifierForBuffer(lhsBuffer) .compare(ctx.SourceMgr.getIdentifierForBuffer(rhsBuffer)); } } } // Otherwise, sort by protocol. ProtocolDecl *lhsProto = lhs->getProtocol(); ProtocolDecl *rhsProto = rhs->getProtocol(); return TypeDecl::compare(lhsProto, rhsProto); } void ConformanceLookupTable::getAllConformances( NominalTypeDecl *nominal, bool sorted, SmallVectorImpl &scratch) { // We need to expand and resolve all conformances to enumerate them. updateLookupTable(nominal, ConformanceStage::Resolved); // Gather all of the protocols. for (const auto &conformance : AllConformances) { for (auto entry : conformance.second) { if (auto conformance = getConformance(nominal, entry)) scratch.push_back(conformance); } } // If requested, sort the results. if (sorted) { llvm::array_pod_sort(scratch.begin(), scratch.end(), &compareProtocolConformances); } } void ConformanceLookupTable::getImplicitProtocols( NominalTypeDecl *nominal, SmallVectorImpl &protocols) { for (auto conformance : AllConformances[nominal]) { if (conformance->getKind() == ConformanceEntryKind::Synthesized) { protocols.push_back(conformance->getProtocol()); } } } ArrayRef ConformanceLookupTable::getSatisfiedProtocolRequirementsForMember( const ValueDecl *member, NominalTypeDecl *nominal, bool sorted) { auto It = ConformingDeclMap.find(member); if (It != ConformingDeclMap.end()) return It->second; SmallVector result; getAllConformances(nominal, sorted, result); auto &reqs = ConformingDeclMap[member]; if (isa(member)) { for (auto *conf : result) { if (conf->isInvalid()) continue; conf->forEachTypeWitness([&](const AssociatedTypeDecl *assoc, Type type, TypeDecl *typeDecl) -> bool { if (typeDecl == member) reqs.push_back(const_cast(assoc)); return false; }); } } else { for (auto *conf : result) { if (conf->isInvalid()) continue; auto root = conf->getRootConformance(); root->forEachValueWitness([&](ValueDecl *req, Witness witness) { if (witness.getDecl() == member) reqs.push_back(req); }); } } return reqs; } void ConformanceLookupTable::dump() const { dump(llvm::errs()); } void ConformanceLookupTable::dump(raw_ostream &os) const { for (const auto &dcEntries : AllConformances) { os << "Conformances in context:\n"; dcEntries.first->printContext(os); for (auto entry : dcEntries.second) { entry->dump(os); } } }