Skip to content

[ABI] Associated conformance resilience #19358

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
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
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ NODE(CurryThunk)
NODE(DispatchThunk)
NODE(MethodDescriptor)
NODE(ProtocolRequirementsBaseDescriptor)
NODE(AssociatedConformanceDescriptor)
NODE(AssociatedTypeDescriptor)
NODE(ThrowsAnnotation)
NODE(EmptyList)
Expand Down
72 changes: 63 additions & 9 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ class LinkEntity {
/// The pointer is an AssociatedTypeDecl*.
AssociatedTypeDescriptor,

/// An descriptor for an associated conformance within a protocol, which
/// will alias the TargetProtocolRequirement descripting this
/// particular associated conformance.
/// The pointer is a ProtocolDecl*; the index of the associated conformance
/// is stored in the data.
AssociatedConformanceDescriptor,

/// A function which returns the default type metadata for the associated
/// type of a protocol. The secondary pointer is a ProtocolDecl*.
/// The index of the associated type declaration is stored in the data.
Expand Down Expand Up @@ -342,6 +349,21 @@ class LinkEntity {
Data = LINKENTITY_SET_FIELD(Kind, unsigned(kind));
}

void setForProtocolAndAssociatedConformance(Kind kind,
const ProtocolDecl *proto,
CanType associatedType,
ProtocolDecl *associatedProtocol){
assert(isDeclKind(kind));
Pointer = static_cast<ValueDecl *>(const_cast<ProtocolDecl *>(proto));
SecondaryPointer = nullptr;
Data = LINKENTITY_SET_FIELD(Kind, unsigned(kind)) |
LINKENTITY_SET_FIELD(AssociatedConformanceIndex,
getAssociatedConformanceIndex(
proto,
associatedType,
associatedProtocol));
}

void setForProtocolConformance(Kind kind, const ProtocolConformance *c) {
assert(isProtocolConformanceKind(kind) && !isTypeKind(kind));
Pointer = nullptr;
Expand Down Expand Up @@ -407,14 +429,12 @@ class LinkEntity {
}

// We store associated conformances using their index in the requirement
// list of the requirement signature of the conformance's protocol.
static unsigned getAssociatedConformanceIndex(
const ProtocolConformance *conformance,
// list of the requirement signature of the protocol.
static unsigned getAssociatedConformanceIndex(const ProtocolDecl *proto,
CanType associatedType,
ProtocolDecl *requirement) {
unsigned index = 0;
for (const auto &reqt :
conformance->getProtocol()->getRequirementSignature()) {
for (const auto &reqt : proto->getRequirementSignature()) {
if (reqt.getKind() == RequirementKind::Conformance &&
reqt.getFirstType()->getCanonicalType() == associatedType &&
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() ==
Expand All @@ -426,15 +446,31 @@ class LinkEntity {
llvm_unreachable("requirement not found in protocol");
}

// We store associated conformances using their index in the requirement
// list of the requirement signature of the conformance's protocol.
static unsigned getAssociatedConformanceIndex(
const ProtocolConformance *conformance,
CanType associatedType,
ProtocolDecl *requirement) {
return getAssociatedConformanceIndex(conformance->getProtocol(),
associatedType, requirement);
}

static std::pair<CanType, ProtocolDecl*>
getAssociatedConformanceByIndex(const ProtocolConformance *conformance,
getAssociatedConformanceByIndex(const ProtocolDecl *proto,
unsigned index) {
auto &reqt = conformance->getProtocol()->getRequirementSignature()[index];
auto &reqt = proto->getRequirementSignature()[index];
assert(reqt.getKind() == RequirementKind::Conformance);
return { reqt.getFirstType()->getCanonicalType(),
reqt.getSecondType()->castTo<ProtocolType>()->getDecl() };
}

static std::pair<CanType, ProtocolDecl*>
getAssociatedConformanceByIndex(const ProtocolConformance *conformance,
unsigned index) {
return getAssociatedConformanceByIndex(conformance->getProtocol(), index);
}

void setForType(Kind kind, CanType type) {
assert(isTypeKind(kind));
Pointer = type.getPointer();
Expand Down Expand Up @@ -758,6 +794,18 @@ class LinkEntity {
return entity;
}

static LinkEntity
forAssociatedConformanceDescriptor(ProtocolDecl *proto,
CanType associatedType,
ProtocolDecl *associatedProtocol) {
LinkEntity entity;
entity.setForProtocolAndAssociatedConformance(
Kind::AssociatedConformanceDescriptor,
proto, associatedType,
associatedProtocol);
return entity;
}

static LinkEntity
forAssociatedTypeMetadataAccessFunction(const ProtocolConformance *C,
AssociatedType association) {
Expand Down Expand Up @@ -872,9 +920,15 @@ class LinkEntity {
}

std::pair<CanType, ProtocolDecl *> getAssociatedConformance() const {
assert(getKind() == Kind::AssociatedTypeWitnessTableAccessFunction);
return getAssociatedConformanceByIndex(getProtocolConformance(),
if (getKind() == Kind::AssociatedTypeWitnessTableAccessFunction) {
return getAssociatedConformanceByIndex(getProtocolConformance(),
LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex));
}

assert(getKind() == Kind::AssociatedConformanceDescriptor);
return getAssociatedConformanceByIndex(
cast<ProtocolDecl>(getDecl()),
LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex));
}

ProtocolDecl *getAssociatedProtocol() const {
Expand Down
8 changes: 8 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,14 @@ NodePointer Demangler::demangleThunkOrSpecialization() {
return createWithChild(Node::Kind::DefaultAssociatedTypeMetadataAccessor,
popAssocTypeName());

case 'n': {
NodePointer requirementTy = popProtocol();
auto assocTypePath = popAssocTypePath();
NodePointer protoTy = popNode(Node::Kind::Type);
return createWithChildren(Node::Kind::AssociatedConformanceDescriptor,
protoTy, assocTypePath, requirementTy);
}

case 'H':
case 'h': {
auto nodeKind = c == 'H' ? Node::Kind::KeyPathEqualsThunkHelper
Expand Down
9 changes: 9 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::AssociatedConformanceDescriptor:
case Node::Kind::AssociatedTypeDescriptor:
case Node::Kind::AssociatedTypeMetadataAccessor:
case Node::Kind::AssociatedTypeWitnessTableAccessor:
Expand Down Expand Up @@ -1555,6 +1556,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << "lazy cache variable for type metadata for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::AssociatedConformanceDescriptor:
Printer << "associated conformance descriptor for ";
print(Node->getChild(0));
Printer << ".";
print(Node->getChild(1));
Printer << ": ";
print(Node->getChild(2));
return nullptr;
case Node::Kind::AssociatedTypeDescriptor:
Printer << "associated type descriptor for ";
print(Node->getChild(0));
Expand Down
4 changes: 4 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,10 @@ void Remangler::mangleAssociatedTypeDescriptor(Node *node) {
Out << "<associated-type-descriptor>";
}

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

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

void Remangler::mangleAssociatedConformanceDescriptor(Node *node) {
mangleChildNodes(node);
Buffer << "Tn";
}

void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) {
mangleChildNodes(node); // protocol conformance, identifier
Buffer << "Wt";
Expand Down
20 changes: 20 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3387,6 +3387,26 @@ llvm::GlobalValue *IRGenModule::defineAssociatedTypeDescriptor(
return defineAlias(entity, definition);
}

llvm::Constant *IRGenModule::getAddrOfAssociatedConformanceDescriptor(
AssociatedConformance conformance) {
auto entity = LinkEntity::forAssociatedConformanceDescriptor(
conformance.getSourceProtocol(),
conformance.getAssociation(),
conformance.getAssociatedRequirement());
return getAddrOfLLVMVariable(entity, getPointerAlignment(), ConstantInit(),
ProtocolRequirementStructTy, DebugTypeInfo());
}

llvm::GlobalValue *IRGenModule::defineAssociatedConformanceDescriptor(
ProtocolDecl *proto,
CanType subject,
ProtocolDecl *requirement,
llvm::Constant *definition) {
auto entity = LinkEntity::forAssociatedConformanceDescriptor(proto, subject,
requirement);
return defineAlias(entity, definition);
}

llvm::Constant *IRGenModule::getAddrOfProtocolConformanceDescriptor(
const NormalProtocolConformance *conformance,
ConstantInit definition) {
Expand Down
10 changes: 10 additions & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,16 @@ namespace {
B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy));
}

if (entry.isAssociatedConformance()) {
// Define the associated conformance descriptor to point to the
// current position in the protocol descriptor.
IGM.defineAssociatedConformanceDescriptor(
Proto,
entry.getAssociatedConformancePath(),
entry.getAssociatedConformanceRequirement(),
B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy));
}

auto reqt = B.beginStruct(IGM.ProtocolRequirementStructTy);

auto info = getRequirementInfo(entry);
Expand Down
41 changes: 29 additions & 12 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2500,10 +2500,30 @@ static llvm::Value *
emitAssociatedTypeWitnessTableRef(IRGenFunction &IGF,
llvm::Value *parentMetadata,
llvm::Value *wtable,
WitnessIndex index,
AssociatedConformance conformance,
llvm::Value *associatedTypeMetadata) {
llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, wtable,
index.forProtocolWitnessTable());
auto sourceProtocol = conformance.getSourceProtocol();
llvm::Value *witness;
if (IGF.IGM.isResilient(sourceProtocol, ResilienceExpansion::Maximal)) {
// For resilient protocols, use the associated conformance descriptor to
// determine the index.
auto assocConformanceDescriptor =
IGF.IGM.getAddrOfAssociatedConformanceDescriptor(conformance);

auto index =
computeResilientWitnessTableIndex(IGF, sourceProtocol,
assocConformanceDescriptor);

witness = emitInvariantLoadOfOpaqueWitness(IGF, wtable, index);
} else {
// For non-resilient protocols, the index is a constant.
auto &pi = IGF.IGM.getProtocolInfo(sourceProtocol,
ProtocolInfoKind::RequirementSignature);

auto index = pi.getAssociatedConformanceIndex(conformance);
witness = emitInvariantLoadOfOpaqueWitness(IGF, wtable,
index.forProtocolWitnessTable());
}

// Cast the witness to the appropriate function type.
auto sig = IGF.IGM.getAssociatedTypeWitnessTableAccessFunctionSignature();
Expand Down Expand Up @@ -2663,14 +2683,17 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,

if (!source) return MetadataResponse();

WitnessIndex index(component.getPrimaryIndex(), /*prefix*/ false);
auto sourceMetadata = IGF.emitTypeMetadataRef(sourceType);
auto associatedMetadata = IGF.emitTypeMetadataRef(sourceKey.Type);
auto sourceWTable = source.getMetadata();

AssociatedConformance associatedConformanceRef(sourceProtocol,
association,
associatedRequirement);
auto associatedWTable =
emitAssociatedTypeWitnessTableRef(IGF, sourceMetadata, sourceWTable,
index, associatedMetadata);
associatedConformanceRef,
associatedMetadata);

setProtocolWitnessTableName(IGF.IGM, associatedWTable, sourceKey.Type,
associatedRequirement);
Expand Down Expand Up @@ -3396,13 +3419,7 @@ Signature IRGenModule::getAssociatedTypeMetadataAccessFunctionSignature() {
return Signature(fnType, attrs, SwiftCC);
}

/// Compute the index into a witness table for a resilient protocol given
/// a reference to a descriptor of one of the requirements in that witness
/// table.
///
/// Given an index into the witness table for a resilient protocol that
/// was compiuted
static llvm::Value *computeResilientWitnessTableIndex(
llvm::Value *irgen::computeResilientWitnessTableIndex(
IRGenFunction &IGF,
ProtocolDecl *proto,
llvm::Constant *reqtDescriptor) {
Expand Down
8 changes: 8 additions & 0 deletions lib/IRGen/GenProto.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ namespace irgen {
SILDeclRef member,
ProtocolConformanceRef conformance);

/// Compute the index into a witness table for a resilient protocol given
/// a reference to a descriptor of one of the requirements in that witness
/// table.
llvm::Value *computeResilientWitnessTableIndex(
IRGenFunction &IGF,
ProtocolDecl *proto,
llvm::Constant *reqtDescriptor);

/// Given a type T and an associated type X of some protocol P to
/// which T conforms, return the type metadata for T.X.
///
Expand Down
20 changes: 13 additions & 7 deletions lib/IRGen/IRGenMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ class IRGenMangler : public Mangle::ASTMangler {
return finalize();
}

std::string mangleAssociatedConformanceDescriptor(
const ProtocolDecl *proto,
CanType subject,
const ProtocolDecl *requirement) {
beginMangling();
appendAnyGenericType(proto);
bool isFirstAssociatedTypeIdentifier = true;
appendAssociatedTypePath(subject, isFirstAssociatedTypeIdentifier);
appendProtocolName(requirement);
appendOperator("Tn");
return finalize();
}

std::string mangleProtocolConformanceDescriptor(
const ProtocolConformance *Conformance) {
beginMangling();
Expand Down Expand Up @@ -413,13 +426,6 @@ class IRGenMangler : public Mangle::ASTMangler {

std::string manglePartialApplyForwarder(StringRef FuncName);

std::string mangleForProtocolDescriptor(ProtocolType *Proto) {
beginMangling();
appendProtocolName(Proto->getDecl(), /*allowStandardSubstitution=*/false);
appendOperator("P");
return finalize();
}

std::string mangleTypeForForeignMetadataUniquing(Type type) {
return mangleTypeWithoutPrefix(type);
}
Expand Down
7 changes: 7 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1234,6 +1234,13 @@ private: \
llvm::GlobalValue *defineAssociatedTypeDescriptor(
AssociatedTypeDecl *assocType,
llvm::Constant *definition);
llvm::Constant *getAddrOfAssociatedConformanceDescriptor(
AssociatedConformance conformance);
llvm::GlobalValue *defineAssociatedConformanceDescriptor(
ProtocolDecl *proto,
CanType subject,
ProtocolDecl *requirement,
llvm::Constant *definition);

llvm::Constant *getAddrOfProtocolDescriptor(ProtocolDecl *D,
ConstantInit definition = ConstantInit());
Expand Down
Loading