Skip to content

Associated type resilience #19266

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 11 commits into from
Sep 16, 2018
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
4 changes: 4 additions & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ types where the metadata itself has unknown layout.)
global ::= type generic-signature 'TH' // key path equality
global ::= type generic-signature 'Th' // key path hasher

global ::= protocol 'TL' // protocol requirements base descriptor
global ::= assoc-type-name 'Tl' // associated type descriptor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the same Tq suffix we use for method descriptors? There won't be a conflict and you'll be able to reuse l for other things

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about conformances? Are we just going to say you can't add new conformance requirements after the fact, and existing conformances have a canonical ordering?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't re-use Tq because IIUC the mangling grammar is usually not resolved this way: the suffix always (?) tells you what grammar to expect before it. Now, I am stealing several of the thunk mangling (for the requirements base, associated type, and possibly associated conformance descriptors), so perhaps I should move out to three-letter discriminators because there aren't that many of these.

Associated conformances are interesting. We need to be able to add new conformances for new defaulted associated types, but we can't add any to existing associated types. Introducing associated conformance descriptors would be the simplest way of ensuring that the addition of new requirements doesn't affect existing clients, so I was planning to investigate that next.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If new conformances are only ever added to new associated types, you might be able to get away with not giving them their own descriptors no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can find a way to consistently put them at the end, we wouldn't need descriptors for associated conformances. But that gets back to the "ABI history database" issue.



REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk helper function
REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk

Expand Down
6 changes: 3 additions & 3 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -2501,9 +2501,9 @@ class TargetGenericParamRef {
/// The protocol the associated type belongs to.
RelativeIndirectablePointer<TargetProtocolDescriptor<Runtime>,
/*nullable*/ false> Protocol;
/// The index of the associated type metadata within a witness table for
/// the protocol.
unsigned Index;
/// A reference to the associated type descriptor within the protocol.
RelativeIndirectablePointer<TargetProtocolRequirement<Runtime>,
/*nullable*/ false> Requirement;
};

