Skip to content

[ABI] Associated conformance defaults #19391

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
3 changes: 2 additions & 1 deletion docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ types where the metadata itself has unknown layout.)
global ::= protocol 'TL' // protocol requirements base descriptor
global ::= assoc-type-name 'Tl' // associated type descriptor
global ::= assoc-type-name 'TM' // default associated type witness accessor

global ::= type assoc-type-path protocol 'Tn' // associated conformance descriptor
global ::= type assoc-type-path protocol 'TN' // default associated conformance witness accessor

REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk helper function
REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk
Expand Down
13 changes: 13 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3979,6 +3979,19 @@ class ProtocolDecl final : public NominalTypeDecl {
/// Record the default witness for a requirement.
void setDefaultWitness(ValueDecl *requirement, Witness witness);

/// Returns the default associated conformance witness for an associated
/// type, or \c None if there is no default.
Optional<ProtocolConformanceRef> getDefaultAssociatedConformanceWitness(
CanType association,
ProtocolDecl *requirement) const;

/// Set the default associated conformance witness for the given
/// associated conformance.
void setDefaultAssociatedConformanceWitness(
CanType association,
ProtocolDecl *requirement,
ProtocolConformanceRef conformance);

/// Retrieve the name to use for this protocol when interoperating
/// with the Objective-C runtime.
StringRef getObjCRuntimeName(llvm::SmallVectorImpl<char> &buffer) const;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,11 @@ ERROR(type_witness_objc_generic_parameter,none,
NOTE(witness_fix_access,none,
"mark the %0 as '%select{%error|fileprivate|internal|public|%error}1' to "
"satisfy the requirement", (DescriptiveDeclKind, AccessLevel))
WARNING(assoc_type_default_conformance_failed,none,
"default type %0 for associated type %1 does not satisfy constraint "
"%2: %3", (Type, DeclName, Type, Type))
NOTE(assoc_type_default_here,none,
"associated type %0 has default type %1 written here", (DeclName, Type))

ERROR(protocol_access,none,
"%select{protocol must be declared %select{"
Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ NODE(DispatchThunk)
NODE(MethodDescriptor)
NODE(ProtocolRequirementsBaseDescriptor)
NODE(AssociatedConformanceDescriptor)
NODE(DefaultAssociatedConformanceAccessor)
NODE(AssociatedTypeDescriptor)
NODE(ThrowsAnnotation)
NODE(EmptyList)
Expand Down
28 changes: 22 additions & 6 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ class LinkEntity {
/// is stored in the data.
AssociatedConformanceDescriptor,

/// A default accessor for an associated conformance of a protocol.
/// The pointer is a ProtocolDecl*; the index of the associated conformance
/// is stored in the data.
DefaultAssociatedConformanceAccessor,

/// 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 @@ -795,14 +800,13 @@ class LinkEntity {
}

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

Expand Down Expand Up @@ -835,6 +839,17 @@ class LinkEntity {
return entity;
}

static LinkEntity
forDefaultAssociatedConformanceAccessor(AssociatedConformance conformance) {
LinkEntity entity;
entity.setForProtocolAndAssociatedConformance(
Kind::DefaultAssociatedConformanceAccessor,
conformance.getSourceProtocol(),
conformance.getAssociation(),
conformance.getAssociatedRequirement());
return entity;
}

static LinkEntity forReflectionBuiltinDescriptor(CanType type) {
LinkEntity entity;
entity.setForType(Kind::ReflectionBuiltinDescriptor, type);
Expand Down Expand Up @@ -925,7 +940,8 @@ class LinkEntity {
LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex));
}

assert(getKind() == Kind::AssociatedConformanceDescriptor);
assert(getKind() == Kind::AssociatedConformanceDescriptor ||
getKind() == Kind::DefaultAssociatedConformanceAccessor);
return getAssociatedConformanceByIndex(
cast<ProtocolDecl>(getDecl()),
LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex));
Expand Down
31 changes: 31 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL)
llvm::DenseMap<std::pair<const ProtocolDecl *, AssociatedTypeDecl *>, Type>
DefaultTypeWitnesses;

/// Default associated conformance witnesses for protocols.
llvm::DenseMap<std::tuple<const ProtocolDecl *, CanType, ProtocolDecl *>,
ProtocolConformanceRef>
DefaultAssociatedConformanceWitnesses;

