//===--- SILGenLazyConformance.cpp ----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 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 forces emission of lazily-generated ClangImporter-synthesized // conformances. // //===----------------------------------------------------------------------===// #include "SILGen.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/Decl.h" #include "swift/AST/PackConformance.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ProtocolConformanceRef.h" #include "swift/Basic/Assertions.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/SIL/FormalLinkage.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILVisitor.h" using namespace swift; using namespace swift::Lowering; void SILGenModule::useConformance(SILInstruction *inst, ProtocolConformanceRef conformanceRef) { // If the conformance is invalid, crash deterministically even in noassert // builds. if (conformanceRef.isInvalid()) { // When lazy type checking is enabled, a conformance may only be diagnosed // as invalid during SILGen. Ignore it instead of asserting. auto &ctx = getASTContext(); if (ctx.TypeCheckerOpts.EnableLazyTypecheck && ctx.hadError()) return; ABORT([&](auto &out) { out << "Found an invalid conformance in SILGen. This may indicate a problem in "; out << "Sema or the ClangImporter.\n"; if (inst != nullptr) { out << "\n"; out << "Emitting lazy conformances for instruction:\n"; inst->print(out); out << "\n"; out << "In function:\n"; inst->getFunction()->print(out); out << "\n"; } }); } // We don't need to emit dependent conformances. if (conformanceRef.isAbstract()) return; // Recursively visit pack conformances. if (conformanceRef.isPack()) { auto *packConformance = conformanceRef.getPack(); for (auto patternConformanceRef : packConformance->getPatternConformances()) useConformance(inst, patternConformanceRef); return; } auto conformance = conformanceRef.getConcrete(); // Always look through inherited conformances. if (auto *inherited = dyn_cast(conformance)) conformance = inherited->getInheritedConformance(); // Emit any conformances implied by conditional requirements. if (auto *specialized = dyn_cast(conformance)) { useConformancesFromSubstitutions(inst, specialized->getSubstitutionMap()); conformance = specialized->getGenericConformance(); } // At this point, we should have a root conformance. auto rootConformance = dyn_cast(conformance); if (!rootConformance) return; // Builtin protocol conformances don't require anything to be emitted. if (isa(rootConformance)) return; // If this is a normal conformance that needs to be synthesized, we'll want // to emit it. if (auto normal = dyn_cast(conformance)) { // If this conformance was not synthesized, we're not going to be emitting // it lazily either, so we can avoid doing anything below. if (!normal->isSynthesized()) return; } else if (auto self = dyn_cast(conformance)) { // Marker protocols don't need anything synthesized. if (self->getProtocol()->isMarkerProtocol()) return; // Only Embedded Swift defers the synthesis of this symbol. if (!getASTContext().LangOpts.hasFeature(Feature::Embedded)) return; } else { // Nothing to synthesize. return; } // If we already emitted this witness table, we don't need to track the fact // we need it. if (emittedWitnessTables.count(rootConformance)) return; // Check if we already forced this witness table but haven't emitted it yet. if (!forcedConformances.insert(rootConformance).second) return; pendingConformances.push_back(rootConformance); } void SILGenModule::useConformancesFromSubstitutions( SILInstruction *inst, const SubstitutionMap subs) { for (auto conf : subs.getConformances()) useConformance(inst, conf); } void SILGenModule::useConformancesFromType(SILInstruction *inst, CanType type) { if (!usedConformancesFromTypes.insert(type.getPointer()).second) return; type.visit([&](Type t) { auto *decl = t->getAnyNominal(); if (!decl) return; if (isa(decl)) return; // If this is an imported noncopyable type with a deinitializer, record it. if (decl->hasClangNode() && !decl->canBeCopyable() && decl->getValueTypeDestructor() && importedNontrivialNoncopyableTypes.insert(decl).second) { visitImportedNontrivialNoncopyableType(decl); } auto genericSig = decl->getGenericSignature(); if (!genericSig) return; auto subMap = t->getContextSubstitutionMap(); useConformancesFromSubstitutions(inst, subMap); return; }); } void SILGenModule::useConformancesFromObjectiveCType( SILInstruction *inst, CanType type) { if (!usedConformancesFromObjectiveCTypes.insert(type.getPointer()).second) return; auto &ctx = getASTContext(); auto objectiveCBridgeable = ctx.getProtocol( KnownProtocolKind::ObjectiveCBridgeable); auto bridgedStoredNSError = ctx.getProtocol( KnownProtocolKind::BridgedStoredNSError); if (!objectiveCBridgeable && !bridgedStoredNSError) return; type.visit([&](Type t) { auto *decl = t->getAnyNominal(); if (!decl) return; if (!isa(decl->getModuleScopeContext())) return; if (objectiveCBridgeable) { if (auto subConformance = lookupConformance(t, objectiveCBridgeable)) useConformance(inst, subConformance); } if (bridgedStoredNSError) { if (auto subConformance = lookupConformance(t, bridgedStoredNSError)) useConformance(inst, subConformance); } }); } /// A visitor class that tries to guess which SIL instructions can cause /// IRGen to emit references to witness tables. This is used to emit /// ClangImporter-synthesized conformances lazily. /// /// In the long run, we'll instead have IRGen directly ask SILGen to /// generate a witness table when needed, so that we don't have to do /// any "guessing" here. class LazyConformanceEmitter : public SILInstructionVisitor { SILGenModule &SGM; public: LazyConformanceEmitter(SILGenModule &SGM) : SGM(SGM) {} void visitAllocExistentialBoxInst(AllocExistentialBoxInst *AEBI) { SGM.useConformancesFromType(AEBI, AEBI->getFormalConcreteType()); SGM.useConformancesFromObjectiveCType(AEBI, AEBI->getFormalConcreteType()); for (auto conformance : AEBI->getConformances()) SGM.useConformance(AEBI, conformance); } void visitAllocGlobalInst(AllocGlobalInst *AGI) { SGM.useConformancesFromType(AGI, AGI->getReferencedGlobal()->getLoweredType().getASTType()); } void visitAllocRefInst(AllocRefInst *ARI) { SGM.useConformancesFromType(ARI, ARI->getType().getASTType()); } void visitAllocStackInst(AllocStackInst *ASI) { SGM.useConformancesFromType(ASI, ASI->getType().getASTType()); } void visitApplyInst(ApplyInst *AI) { SGM.useConformancesFromObjectiveCType(AI, AI->getSubstCalleeType()); SGM.useConformancesFromSubstitutions(AI, AI->getSubstitutionMap()); } void visitBeginApplyInst(BeginApplyInst *BAI) { SGM.useConformancesFromObjectiveCType(BAI, BAI->getSubstCalleeType()); SGM.useConformancesFromSubstitutions(BAI, BAI->getSubstitutionMap()); } void visitBuiltinInst(BuiltinInst *BI) { SGM.useConformancesFromSubstitutions(BI, BI->getSubstitutions()); } void visitCheckedCastBranchInst(CheckedCastBranchInst *CCBI) { SGM.useConformancesFromType(CCBI, CCBI->getSourceFormalType()); SGM.useConformancesFromType(CCBI, CCBI->getTargetFormalType()); SGM.useConformancesFromObjectiveCType(CCBI, CCBI->getSourceFormalType()); SGM.useConformancesFromObjectiveCType(CCBI, CCBI->getTargetFormalType()); } void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *CCABI) { SGM.useConformancesFromType(CCABI, CCABI->getSourceFormalType()); SGM.useConformancesFromType(CCABI, CCABI->getTargetFormalType()); SGM.useConformancesFromObjectiveCType(CCABI, CCABI->getSourceFormalType()); SGM.useConformancesFromObjectiveCType(CCABI, CCABI->getTargetFormalType()); } void visitCopyAddrInst(CopyAddrInst *CAI) { SGM.useConformancesFromType(CAI, CAI->getSrc()->getType().getASTType()); SGM.useConformancesFromType(CAI, CAI->getDest()->getType().getASTType()); } void visitMarkUnresolvedMoveAddrInst(MarkUnresolvedMoveAddrInst *MAI) { SGM.useConformancesFromType(MAI, MAI->getSrc()->getType().getASTType()); SGM.useConformancesFromType(MAI, MAI->getDest()->getType().getASTType()); } void visitCopyValueInst(CopyValueInst *CVI) { SGM.useConformancesFromType(CVI, CVI->getOperand()->getType().getASTType()); } void visitDestroyAddrInst(DestroyAddrInst *DAI) { SGM.useConformancesFromType(DAI, DAI->getOperand()->getType().getASTType()); } void visitDestroyValueInst(DestroyValueInst *DVI) { SGM.useConformancesFromType(DVI, DVI->getOperand()->getType().getASTType()); } void visitGlobalAddrInst(GlobalAddrInst *GAI) { SGM.useConformancesFromType(GAI, GAI->getReferencedGlobal()->getLoweredType().getASTType()); } void visitGlobalValueInst(GlobalValueInst *GVI) { SGM.useConformancesFromType(GVI, GVI->getReferencedGlobal()->getLoweredType().getASTType()); } void visitKeyPathInst(KeyPathInst *KPI) { SGM.useConformancesFromSubstitutions(KPI, KPI->getSubstitutions()); } void visitInitEnumDataAddrInst(InitEnumDataAddrInst *IEDAI) { SGM.useConformancesFromType(IEDAI, IEDAI->getOperand()->getType().getASTType()); } void visitInjectEnumAddrInst(InjectEnumAddrInst *IEAI) { SGM.useConformancesFromType(IEAI, IEAI->getOperand()->getType().getASTType()); } void visitInitExistentialAddrInst(InitExistentialAddrInst *IEAI) { SGM.useConformancesFromType(IEAI, IEAI->getFormalConcreteType()); SGM.useConformancesFromObjectiveCType(IEAI, IEAI->getFormalConcreteType()); for (auto conformance : IEAI->getConformances()) SGM.useConformance(IEAI, conformance); } void visitInitExistentialMetatypeInst(InitExistentialMetatypeInst *IEMI) { SGM.useConformancesFromType(IEMI, IEMI->getOperand()->getType().getASTType()); for (auto conformance : IEMI->getConformances()) SGM.useConformance(IEMI, conformance); } void visitInitExistentialRefInst(InitExistentialRefInst *IERI) { SGM.useConformancesFromType(IERI, IERI->getFormalConcreteType()); SGM.useConformancesFromObjectiveCType(IERI, IERI->getFormalConcreteType()); for (auto conformance : IERI->getConformances()) SGM.useConformance(IERI, conformance); } void visitInitExistentialValueInst(InitExistentialValueInst *IEVI) { SGM.useConformancesFromType(IEVI, IEVI->getFormalConcreteType()); SGM.useConformancesFromObjectiveCType(IEVI, IEVI->getFormalConcreteType()); for (auto conformance : IEVI->getConformances()) SGM.useConformance(IEVI, conformance); } void visitMetatypeInst(MetatypeInst *MI) { SGM.useConformancesFromType(MI, MI->getType().getASTType()); } void visitPartialApplyInst(PartialApplyInst *PAI) { SGM.useConformancesFromObjectiveCType(PAI, PAI->getSubstCalleeType()); SGM.useConformancesFromSubstitutions(PAI, PAI->getSubstitutionMap()); } void visitSelectEnumAddrInst(SelectEnumAddrInst *SEAI) { SGM.useConformancesFromType(SEAI, SEAI->getEnumOperand()->getType().getASTType()); } void visitStructElementAddrInst(StructElementAddrInst *SEAI) { SGM.useConformancesFromType(SEAI, SEAI->getOperand()->getType().getASTType()); } void visitTryApplyInst(TryApplyInst *TAI) { SGM.useConformancesFromObjectiveCType(TAI, TAI->getSubstCalleeType()); SGM.useConformancesFromSubstitutions(TAI, TAI->getSubstitutionMap()); } void visitTupleElementAddrInst(TupleElementAddrInst *TEAI) { SGM.useConformancesFromType(TEAI, TEAI->getOperand()->getType().getASTType()); } void visitUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *UCCI) { SGM.useConformancesFromType(UCCI, UCCI->getSourceFormalType()); SGM.useConformancesFromType(UCCI, UCCI->getTargetFormalType()); SGM.useConformancesFromObjectiveCType(UCCI, UCCI->getSourceFormalType()); SGM.useConformancesFromObjectiveCType(UCCI, UCCI->getTargetFormalType()); } void visitUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *UCCAI) { SGM.useConformancesFromType(UCCAI, UCCAI->getSourceFormalType()); SGM.useConformancesFromType(UCCAI, UCCAI->getTargetFormalType()); SGM.useConformancesFromObjectiveCType(UCCAI, UCCAI->getSourceFormalType()); SGM.useConformancesFromObjectiveCType(UCCAI, UCCAI->getTargetFormalType()); } void visitUncheckedEnumDataAddrInstBase(UncheckedEnumDataAddrInstBase *UTEDAI) { SGM.useConformancesFromType(UTEDAI, UTEDAI->getEnum()->getType().getASTType()); } void visitWitnessMethodInst(WitnessMethodInst *WMI) { SGM.useConformance(WMI, WMI->getConformance()); } void visitSILInstruction(SILInstruction *I) {} }; void SILGenModule::emitLazyConformancesForFunction(SILFunction *F) { LazyConformanceEmitter emitter(*this); for (auto &BB : *F) for (auto &I : BB) emitter.visit(&I); } void SILGenModule::emitLazyConformancesForType(NominalTypeDecl *NTD) { auto genericSig = NTD->getGenericSignature(); for (auto reqt : genericSig.getRequirements()) { if (reqt.getKind() != RequirementKind::Layout) useConformancesFromType(nullptr, reqt.getSecondType()->getCanonicalType()); } if (auto *ED = dyn_cast(NTD)) { for (auto *EED : ED->getAllElements()) { if (EED->hasAssociatedValues()) { useConformancesFromType(nullptr, EED->getPayloadInterfaceType() ->getReducedType(genericSig)); } } } if (isa(NTD) || isa(NTD)) { for (auto *VD : NTD->getStoredProperties()) { useConformancesFromType(nullptr, VD->getValueInterfaceType() ->getReducedType(genericSig)); } } if (auto *CD = dyn_cast(NTD)) if (auto superclass = CD->getSuperclass()) useConformancesFromType(nullptr, superclass->getReducedType(genericSig)); if (auto *PD = dyn_cast(NTD)) { for (auto reqt : PD->getRequirementSignature().getRequirements()) { if (reqt.getKind() != RequirementKind::Layout) useConformancesFromType(nullptr, reqt.getSecondType()->getCanonicalType()); } } }