Exemplo n.º 1
0
/// Emit a single protocol witness table reference.
llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF,
                                                 CanArchetypeType archetype,
                                                 ProtocolDecl *protocol) {
  assert(Lowering::TypeConverter::protocolRequiresWitnessTable(protocol) &&
         "looking up witness table for protocol that doesn't have one");

  // The following approach assumes that a protocol will only appear in
  // an archetype's conformsTo array if the archetype is either explicitly
  // constrained to conform to that protocol (in which case we should have
  // a cache entry for it) or there's an associated type declaration with
  // that protocol listed as a direct requirement.

  auto localDataKind =
    LocalTypeDataKind::forAbstractProtocolWitnessTable(protocol);

  // Check immediately for an existing cache entry.
  // TODO: don't give this absolute precedence over other access paths.
  auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind);
  if (wtable) return wtable;

  // If we don't have an environment, this must be an implied witness table
  // reference.
  // FIXME: eliminate this path when opened types have generic environments.
  auto environment = archetype->getGenericEnvironment();
  if (!environment) {
    assert(archetype->isOpenedExistential() &&
           "non-opened archetype lacking generic environment?");
    SmallVector<ProtocolEntry, 4> entries;
    for (auto p : archetype->getConformsTo()) {
      const ProtocolInfo &impl =
          IGF.IGM.getProtocolInfo(p, ProtocolInfoKind::RequirementSignature);
      entries.push_back(ProtocolEntry(p, impl));
    }

    return emitImpliedWitnessTableRef(IGF, entries, protocol,
        [&](unsigned index) -> llvm::Value* {
      auto localDataKind =
         LocalTypeDataKind::forAbstractProtocolWitnessTable(
                                                  entries[index].getProtocol());
      auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind);
      assert(wtable &&
             "opened type without local type data for direct conformance?");
      return wtable;
    });
  }

  // Otherwise, ask the generic signature for the environment for the best
  // path to the conformance.
  // TODO: this isn't necessarily optimal if the direct conformance isn't
  // concretely available; we really ought to be comparing the full paths
  // to this conformance from concrete sources.

  auto signature = environment->getGenericSignature()->getCanonicalSignature();
  auto archetypeDepType = archetype->getInterfaceType();

  auto astPath = signature->getConformanceAccessPath(archetypeDepType,
                                                     protocol);

  auto i = astPath.begin(), e = astPath.end();
  assert(i != e && "empty path!");

  // The first entry in the path is a direct requirement of the signature,
  // for which we should always have local type data available.
  CanType rootArchetype =
    environment->mapTypeIntoContext(i->first)->getCanonicalType();
  ProtocolDecl *rootProtocol = i->second;

  // Turn the rest of the path into a MetadataPath.
  auto lastProtocol = rootProtocol;
  MetadataPath path;
  while (++i != e) {
    auto &entry = *i;
    CanType depType = CanType(entry.first);
    ProtocolDecl *requirement = entry.second;

    const ProtocolInfo &lastPI =
        IGF.IGM.getProtocolInfo(lastProtocol,
                                ProtocolInfoKind::RequirementSignature);

    // If it's a type parameter, it's self, and this is a base protocol
    // requirement.
    if (isa<GenericTypeParamType>(depType)) {
      assert(depType->isEqual(lastProtocol->getSelfInterfaceType()));
      WitnessIndex index = lastPI.getBaseIndex(requirement);
      path.addInheritedProtocolComponent(index);

    // Otherwise, it's an associated conformance requirement.
    } else {
      AssociatedConformance association(lastProtocol, depType, requirement);
      WitnessIndex index = lastPI.getAssociatedConformanceIndex(association);
      path.addAssociatedConformanceComponent(index);
    }

    lastProtocol = requirement;
  }
  assert(lastProtocol == protocol);

  auto rootWTable = IGF.tryGetLocalTypeData(rootArchetype,
              LocalTypeDataKind::forAbstractProtocolWitnessTable(rootProtocol));
  assert(rootWTable && "root witness table not bound in local context!");

  wtable = path.followFromWitnessTable(IGF, rootArchetype,
                                       ProtocolConformanceRef(rootProtocol),
                                       MetadataResponse::forComplete(rootWTable),
                                       /*request*/ MetadataState::Complete,
                                       nullptr).getMetadata();

  return wtable;
}