Skip to content

IRGen: Use conformance access paths to find inherited conformances from opened existentials #22777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions lib/IRGen/GenArchetype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,31 +172,6 @@ llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF,
auto wtable = IGF.tryGetLocalTypeData(archetype, localDataKind);
if (wtable) return wtable;

// If we have an opened type, this must be an implied witness table
// reference.
// FIXME: eliminate this path when opened types have generic environments.
if (auto opened = dyn_cast<OpenedArchetypeType>(archetype)) {
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;
});
}

assert(isa<PrimaryArchetypeType>(archetype->getRoot())
&& "might not work yet for opened/opaque archetypes!");
auto environment = archetype->getGenericEnvironment();

// Otherwise, ask the generic signature for the environment for the best
Expand Down
154 changes: 0 additions & 154 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,150 +800,6 @@ namespace {

ArrayRef<WitnessTableEntry> getEntries() const { return Entries; }
};

/// A path through a protocol hierarchy.
class ProtocolPath {
IRGenModule &IGM;

/// The destination protocol.
ProtocolDecl *Dest;

/// The path from the selected origin down to the destination
/// protocol.
SmallVector<WitnessIndex, 8> ReversePath;

/// The origin index to use.
unsigned OriginIndex;

/// The best path length we found.
unsigned BestPathLength;

public:
/// Find a path from the given set of origins to the destination
/// protocol.
///
/// T needs to provide a couple of member functions:
/// ProtocolDecl *getProtocol() const;
/// const ProtocolInfo &getInfo() const;
template <class T>
ProtocolPath(IRGenModule &IGM, ArrayRef<T> origins, ProtocolDecl *dest)
: IGM(IGM), Dest(dest), BestPathLength(~0U) {

// Consider each of the origins in turn, breaking out if any of
// them yields a zero-length path.
for (unsigned i = 0, e = origins.size(); i != e; ++i) {
auto &origin = origins[i];
if (considerOrigin(origin.getProtocol(), origin.getInfo(), i))
break;
}

// Sanity check that we actually found a path at all.
assert(BestPathLength != ~0U);
assert(BestPathLength == ReversePath.size());
}

/// Returns the index of the origin protocol we chose.
unsigned getOriginIndex() const { return OriginIndex; }

/// Apply the path to the given witness table.
llvm::Value *apply(IRGenFunction &IGF, llvm::Value *wtable) const {
for (unsigned i = ReversePath.size(); i != 0; --i) {
wtable = emitInvariantLoadOfOpaqueWitness(IGF, wtable,
ReversePath[i-1].forProtocolWitnessTable());
wtable = IGF.Builder.CreateBitCast(wtable, IGF.IGM.WitnessTablePtrTy);
}
return wtable;
}

private:
/// Consider paths starting from a new origin protocol.
/// Returns true if there's no point in considering other origins.
bool considerOrigin(ProtocolDecl *origin, const ProtocolInfo &originInfo,
unsigned originIndex) {
assert(BestPathLength != 0);

// If the origin *is* the destination, we can stop here.
if (origin == Dest) {
OriginIndex = originIndex;
BestPathLength = 0;
ReversePath.clear();
return true;
}

// Otherwise, if the origin gives rise to a better path, that's
// also cool.
if (findBetterPath(origin, originInfo, 0)) {
OriginIndex = originIndex;
return BestPathLength == 0;
}

return false;
}

/// Consider paths starting at the given protocol.
bool findBetterPath(ProtocolDecl *proto, const ProtocolInfo &protoInfo,
unsigned lengthSoFar) {
assert(lengthSoFar < BestPathLength);
assert(proto != Dest);

// Keep track of whether we found a better path than the
// previous best.
bool foundBetter = false;
for (auto base : proto->getInheritedProtocols()) {
// ObjC protocols do not have witnesses.
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(base))
continue;

auto baseIndex = protoInfo.getBaseIndex(base);

// Compute the length down to this base.
unsigned lengthToBase = lengthSoFar;
if (!baseIndex.isPrefix()) {
lengthToBase++;

// Don't consider this path if we reach a length that can't
// possibly be better than the best so far.
if (lengthToBase == BestPathLength) continue;
}
assert(lengthToBase < BestPathLength);

// If this base *is* the destination, go ahead and start
// building the path into ReversePath.
if (base == Dest) {
// Reset the collected best-path information.
BestPathLength = lengthToBase;
ReversePath.clear();

// Otherwise, if there isn't a better path through this base,
// don't accumulate anything in the path.
} else {
const ProtocolInfo &baseInfo =
IGM.getProtocolInfo(base, ProtocolInfoKind::RequirementSignature);
if (!findBetterPath(base, baseInfo, lengthToBase))
continue;
}

// Okay, we've found a better path, and ReversePath contains a
// path leading from base to Dest.
assert(BestPathLength >= lengthToBase);
foundBetter = true;

// Add the link from proto to base if necessary.
if (!baseIndex.isPrefix()) {
ReversePath.push_back(baseIndex);

// If it isn't necessary, then we might be able to
// short-circuit considering the bases of this protocol.
} else {
if (lengthSoFar == BestPathLength)
return true;
}
}

return foundBetter;
}
};

} // end anonymous namespace

/// Return true if the witness table requires runtime instantiation to
Expand Down Expand Up @@ -2889,16 +2745,6 @@ void NecessaryBindings::addProtocolConformance(CanType type,
Requirements.insert({type, conf.getAbstract()});
}

llvm::Value *irgen::emitImpliedWitnessTableRef(IRGenFunction &IGF,
ArrayRef<ProtocolEntry> entries,
ProtocolDecl *target,
const GetWitnessTableFn &getWitnessTable) {
ProtocolPath path(IGF.IGM, entries, target);
auto wtable = getWitnessTable(path.getOriginIndex());
wtable = path.apply(IGF, wtable);
return wtable;
}

llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
CanType srcType,
ProtocolConformanceRef conformance) {
Expand Down
20 changes: 0 additions & 20 deletions lib/IRGen/GenProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,26 +177,6 @@ namespace irgen {
CanType srcType,
ProtocolConformanceRef conformance);

/// An entry in a list of known protocols.
class ProtocolEntry {
ProtocolDecl *Protocol;
const ProtocolInfo &Impl;

public:
explicit ProtocolEntry(ProtocolDecl *proto, const ProtocolInfo &impl)
: Protocol(proto), Impl(impl) {}

ProtocolDecl *getProtocol() const { return Protocol; }
const ProtocolInfo &getInfo() const { return Impl; }
};

using GetWitnessTableFn =
llvm::function_ref<llvm::Value*(unsigned originIndex)>;
llvm::Value *emitImpliedWitnessTableRef(IRGenFunction &IGF,
ArrayRef<ProtocolEntry> protos,
ProtocolDecl *target,
const GetWitnessTableFn &getWitnessTable);

class MetadataSource {
public:
enum class Kind {
Expand Down