/// Compute substitutions for making a direct call to a SIL function with /// @convention(witness_method) convention. /// /// Such functions have a substituted generic signature where the /// abstract `Self` parameter from the original type of the protocol /// requirement is replaced by a concrete type. /// /// Thus, the original substitutions of the apply instruction that /// are written in terms of the requirement's generic signature need /// to be remapped to substitutions suitable for the witness signature. /// /// Supported remappings are: /// /// - (Concrete witness thunk) Original substitutions: /// [Self := ConcreteType, R0 := X0, R1 := X1, ...] /// - Requirement generic signature: /// <Self : P, R0, R1, ...> /// - Witness thunk generic signature: /// <W0, W1, ...> /// - Remapped substitutions: /// [W0 := X0, W1 := X1, ...] /// /// - (Class witness thunk) Original substitutions: /// [Self := C<A0, A1>, T0 := X0, T1 := X1, ...] /// - Requirement generic signature: /// <Self : P, R0, R1, ...> /// - Witness thunk generic signature: /// <Self : C<B0, B1>, B0, B1, W0, W1, ...> /// - Remapped substitutions: /// [Self := C<B0, B1>, B0 := A0, B1 := A1, W0 := X0, W1 := X1] /// /// - (Default witness thunk) Original substitutions: /// [Self := ConcreteType, R0 := X0, R1 := X1, ...] /// - Requirement generic signature: /// <Self : P, R0, R1, ...> /// - Witness thunk generic signature: /// <Self : P, W0, W1, ...> /// - Remapped substitutions: /// [Self := ConcreteType, W0 := X0, W1 := X1, ...] /// /// \param conformanceRef The (possibly-specialized) conformance /// \param requirementSig The generic signature of the requirement /// \param witnessThunkSig The generic signature of the witness method /// \param origSubMap The substitutions from the call instruction /// \param isDefaultWitness True if this is a default witness method /// \param classWitness The ClassDecl if this is a class witness method static SubstitutionMap getWitnessMethodSubstitutions( ModuleDecl *mod, ProtocolConformanceRef conformanceRef, GenericSignature *requirementSig, GenericSignature *witnessThunkSig, SubstitutionMap origSubMap, bool isDefaultWitness, ClassDecl *classWitness) { if (witnessThunkSig == nullptr) return SubstitutionMap(); if (isDefaultWitness) return origSubMap; assert(!conformanceRef.isAbstract()); auto conformance = conformanceRef.getConcrete(); // If `Self` maps to a bound generic type, this gives us the // substitutions for the concrete type's generic parameters. auto baseSubMap = conformance->getSubstitutions(mod); unsigned baseDepth = 0; auto *rootConformance = conformance->getRootNormalConformance(); if (auto *witnessSig = rootConformance->getGenericSignature()) baseDepth = witnessSig->getGenericParams().back()->getDepth() + 1; // If the witness has a class-constrained 'Self' generic parameter, // we have to build a new substitution map that shifts all generic // parameters down by one. if (classWitness != nullptr) { auto *proto = conformance->getProtocol(); auto selfType = proto->getSelfInterfaceType(); auto selfSubMap = SubstitutionMap::getProtocolSubstitutions( proto, selfType.subst(origSubMap), conformanceRef); if (baseSubMap.empty()) { assert(baseDepth == 0); baseSubMap = selfSubMap; } else { baseSubMap = SubstitutionMap::combineSubstitutionMaps( selfSubMap, baseSubMap, CombineSubstitutionMaps::AtDepth, /*firstDepth=*/1, /*secondDepth=*/0, witnessThunkSig); } baseDepth += 1; } return SubstitutionMap::combineSubstitutionMaps( baseSubMap, origSubMap, CombineSubstitutionMaps::AtDepth, /*firstDepth=*/baseDepth, /*secondDepth=*/1, witnessThunkSig); }
Type ProtocolConformanceRef::getTypeWitnessByName(Type type, ProtocolConformanceRef conformance, Identifier name, LazyResolver *resolver) { // For an archetype, retrieve the nested type with the appropriate // name. There are no conformance tables. if (auto archetype = type->getAs<ArchetypeType>()) { return archetype->getNestedType(name); } // Find the named requirement. AssociatedTypeDecl *assocType = nullptr; auto members = conformance.getRequirement()->lookupDirect(name); for (auto member : members) { assocType = dyn_cast<AssociatedTypeDecl>(member); if (assocType) break; } // FIXME: Shouldn't this be a hard error? if (!assocType) return nullptr; if (conformance.isAbstract()) return DependentMemberType::get(type, assocType); auto concrete = conformance.getConcrete(); if (!concrete->hasTypeWitness(assocType, resolver)) { return nullptr; } return concrete->getTypeWitness(assocType, resolver); }
// Eagerly visiting all used conformances leads to a large blowup // in the amount of SIL we read in. For optimization purposes we can defer // reading in most conformances until we need them for devirtualization. // However, we *must* pull in shared clang-importer-derived conformances // we potentially use, since we may not otherwise have a local definition. static bool mustDeserializeProtocolConformance(SILModule &M, ProtocolConformanceRef c) { if (!c.isConcrete()) return false; auto conformance = c.getConcrete()->getRootNormalConformance(); return M.Types.protocolRequiresWitnessTable(conformance->getProtocol()) && isa<ClangModuleUnit>(conformance->getDeclContext() ->getModuleScopeContext()); }
std::pair<SILWitnessTable *, ArrayRef<Substitution>> SILModule:: lookUpWitnessTable(ProtocolConformanceRef C, bool deserializeLazily) { // If we have an abstract conformance passed in (a legal value), just return // nullptr. if (!C.isConcrete()) return {nullptr, {}}; return lookUpWitnessTable(C.getConcrete()); }
Type TypeChecker::getWitnessType(Type type, ProtocolDecl *protocol, ProtocolConformanceRef conformance, Identifier name, Diag<> brokenProtocolDiag) { Type ty = ProtocolConformance::getTypeWitnessByName(type, conformance, name, this); if (!ty && !(conformance.isConcrete() && conformance.getConcrete()->isInvalid())) diagnose(protocol->getLoc(), brokenProtocolDiag); return ty; }
bool SILLinkerVisitor::visitProtocolConformance( ProtocolConformanceRef ref, const Optional<SILDeclRef> &Member) { // If an abstract protocol conformance was passed in, just return false. if (ref.isAbstract()) return false; // Otherwise try and lookup a witness table for C. auto C = ref.getConcrete(); SILWitnessTable *WT = Mod.lookUpWitnessTable(C).first; // If we don't find any witness table for the conformance, bail and return // false. if (!WT) { Mod.createWitnessTableDeclaration( C, TypeConverter::getLinkageForProtocolConformance( C->getRootNormalConformance(), NotForDefinition)); return false; } // If the looked up witness table is a declaration, there is nothing we can // do here. Just bail and return false. if (WT->isDeclaration()) return false; bool performFuncDeserialization = false; // For each entry in the witness table... for (auto &E : WT->getEntries()) { // If the entry is a witness method... if (E.getKind() == SILWitnessTable::WitnessKind::Method) { // And we are only interested in deserializing a specific requirement // and don't have that requirement, don't deserialize this method. if (Member.hasValue() && E.getMethodWitness().Requirement != *Member) continue; // The witness could be removed by dead function elimination. if (!E.getMethodWitness().Witness) continue; // Otherwise if it is the requirement we are looking for or we just want // to deserialize everything, add the function to the list of functions // to deserialize. performFuncDeserialization = true; addFunctionToWorklist(E.getMethodWitness().Witness); } } return performFuncDeserialization; }
/// Compute substitutions for making a direct call to a SIL function with /// @convention(witness_method) convention. /// /// Such functions have a substituted generic signature where the /// abstract `Self` parameter from the original type of the protocol /// requirement is replaced by a concrete type. /// /// Thus, the original substitutions of the apply instruction that /// are written in terms of the requirement's generic signature need /// to be remapped to substitutions suitable for the witness signature. /// /// \param conformanceRef The (possibly-specialized) conformance /// \param requirementSig The generic signature of the requirement /// \param witnessThunkSig The generic signature of the witness method /// \param origSubs The substitutions from the call instruction /// \param newSubs New substitutions are stored here static void getWitnessMethodSubstitutions( SILModule &M, ProtocolConformanceRef conformanceRef, GenericSignature *requirementSig, GenericSignature *witnessThunkSig, SubstitutionList origSubs, bool isDefaultWitness, SmallVectorImpl<Substitution> &newSubs) { if (witnessThunkSig == nullptr) return; if (isDefaultWitness) { newSubs.append(origSubs.begin(), origSubs.end()); return; } assert(!conformanceRef.isAbstract()); auto conformance = conformanceRef.getConcrete(); // If `Self` maps to a bound generic type, this gives us the // substitutions for the concrete type's generic parameters. auto baseSubMap = getSubstitutionsForProtocolConformance(conformanceRef); unsigned baseDepth = 0; auto *rootConformance = conformance->getRootNormalConformance(); if (auto *witnessSig = rootConformance->getGenericSignature()) baseDepth = witnessSig->getGenericParams().back()->getDepth() + 1; auto origDepth = 1; auto origSubMap = requirementSig->getSubstitutionMap(origSubs); auto subMap = SubstitutionMap::combineSubstitutionMaps(baseSubMap, origSubMap, CombineSubstitutionMaps::AtDepth, baseDepth, origDepth, witnessThunkSig); witnessThunkSig->getSubstitutions(subMap, newSubs); }
static ArrayRef<Substitution> getSubstitutionsForProtocolConformance(ProtocolConformanceRef CRef) { if (CRef.isAbstract()) return {}; auto C = CRef.getConcrete(); // Walk down to the base NormalProtocolConformance. ArrayRef<Substitution> Subs; const ProtocolConformance *ParentC = C; while (!isa<NormalProtocolConformance>(ParentC)) { switch (ParentC->getKind()) { case ProtocolConformanceKind::Normal: llvm_unreachable("should have exited the loop?!"); case ProtocolConformanceKind::Inherited: ParentC = cast<InheritedProtocolConformance>(ParentC) ->getInheritedConformance(); break; case ProtocolConformanceKind::Specialized: { auto SC = cast<SpecializedProtocolConformance>(ParentC); ParentC = SC->getGenericConformance(); assert(Subs.empty() && "multiple conformance specializations?!"); Subs = SC->getGenericSubstitutions(); break; } } } const NormalProtocolConformance *NormalC = cast<NormalProtocolConformance>(ParentC); // If the normal conformance is for a generic type, and we didn't hit a // specialized conformance, collect the substitutions from the generic type. // FIXME: The AST should do this for us. if (NormalC->getType()->isSpecialized() && Subs.empty()) { Subs = NormalC->getType() ->gatherAllSubstitutions(NormalC->getDeclContext()->getParentModule(), nullptr); } return Subs; }
void SILLinkerVisitor::visitProtocolConformance( ProtocolConformanceRef ref, const Optional<SILDeclRef> &Member) { // If an abstract protocol conformance was passed in, do nothing. if (ref.isAbstract()) return; bool mustDeserialize = mustDeserializeProtocolConformance(Mod, ref); // Otherwise try and lookup a witness table for C. auto C = ref.getConcrete(); if (!VisitedConformances.insert(C).second) return; auto *WT = Mod.lookUpWitnessTable(C, mustDeserialize); // If the looked up witness table is a declaration, there is nothing we can // do here. if (WT == nullptr || WT->isDeclaration()) { assert(!mustDeserialize && "unable to deserialize witness table when we must?!"); return; } auto maybeVisitRelatedConformance = [&](ProtocolConformanceRef c) { // Formally all conformances referenced by a used conformance are used. // However, eagerly visiting them all at this point leads to a large blowup // in the amount of SIL we read in. For optimization purposes we can defer // reading in most conformances until we need them for devirtualization. // However, we *must* pull in shared clang-importer-derived conformances // we potentially use, since we may not otherwise have a local definition. if (mustDeserializeProtocolConformance(Mod, c)) visitProtocolConformance(c, None); }; // For each entry in the witness table... for (auto &E : WT->getEntries()) { switch (E.getKind()) { // If the entry is a witness method... case SILWitnessTable::WitnessKind::Method: { // And we are only interested in deserializing a specific requirement // and don't have that requirement, don't deserialize this method. if (Member.hasValue() && E.getMethodWitness().Requirement != *Member) continue; // The witness could be removed by dead function elimination. if (!E.getMethodWitness().Witness) continue; // Otherwise, deserialize the witness if it has shared linkage, or if // we were asked to deserialize everything. maybeAddFunctionToWorklist(E.getMethodWitness().Witness); break; } // If the entry is a related witness table, see whether we need to // eagerly deserialize it. case SILWitnessTable::WitnessKind::BaseProtocol: { auto baseConformance = E.getBaseProtocolWitness().Witness; maybeVisitRelatedConformance(ProtocolConformanceRef(baseConformance)); break; } case SILWitnessTable::WitnessKind::AssociatedTypeProtocol: { auto assocConformance = E.getAssociatedTypeProtocolWitness().Witness; maybeVisitRelatedConformance(assocConformance); break; } case SILWitnessTable::WitnessKind::AssociatedType: case SILWitnessTable::WitnessKind::Invalid: break; } } }
/// Compute substitutions for making a direct call to a SIL function with /// @convention(witness_method) convention. /// /// Such functions have a substituted generic signature where the /// abstract `Self` parameter from the original type of the protocol /// requirement is replaced by a concrete type. /// /// Thus, the original substitutions of the apply instruction that /// are written in terms of the requirement's generic signature need /// to be remapped to substitutions suitable for the witness signature. /// /// \param conformanceRef The (possibly-specialized) conformance /// \param requirementSig The generic signature of the requirement /// \param witnessThunkSig The generic signature of the witness method /// \param origSubs The substitutions from the call instruction /// \param newSubs New substitutions are stored here static void getWitnessMethodSubstitutions( SILModule &M, ProtocolConformanceRef conformanceRef, GenericSignature *requirementSig, GenericSignature *witnessThunkSig, ArrayRef<Substitution> origSubs, bool isDefaultWitness, SmallVectorImpl<Substitution> &newSubs) { if (witnessThunkSig == nullptr) return; assert(!conformanceRef.isAbstract()); auto conformance = conformanceRef.getConcrete(); // Otherwise, we need to build new caller-side substitutions // written in terms of the witness thunk's generic signature, // mapping to the archetypes of the caller. SubstitutionMap subMap; // Take apart substitutions from the conforming type. ArrayRef<Substitution> witnessSubs; auto *rootConformance = conformance->getRootNormalConformance(); auto *witnessSig = rootConformance->getGenericSignature(); unsigned depth = 0; if (isDefaultWitness) { // For default witnesses, we substitute all of Self. auto gp = witnessThunkSig->getGenericParams().front()->getCanonicalType(); subMap.addSubstitution(gp, origSubs.front().getReplacement()); subMap.addConformances(gp, origSubs.front().getConformances()); // For default witnesses, innermost generic parameters are always at // depth 1. depth = 1; } else { // If `Self` maps to a bound generic type, this gives us the // substitutions for the concrete type's generic parameters. witnessSubs = getSubstitutionsForProtocolConformance(conformanceRef); if (!witnessSubs.empty()) { witnessSig->getSubstitutionMap(witnessSubs, subMap); depth = witnessSig->getGenericParams().back()->getDepth() + 1; } } // Next, take apart caller-side substitutions. // // Note that the Self-derived dependent types appearing on the left // hand side of the map are dropped. // FIXME: This won't be correct if the requirement itself adds 'Self' // requirements. We should be working from the substitutions in the witness. // // Also note that we rebuild the generic parameters in the requirement // to provide them with the required depth for the thunk itself. if (requirementSig->getGenericParams().back()->getDepth() > 0) { // Local function to replace generic parameters within the requirement // signature with the generic parameter we want to use in the substitution // map: // - If the generic parameter is 'Self', return a null type so we don't // add any substitution. // - Otherwise, reset the generic parameter's depth one level deeper than // the deepest generic parameter in the conformance. // // This local function is meant to be used with Type::transform(); auto replaceGenericParameter = [&](Type type) -> Type { if (auto gp = type->getAs<GenericTypeParamType>()) { if (gp->getDepth() == 0) return Type(); return GenericTypeParamType::get(depth, gp->getIndex(), M.getASTContext()); } return type; }; // Walk through the substitutions and dependent types. ArrayRef<Substitution> subs = origSubs; for (auto origDepTy : requirementSig->getAllDependentTypes()) { // Grab the next substitution. auto sub = subs.front(); subs = subs.slice(1); // Map the generic parameters in the dependent type into the witness // thunk's depth. auto mappedDepTy = origDepTy.transform(replaceGenericParameter); // If the dependent type was rooted in 'Self', it will come out null; // skip it. if (!mappedDepTy) continue; // Otherwise, record the replacement and conformances for the mapped // type. auto canTy = mappedDepTy->getCanonicalType(); subMap.addSubstitution(canTy, sub.getReplacement()); subMap.addConformances(canTy, sub.getConformances()); } assert(subs.empty() && "Did not consume all substitutions"); } // Now, apply both sets of substitutions computed above to the // forwarding substitutions of the witness thunk. witnessThunkSig->getSubstitutions(subMap, newSubs); }