/// A forward iterator that walks through the associated type path, which is
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ NODE(GenericTypeParamDecl)
NODE(CurryThunk)
NODE(DispatchThunk)
NODE(MethodDescriptor)
NODE(ProtocolRequirementsBaseDescriptor)
NODE(AssociatedTypeDescriptor)
NODE(ThrowsAnnotation)
NODE(EmptyList)
NODE(FirstElementMarker)
Expand Down
34 changes: 31 additions & 3 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ class LinkEntity {
/// The pointer is a ProtocolDecl*.
ProtocolDescriptor,

/// The alias referring to the base of the requirements within the
/// protocol descriptor, which is used to determine the offset of a
/// particular requirement in the witness table.
/// The pointer is a ProtocolDecl*.
ProtocolRequirementsBaseDescriptor,

/// An descriptor for an associated type within a protocol, which
/// will alias the TargetProtocolRequirement descripting this
/// particular associated type.
/// The pointer is an AssociatedTypeDecl*.
AssociatedTypeDescriptor,

/// A SIL function. The pointer is a SILFunction*.
SILFunction,

Expand Down Expand Up @@ -307,7 +319,7 @@ class LinkEntity {
}

static bool isDeclKind(Kind k) {
return k <= Kind::ProtocolDescriptor;
return k <= Kind::AssociatedTypeDescriptor;
}
static bool isTypeKind(Kind k) {
return k >= Kind::ProtocolWitnessTableLazyAccessFunction;
Expand Down Expand Up @@ -634,6 +646,12 @@ class LinkEntity {
return entity;
}

static LinkEntity forProtocolRequirementsBaseDescriptor(ProtocolDecl *decl) {
LinkEntity entity;
entity.setForDecl(Kind::ProtocolRequirementsBaseDescriptor, decl);
return entity;
}

static LinkEntity forValueWitness(CanType concreteType, ValueWitness witness) {
LinkEntity entity;
entity.Pointer = concreteType.getPointer();
Expand Down Expand Up @@ -728,6 +746,13 @@ class LinkEntity {
return entity;
}

static LinkEntity
forAssociatedTypeDescriptor(AssociatedTypeDecl *assocType) {
LinkEntity entity;
entity.setForDecl(Kind::AssociatedTypeDescriptor, assocType);
return entity;
}

static LinkEntity
forAssociatedTypeMetadataAccessFunction(const ProtocolConformance *C,
AssociatedType association) {
Expand Down Expand Up @@ -824,9 +849,12 @@ class LinkEntity {
}

AssociatedTypeDecl *getAssociatedType() const {
assert(getKind() == Kind::AssociatedTypeMetadataAccessFunction);
return getAssociatedTypeByIndex(getProtocolConformance(),
if (getKind() == Kind::AssociatedTypeMetadataAccessFunction)
return getAssociatedTypeByIndex(getProtocolConformance(),
LINKENTITY_GET_FIELD(Data, AssociatedTypeIndex));

assert(getKind() == Kind::AssociatedTypeDescriptor);
return reinterpret_cast<AssociatedTypeDecl *>(Pointer);
}

std::pair<CanType, ProtocolDecl *> getAssociatedConformance() const {
Expand Down
42 changes: 9 additions & 33 deletions include/swift/SIL/SILWitnessVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,6 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
// The protocol conformance descriptor gets added first.
asDerived().addProtocolConformanceDescriptor();

// Associated types get added after the inherited conformances, but
// before all the function requirements.
bool haveAddedAssociatedTypes = false;
auto addAssociatedTypes = [&] {
if (haveAddedAssociatedTypes) return;
haveAddedAssociatedTypes = true;

SmallVector<AssociatedTypeDecl *, 2> associatedTypes;
for (Decl *member : protocol->getMembers()) {
if (auto associatedType = dyn_cast<AssociatedTypeDecl>(member)) {
// If this is a new associated type (which does not override an
// existing associated type), add it.
if (associatedType->getOverriddenDecls().empty())
associatedTypes.push_back(associatedType);
}
}

// Sort associated types by name, for resilience.
llvm::array_pod_sort(associatedTypes.begin(), associatedTypes.end(),
TypeDecl::compare);

for (auto *associatedType : associatedTypes) {
asDerived().addAssociatedType(AssociatedType(associatedType));
}
};

for (const auto &reqt : protocol->getRequirementSignature()) {
switch (reqt.getKind()) {
// These requirements don't show up in the witness table.
Expand All @@ -101,17 +75,12 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
// come before any protocol requirements on associated types.
if (auto parameter = dyn_cast<GenericTypeParamType>(type)) {
assert(type->isEqual(protocol->getSelfInterfaceType()));
assert(!haveAddedAssociatedTypes &&
"unexpected ordering of conformances");
assert(parameter->getDepth() == 0 && parameter->getIndex() == 0 &&
"non-self type parameter in protocol");
asDerived().addOutOfLineBaseProtocol(requirement);
continue;
}

// Add the associated types if we haven't yet.
addAssociatedTypes();

// Otherwise, add an associated requirement.
AssociatedConformance assocConf(protocol, type, requirement);
asDerived().addAssociatedConformance(assocConf);
Expand All @@ -121,8 +90,15 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
llvm_unreachable("bad requirement kind");
}

// Add the associated types if we haven't yet.
addAssociatedTypes();
// Add the associated types.
for (Decl *member : protocol->getMembers()) {
if (auto associatedType = dyn_cast<AssociatedTypeDecl>(member)) {
// If this is a new associated type (which does not override an
// existing associated type), add it.
if (associatedType->getOverriddenDecls().empty())
asDerived().addAssociatedType(AssociatedType(associatedType));
}
}

if (asDerived().shouldVisitRequirementSignatureOnly())
return;
Expand Down
12 changes: 12 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1812,6 +1812,18 @@ NodePointer Demangler::demangleThunkOrSpecialization() {
}
return result;
}
case 'l': {
auto assocTypeName = popAssocTypeName();
if (!assocTypeName)
return nullptr;

return createWithChild(Node::Kind::AssociatedTypeDescriptor,
assocTypeName);
}
case 'L':
return createWithChild(Node::Kind::ProtocolRequirementsBaseDescriptor,
popProtocol());

case 'H':
case 'h': {
auto nodeKind = c == 'H' ? Node::Kind::KeyPathEqualsThunkHelper
Expand Down
10 changes: 10 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ class NodePrinter {
case Node::Kind::ProtocolListWithClass:
case Node::Kind::Allocator:
case Node::Kind::ArgumentTuple:
case Node::Kind::AssociatedTypeDescriptor:
case Node::Kind::AssociatedTypeMetadataAccessor:
case Node::Kind::AssociatedTypeWitnessTableAccessor:
case Node::Kind::AutoClosureType:
Expand Down Expand Up @@ -406,6 +407,7 @@ class NodePrinter {
case Node::Kind::ProtocolConformance:
case Node::Kind::ProtocolConformanceDescriptor:
case Node::Kind::ProtocolDescriptor:
case Node::Kind::ProtocolRequirementsBaseDescriptor:
case Node::Kind::ProtocolWitness:
case Node::Kind::ProtocolWitnessTable:
case Node::Kind::ProtocolWitnessTableAccessor:
Expand Down Expand Up @@ -1516,6 +1518,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << "protocol descriptor for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::ProtocolRequirementsBaseDescriptor:
Printer << "protocol requirements base descriptor for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::FullTypeMetadata:
Printer << "full type metadata for ";
print(Node->getChild(0));
Expand Down Expand Up @@ -1548,6 +1554,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << "lazy cache variable for type metadata for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::AssociatedTypeDescriptor:
Printer << "associated type descriptor for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::AssociatedTypeMetadataAccessor:
Printer << "associated type metadata accessor for ";
print(Node->getChild(1));
Expand Down
8 changes: 8 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ void Remangler::mangleProtocolDescriptor(Node *node) {
mangleProtocolWithoutPrefix(node->begin()[0]);
}

void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node) {
Out << "<protocol-requirements-base-descriptor>";
}

void Remangler::mangleProtocolWitnessTablePattern(Node *node) {
unreachable("todo");
}
Expand Down Expand Up @@ -876,6 +880,10 @@ void Remangler::mangleLazyProtocolWitnessTableCacheVariable(Node *node) {
mangleChildNodes(node); // type, protocol conformance
}

void Remangler::mangleAssociatedTypeDescriptor(Node *node) {
Out << "<associated-type-descriptor>";
}

void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) {
Out << "Wt";
mangleChildNodes(node); // protocol conformance, identifier
Expand Down
10 changes: 10 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,11 @@ void Remangler::mangleAssociatedTypeRef(Node *node) {
addSubstitution(entry);
}

void Remangler::mangleAssociatedTypeDescriptor(Node *node) {
mangleChildNodes(node);
Buffer << "Tl";
}

void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) {
mangleChildNodes(node); // protocol conformance, identifier
Buffer << "Wt";
Expand Down Expand Up @@ -1609,6 +1614,11 @@ void Remangler::mangleProtocolDescriptor(Node *node) {
Buffer << "Mp";
}

void Remangler::mangleProtocolRequirementsBaseDescriptor(Node *node) {
manglePureProtocol(getSingleChild(node));
Buffer << "TL";
}

void Remangler::mangleProtocolConformanceDescriptor(Node *node) {
mangleProtocolConformance(node->getChild(0));
Buffer << "Mc";
Expand Down
46 changes: 37 additions & 9 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,15 +907,13 @@ llvm::Constant *IRGenModule::getAddrOfAssociatedTypeGenericParamRef(
auto proto = getConstantReferenceForProtocolDescriptor(
assocType->getProtocol());
B.addRelativeAddress(proto);

// Find the offset of the associated type entry in witness tables of this
// protocol.
auto &protoInfo = getProtocolInfo(assocType->getProtocol(),
ProtocolInfoKind::RequirementSignature);
auto index = protoInfo.getAssociatedTypeIndex(AssociatedType(assocType))
.getValue();

B.addInt32(index);

// Add a reference to the associated type descriptor.
auto assocTypeDescriptor =
getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forAssociatedTypeDescriptor(assocType),
Alignment(4), ProtocolRequirementStructTy);
B.addRelativeAddress(assocTypeDescriptor);
}

// Null terminator.
Expand Down Expand Up @@ -3359,6 +3357,36 @@ llvm::Constant *IRGenModule::getAddrOfProtocolDescriptor(ProtocolDecl *D,
ProtocolDescriptorStructTy, DebugTypeInfo());
}

llvm::Constant *IRGenModule::getAddrOfProtocolRequirementsBaseDescriptor(
ProtocolDecl *proto) {
auto entity = LinkEntity::forProtocolRequirementsBaseDescriptor(proto);
return getAddrOfLLVMVariable(entity, getPointerAlignment(), ConstantInit(),
ProtocolRequirementStructTy,
DebugTypeInfo());
}

llvm::GlobalValue *IRGenModule::defineProtocolRequirementsBaseDescriptor(
ProtocolDecl *proto,
llvm::Constant *definition) {
auto entity = LinkEntity::forProtocolRequirementsBaseDescriptor(proto);
return defineAlias(entity, definition);
}

llvm::Constant *IRGenModule::getAddrOfAssociatedTypeDescriptor(
AssociatedTypeDecl *assocType) {
auto entity = LinkEntity::forAssociatedTypeDescriptor(assocType);
return getAddrOfLLVMVariable(entity, getPointerAlignment(), ConstantInit(),
ProtocolRequirementStructTy,
DebugTypeInfo());
}

llvm::GlobalValue *IRGenModule::defineAssociatedTypeDescriptor(
AssociatedTypeDecl *assocType,
llvm::Constant *definition) {
auto entity = LinkEntity::forAssociatedTypeDescriptor(assocType);
return defineAlias(entity, definition);
}

llvm::Constant *IRGenModule::getAddrOfProtocolConformanceDescriptor(
const NormalProtocolConformance *conformance,
ConstantInit definition) {
Expand Down
41 changes: 34 additions & 7 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,14 +667,41 @@ namespace {
B.fillPlaceholderWithInt(*NumRequirements, IGM.Int32Ty,
pi.getNumWitnesses());

if (pi.getNumWitnesses() > 0) {
// Define the protocol requirements "base" descriptor, which references
// the beginning of the protocol requirements, offset so that
// subtracting this address from the address of a given protocol
// requirements gives the corresponding offset into the witness
// table.
auto address =
B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy);
int offset = WitnessTableFirstRequirementOffset;
auto firstReqAdjustment = llvm::ConstantInt::get(IGM.Int32Ty, -offset);
address = llvm::ConstantExpr::getGetElementPtr(nullptr, address,
firstReqAdjustment);

IGM.defineProtocolRequirementsBaseDescriptor(Proto, address);
}

for (auto &entry : pi.getWitnessEntries()) {
if (Resilient && entry.isFunction()) {
// Define the method descriptor.
SILDeclRef func(entry.getFunction());
auto *descriptor =
B.getAddrOfCurrentPosition(
IGM.ProtocolRequirementStructTy);
IGM.defineMethodDescriptor(func, Proto, descriptor);
if (Resilient) {
if (entry.isFunction()) {
// Define the method descriptor.
SILDeclRef func(entry.getFunction());
auto *descriptor =
B.getAddrOfCurrentPosition(
IGM.ProtocolRequirementStructTy);
IGM.defineMethodDescriptor(func, Proto, descriptor);
}
}

if (entry.isAssociatedType()) {
auto assocType = entry.getAssociatedType();
// Define the associated type descriptor to point to the current
// position in the protocol descriptor.
IGM.defineAssociatedTypeDescriptor(
assocType,
B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy));
}

auto reqt = B.beginStruct(IGM.ProtocolRequirementStructTy);
Expand Down
Loading