/// \brief Structure that captures data that is segregated into different
/// arenas.
struct Arena {
Expand Down Expand Up @@ -1709,6 +1714,32 @@ void ProtocolDecl::setDefaultTypeWitness(AssociatedTypeDecl *assocType,
(void)pair;
}

Optional<ProtocolConformanceRef>
ProtocolDecl::getDefaultAssociatedConformanceWitness(
CanType association,
ProtocolDecl *requirement) const {
auto &ctx = getASTContext();
auto found =
ctx.getImpl().DefaultAssociatedConformanceWitnesses.find(
std::make_tuple(this, association, requirement));
if (found == ctx.getImpl().DefaultAssociatedConformanceWitnesses.end())
return None;

return found->second;
}

void ProtocolDecl::setDefaultAssociatedConformanceWitness(
CanType association,
ProtocolDecl *requirement,
ProtocolConformanceRef conformance) {
auto &ctx = getASTContext();
auto pair = ctx.getImpl().DefaultAssociatedConformanceWitnesses.insert(
std::make_pair(std::make_tuple(this, association, requirement),
conformance));
assert(pair.second && "Already have a default associated conformance");
(void)pair;
}

bool ASTContext::canImportModule(std::pair<Identifier, SourceLoc> ModulePath) {
// If this module has already been successfully imported, it is importable.
if (getLoadedModule(ModulePath) != nullptr)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
};

if (Loc.isValid() &&
DC->getParentSourceFile() &&
DC->getParentSourceFile()->Kind != SourceFileKind::REPL &&
Ctx.LangOpts.EnableASTScopeLookup) {
// Find the source file in which we are performing the lookup.
Expand Down
9 changes: 9 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,15 @@ NodePointer Demangler::demangleThunkOrSpecialization() {
protoTy, assocTypePath, requirementTy);
}

case 'N': {
NodePointer requirementTy = popProtocol();
auto assocTypePath = popAssocTypePath();
NodePointer protoTy = popNode(Node::Kind::Type);
return createWithChildren(
Node::Kind::DefaultAssociatedConformanceAccessor,
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 @@ -323,6 +323,7 @@ class NodePrinter {
case Node::Kind::DeclContext:
case Node::Kind::DefaultArgumentInitializer:
case Node::Kind::DefaultAssociatedTypeMetadataAccessor:
case Node::Kind::DefaultAssociatedConformanceAccessor:
case Node::Kind::DependentAssociatedTypeRef:
case Node::Kind::DependentGenericSignature:
case Node::Kind::DependentGenericParamCount:
Expand Down Expand Up @@ -1564,6 +1565,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << ": ";
print(Node->getChild(2));
return nullptr;
case Node::Kind::DefaultAssociatedConformanceAccessor:
Printer << "default associated conformance accessor 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 @@ -888,6 +888,10 @@ void Remangler::mangleAssociatedConformanceDescriptor(Node *node) {
Out << "<associated-conformance-descriptor>";
}

void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) {
Out << "<default-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 @@ -579,6 +579,11 @@ void Remangler::mangleAssociatedConformanceDescriptor(Node *node) {
Buffer << "Tn";
}

void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) {
mangleChildNodes(node);
Buffer << "TN";
}

void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) {
mangleChildNodes(node); // protocol conformance, identifier
Buffer << "Wt";
Expand Down
33 changes: 23 additions & 10 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3389,21 +3389,15 @@ llvm::GlobalValue *IRGenModule::defineAssociatedTypeDescriptor(

llvm::Constant *IRGenModule::getAddrOfAssociatedConformanceDescriptor(
AssociatedConformance conformance) {
auto entity = LinkEntity::forAssociatedConformanceDescriptor(
conformance.getSourceProtocol(),
conformance.getAssociation(),
conformance.getAssociatedRequirement());
auto entity = LinkEntity::forAssociatedConformanceDescriptor(conformance);
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);
AssociatedConformance conformance,
llvm::Constant *definition) {
auto entity = LinkEntity::forAssociatedConformanceDescriptor(conformance);
return defineAlias(entity, definition);
}

Expand Down Expand Up @@ -3988,6 +3982,25 @@ IRGenModule::getAddrOfDefaultAssociatedTypeMetadataAccessFunction(
return entry;
}

llvm::Function *
IRGenModule::getAddrOfDefaultAssociatedConformanceAccessor(
AssociatedConformance requirement) {
auto forDefinition = ForDefinition;

LinkEntity entity =
LinkEntity::forDefaultAssociatedConformanceAccessor(requirement);
llvm::Function *&entry = GlobalFuncs[entity];
if (entry) {
if (forDefinition) updateLinkageForDefinition(*this, entry, entity);
return entry;
}

auto signature = getAssociatedTypeWitnessTableAccessFunctionSignature();
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
entry = createFunction(*this, link, signature);
return entry;
}

llvm::Function *
IRGenModule::getAddrOfContinuationPrototype(CanSILFunctionType fnType) {
LinkEntity entity = LinkEntity::forCoroutineContinuationPrototype(fnType);
Expand Down
100 changes: 96 additions & 4 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,14 @@ namespace {

if (entry.isAssociatedConformance()) {
auto flags = Flags(Flags::Kind::AssociatedConformanceAccessFunction);
return { flags, nullptr };

// Look for a default witness.
llvm::Constant *defaultImpl =
findDefaultAssociatedConformanceWitness(
entry.getAssociatedConformancePath(),
entry.getAssociatedConformanceRequirement());

return { flags, defaultImpl };
}

assert(entry.isFunction());
Expand Down Expand Up @@ -712,10 +719,12 @@ namespace {
if (entry.isAssociatedConformance()) {
// Define the associated conformance descriptor to point to the
// current position in the protocol descriptor.
AssociatedConformance conformance(
Proto,
entry.getAssociatedConformancePath(),
entry.getAssociatedConformanceRequirement());
IGM.defineAssociatedConformanceDescriptor(
Proto,
entry.getAssociatedConformancePath(),
entry.getAssociatedConformanceRequirement(),
conformance,
B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy));
}

Expand Down Expand Up @@ -800,6 +809,89 @@ namespace {
IGF.Builder.CreateRet(returnValue);
return accessor;
}

llvm::Constant *findDefaultAssociatedConformanceWitness(
CanType association,
ProtocolDecl *requirement) {
if (!DefaultWitnesses) return nullptr;

for (auto &entry : DefaultWitnesses->getEntries()) {
if (!entry.isValid() ||
entry.getKind() != SILWitnessTable::AssociatedTypeProtocol ||
entry.getAssociatedTypeProtocolWitness().Protocol != requirement ||
entry.getAssociatedTypeProtocolWitness().Requirement != association)
continue;

auto witness = entry.getAssociatedTypeProtocolWitness().Witness;
return getDefaultAssociatedConformanceAccessFunction(
AssociatedConformance(Proto, association, requirement),
witness);
}

return nullptr;
}

llvm::Constant *getDefaultAssociatedConformanceAccessFunction(
AssociatedConformance requirement,
ProtocolConformanceRef conformance) {
auto accessor =
IGM.getAddrOfDefaultAssociatedConformanceAccessor(requirement);

IRGenFunction IGF(IGM, accessor);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);

Explosion parameters = IGF.collectParameters();

llvm::Value *associatedTypeMetadata = parameters.claimNext();
llvm::Value *self = parameters.claimNext();
llvm::Value *wtable = parameters.claimNext();

bool hasArchetype =
!conformance.isConcrete() ||
conformance.getConcrete()->getType()->hasArchetype();
if (hasArchetype) {
// Bind local Self type data from the metadata argument.
CanType selfInContext =
Proto->mapTypeIntoContext(Proto->getProtocolSelfType())
->getCanonicalType();
IGF.bindLocalTypeDataFromTypeMetadata(selfInContext, IsExact, self,
MetadataState::Abstract);
IGF.setUnscopedLocalTypeData(
selfInContext,
LocalTypeDataKind::forAbstractProtocolWitnessTable(Proto),
wtable);

// Bind the associated type metadata.
IGF.bindLocalTypeDataFromTypeMetadata(requirement.getAssociation(),
IsExact,
associatedTypeMetadata,
MetadataState::Abstract);
}

// For a concrete witness table, call it.
ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement();
if (conformance.isConcrete()) {
auto conformanceI = &IGM.getConformanceInfo(associatedProtocol,
conformance.getConcrete());
auto returnValue = conformanceI->getTable(IGF, &associatedTypeMetadata);
IGF.Builder.CreateRet(returnValue);
return accessor;
}

// For an abstract table, emit a reference to the witness table.
CanType associatedTypeInContext
= Proto->mapTypeIntoContext(requirement.getAssociation())
->getCanonicalType();
auto returnValue =
emitArchetypeWitnessTableRef(
IGF,
cast<ArchetypeType>(associatedTypeInContext),
associatedProtocol);
IGF.Builder.CreateRet(returnValue);
return accessor;
}

void addAssociatedTypeNames() {
std::string AssociatedTypeNames;

Expand Down
Loading