/// Derive a concrete type of self and conformance from the init_existential /// instruction. static Optional<std::tuple<ProtocolConformanceRef, CanType, SILValue>> getConformanceAndConcreteType(FullApplySite AI, SILInstruction *InitExistential, ProtocolDecl *Protocol, SILValue &NewSelf, ArrayRef<ProtocolConformanceRef> &Conformances) { // Try to derive the concrete type of self from the found init_existential. CanType ConcreteType; SILValue ConcreteTypeDef; if (auto IE = dyn_cast<InitExistentialAddrInst>(InitExistential)) { Conformances = IE->getConformances(); ConcreteType = IE->getFormalConcreteType(); NewSelf = IE; } else if (auto IER = dyn_cast<InitExistentialRefInst>(InitExistential)) { Conformances = IER->getConformances(); ConcreteType = IER->getFormalConcreteType(); NewSelf = IER->getOperand(); } else if (auto IEM = dyn_cast<InitExistentialMetatypeInst>(InitExistential)) { Conformances = IEM->getConformances(); NewSelf = IEM->getOperand(); ConcreteType = NewSelf->getType().getSwiftRValueType(); auto ExType = IEM->getType().getSwiftRValueType(); while (auto ExMetatype = dyn_cast<ExistentialMetatypeType>(ExType)) { ExType = ExMetatype.getInstanceType(); ConcreteType = cast<MetatypeType>(ConcreteType).getInstanceType(); } } else { return None; } if (ConcreteType->isOpenedExistential()) { assert(!InitExistential->getTypeDependentOperands().empty() && "init_existential is supposed to have a typedef operand"); ConcreteTypeDef = InitExistential->getTypeDependentOperands()[0].get(); } // Find the conformance for the protocol we're interested in. for (auto Conformance : Conformances) { auto Requirement = Conformance.getRequirement(); if (Requirement == Protocol) { return std::make_tuple(Conformance, ConcreteType, ConcreteTypeDef); } if (Requirement->inheritsFrom(Protocol)) { // If Requirement != Protocol, then the abstract conformance cannot be used // as is and we need to create a proper conformance. return std::make_tuple(Conformance.isAbstract() ? ProtocolConformanceRef(Protocol) : Conformance, ConcreteType, ConcreteTypeDef); } } llvm_unreachable("couldn't find matching conformance in substitution?"); }
/// Find an opened archetype represented by this type. /// It is assumed by this method that the type contains /// at most one opened archetype. /// Typically, it would be called from a type visitor. /// It checks only the type itself, but does not try to /// recursively check any children of this type, because /// this is the task of the type visitor invoking it. /// \returns The found archetype or empty type otherwise. CanType swift::getOpenedArchetypeOf(CanType Ty) { if (!Ty) return CanType(); assert(hasAtMostOneOpenedArchetype(Ty) && "Type should contain at most one opened archetype"); while (auto MetaTy = dyn_cast<AnyMetatypeType>(Ty)) { Ty = MetaTy->getInstanceType().getCanonicalTypeOrNull(); } if (Ty->isOpenedExistential()) return Ty; return CanType(); }
/// Propagate information about a concrete type from init_existential_addr /// or init_existential_ref into witness_method conformances and into /// apply instructions. /// This helps the devirtualizer to replace witness_method by /// class_method instructions and then devirtualize. SILInstruction * SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI, ProtocolDecl *Protocol, llvm::function_ref<void(CanType , ProtocolConformanceRef)> Propagate) { // Get the self argument. SILValue Self; if (auto *Apply = dyn_cast<ApplyInst>(AI)) { if (Apply->hasSelfArgument()) Self = Apply->getSelfArgument(); } else if (auto *Apply = dyn_cast<TryApplyInst>(AI)) { if (Apply->hasSelfArgument()) Self = Apply->getSelfArgument(); } assert(Self && "Self argument should be present"); // Try to find the init_existential, which could be used to // determine a concrete type of the self. CanType OpenedArchetype; SILValue OpenedArchetypeDef; SILInstruction *InitExistential = findInitExistential(AI, Self, OpenedArchetype, OpenedArchetypeDef); if (!InitExistential) return nullptr; // Try to derive the concrete type of self and a related conformance from // the found init_existential. ArrayRef<ProtocolConformanceRef> Conformances; auto NewSelf = SILValue(); auto ConformanceAndConcreteType = getConformanceAndConcreteType(AI, InitExistential, Protocol, NewSelf, Conformances); if (!ConformanceAndConcreteType) return nullptr; ProtocolConformanceRef Conformance = std::get<0>(*ConformanceAndConcreteType); CanType ConcreteType = std::get<1>(*ConformanceAndConcreteType); SILValue ConcreteTypeDef = std::get<2>(*ConformanceAndConcreteType); SILOpenedArchetypesTracker *OldOpenedArchetypesTracker = Builder.getOpenedArchetypesTracker(); SILOpenedArchetypesTracker OpenedArchetypesTracker(*AI.getFunction()); if (ConcreteType->isOpenedExistential()) { // Prepare a mini-mapping for opened archetypes. // SILOpenedArchetypesTracker OpenedArchetypesTracker(*AI.getFunction()); OpenedArchetypesTracker.addOpenedArchetypeDef(ConcreteType, ConcreteTypeDef); Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker); } // Propagate the concrete type into the callee-operand if required. Propagate(ConcreteType, Conformance); // Create a new apply instruction that uses the concrete type instead // of the existential type. auto *NewAI = createApplyWithConcreteType(AI, NewSelf, Self, ConcreteType, ConcreteTypeDef, Conformance, OpenedArchetype); if (ConcreteType->isOpenedExistential()) Builder.setOpenedArchetypesTracker(OldOpenedArchetypesTracker); return NewAI; }