Skip to content

[Mangling] Introduce mangling for protocol conformances. #20230

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
42 changes: 37 additions & 5 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ The following symbolic reference kinds are currently implemented:
protocol-conformance-ref ::= '\x03' .{4} // Reference points directly to protocol conformance descriptor (NOT IMPLEMENTED)
protocol-conformance-ref ::= '\x04' .{4} // Reference points indirectly to protocol conformance descriptor (NOT IMPLEMENTED)

dependent-associated-conformance ::= '\x05' .{4} // Reference points directly to associated conformance descriptor (NOT IMPLEMENTED)
dependent-associated-conformance ::= '\x06' .{4} // Reference points indirectly to associated conformance descriptor (NOT IMPLEMENTED)

Globals
~~~~~~~
Expand Down Expand Up @@ -619,13 +621,43 @@ Property behaviors are implemented using private protocol conformances.

::

concrete-protocol-conformance ::= type protocol-conformance-ref
concrete-protocol-conformance ::= type protocol-conformance-ref any-protocol-conformance-list 'HC'
protocol-conformance-ref ::= protocol module?

any-protocol-conformance ::= concrete-protocol-conformance
any-protocol-conformance ::= dependent-protocol-conformance

any-protocol-conformance-list ::= any-protocol-conformance '_' any-protocol-conformance-list
any-protocol-conformance-list ::= empty-list

DEPENDENT-CONFORMANCE-INDEX ::= INDEX

dependent-protocol-conformance ::= type protocol 'HD' DEPENDENT-CONFORMANCE-INDEX
dependent-protocol-conformance ::= dependent-protocol-conformance protocol 'HI' DEPENDENT-CONFORMANCE-INDEX
dependent-protocol-conformance ::= dependent-protocol-conformance
dependent-associated-conformance 'HA' DEPENDENT-CONFORMANCE-INDEX

dependent-associated-conformance ::= type protocol

A compact representation used to represent mangled protocol conformance witness
arguments at runtime. The ``module`` is only specified for conformances that
are "retroactive", meaning that the context in which the conformance is defined
is in neither the protocol or type module.
arguments at runtime. The ``module`` is only specified for conformances that are
"retroactive", meaning that the context in which the conformance is defined is
in neither the protocol or type module. The concrete protocol conformances that
follow are for the conditional conformance requirements.

Dependent protocol conformances mangle the access path required to extract a
protocol conformance from some conformance passed into the environment. The
first case (operator "HD") is the leaf requirement, containing a dependent type
and the protocol it conforms to. The remaining dependent protocol conformance
manglings describe lookups performed on their child dependent protocol
conformances. The "HI" operator retrieves the named inherited protocol from the
witness table produced by the child. The "HA" operator refers to an associated
conformance within the witness table, identified by the dependent type and
protocol. In all cases, the DEPENDENT-CONFORMANCE-INDEX is an INDEX value
indicating the position of the appropriate value within the generic environment
(for "HD") or witness table (for "HI" and "HA") when it is known to be at a
fixed position. A position of zero is used to indicate "unknown"; all other
values are adjusted by 1.

::

Expand Down Expand Up @@ -685,7 +717,7 @@ from any character in a ``<GENERIC-PARAM-COUNT>``.

::

retroactive-conformance ::= protocol-conformance 'g' INDEX
retroactive-conformance ::= any-protocol-conformance 'g' INDEX

When a protocol conformance used to satisfy one of a bound generic type's
generic requirements is retroactive (i.e., it is specified in a module other
Expand Down
7 changes: 6 additions & 1 deletion include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class NamedDecl;
namespace swift {

class AbstractClosureExpr;
class ConformanceAccessPath;

namespace Mangle {

Expand Down Expand Up @@ -299,7 +300,11 @@ class ASTMangler : public Mangler {
void appendEntity(const ValueDecl *decl);

void appendProtocolConformance(const ProtocolConformance *conformance);

void appendProtocolConformanceRef(
const NormalProtocolConformance *conformance);
void appendConcreteProtocolConformance(
const ProtocolConformance *conformance);
void appendDependentProtocolConformance(const ConformanceAccessPath &path);
void appendOpParamForLayoutConstraint(LayoutConstraint Layout);

void appendSymbolicReference(SymbolicReferent referent);
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

CONTEXT_NODE(Allocator)
CONTEXT_NODE(AnonymousContext)
NODE(AnyProtocolConformanceList)
NODE(ArgumentTuple)
NODE(AssociatedType)
NODE(AssociatedTypeRef)
Expand All @@ -44,11 +45,13 @@ NODE(BuiltinTypeName)
NODE(CFunctionPointer)
CONTEXT_NODE(Class)
NODE(ClassMetadataBaseOffset)
NODE(ConcreteProtocolConformance)
CONTEXT_NODE(Constructor)
NODE(CoroutineContinuationPrototype)
CONTEXT_NODE(Deallocator)
NODE(DeclContext)
CONTEXT_NODE(DefaultArgumentInitializer)
NODE(DependentAssociatedConformance)
NODE(DependentAssociatedTypeRef)
NODE(DependentGenericConformanceRequirement)
NODE(DependentGenericParamCount)
Expand All @@ -59,6 +62,9 @@ NODE(DependentGenericSignature)
NODE(DependentGenericType)
NODE(DependentMemberType)
NODE(DependentPseudogenericSignature)
NODE(DependentProtocolConformanceRoot)
NODE(DependentProtocolConformanceInherited)
NODE(DependentProtocolConformanceAssociated)
CONTEXT_NODE(Destructor)
CONTEXT_NODE(DidSet)
NODE(Directness)
Expand Down Expand Up @@ -146,6 +152,7 @@ NODE(PropertyDescriptor)
CONTEXT_NODE(Protocol)
CONTEXT_NODE(ProtocolSymbolicReference)
NODE(ProtocolConformance)
NODE(ProtocolConformanceRef)
NODE(ProtocolDescriptor)
NODE(ProtocolConformanceDescriptor)
NODE(ProtocolList)
Expand Down
9 changes: 9 additions & 0 deletions include/swift/Demangling/Demangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ class Demangler : public NodeFactory {
NodePointer demangleBoundGenericArgs(NodePointer nominalType,
const Vector<NodePointer> &TypeLists,
size_t TypeListIdx);
NodePointer popAnyProtocolConformanceList();
NodePointer demangleRetroactiveConformance();
NodePointer demangleInitializer();
NodePointer demangleImplParamConvention();
Expand All @@ -457,6 +458,14 @@ class Demangler : public NodeFactory {
NodePointer getDependentGenericParamType(int depth, int index);
NodePointer demangleGenericParamIndex();
NodePointer popProtocolConformance();
NodePointer popProtocolConformanceRef();
NodePointer popAnyProtocolConformance();
NodePointer demangleConcreteProtocolConformance();
NodePointer popDependentProtocolConformance();
NodePointer demangleDependentProtocolConformanceRoot();
NodePointer demangleDependentProtocolConformanceInherited();
NodePointer popDependentAssociatedConformance();
NodePointer demangleDependentProtocolConformanceAssociated();
NodePointer demangleThunkOrSpecialization();
NodePointer demangleGenericSpecialization(Node::Kind SpecKind);
NodePointer demangleFunctionSpecialization();
Expand Down
194 changes: 180 additions & 14 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,48 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) {
}
}

/// Determine whether the given protocol conformance is itself retroactive,
/// meaning that there might be multiple conflicting conformances of the
/// same type to the same protocol.
static bool isRetroactiveConformance(
const NormalProtocolConformance *conformance) {
/// Non-retroactive conformances are... never retroactive.
if (!conformance->isRetroactive())
return false;

/// Synthesized non-unique conformances all get collapsed together at run
/// time.
if (conformance->isSynthesizedNonUnique())
return false;

/// Objective-C protocol conformances don't have identity.
if (conformance->getProtocol()->isObjC())
return false;

return true;
}

/// Determine whether the given protocol conformance contains a retroactive
/// protocol conformance anywhere in it.
static bool containsRetroactiveConformance(
const ProtocolConformance *conformance,
ModuleDecl *module) {
// If the root conformance is retroactive, it's retroactive.
if (isRetroactiveConformance(conformance->getRootNormalConformance()))
return true;

// If any of the substitutions used to form this conformance are retroactive,
// it's retroactive.
auto subMap = conformance->getSubstitutions(module);
for (auto conformance : subMap.getConformances()) {
if (conformance.isConcrete() &&
containsRetroactiveConformance(conformance.getConcrete(), module))
return true;
}

return false;
}

void ASTMangler::appendRetroactiveConformances(Type type) {
auto nominal = type->getAnyNominal();
if (!nominal) return;
Expand All @@ -1124,28 +1166,20 @@ void ASTMangler::appendRetroactiveConformances(Type type) {
if (subMap.empty()) return;

unsigned numProtocolRequirements = 0;
for (const auto &req: genericSig->getRequirements()) {
if (req.getKind() != RequirementKind::Conformance)
continue;

for (auto conformance : subMap.getConformances()) {
SWIFT_DEFER {
++numProtocolRequirements;
};

// Fast path: we're in the module of the protocol.
auto proto = req.getSecondType()->castTo<ProtocolType>()->getDecl();
if (proto->getModuleContext() == module)
// Ignore abstract conformances.
if (!conformance.isConcrete())
continue;

auto conformance =
subMap.lookupConformance(req.getFirstType()->getCanonicalType(), proto);
if (!conformance || !conformance->isConcrete()) continue;

auto normal = conformance->getConcrete()->getRootNormalConformance();
if (!normal->isRetroactive() || normal->isSynthesizedNonUnique())
// Skip non-retroactive conformances.
if (!containsRetroactiveConformance(conformance.getConcrete(), module))
continue;

appendProtocolConformance(normal);
appendConcreteProtocolConformance(conformance.getConcrete());
appendOperator("g", Index(numProtocolRequirements));
}
}
Expand Down Expand Up @@ -2240,6 +2274,138 @@ ASTMangler::appendProtocolConformance(const ProtocolConformance *conformance) {
}
}

void ASTMangler::appendProtocolConformanceRef(
const NormalProtocolConformance *conformance) {
// FIXME: Symbolic reference to the protocol conformance descriptor.
appendProtocolName(conformance->getProtocol());

// For retroactive conformances, add a reference to the module in which the
// conformance resides. For @objc protocols, there is no point: conformances
// are global anyway.
if (conformance->isRetroactive() && !conformance->isSynthesizedNonUnique() &&
!conformance->getProtocol()->isObjC())
appendModule(conformance->getDeclContext()->getParentModule());
}

/// Retrieve the index of the conformance requirement indicated by the
/// conformance access path entry within the given set of requirements.
static unsigned conformanceRequirementIndex(
const ConformanceAccessPath::Entry &entry,
ArrayRef<Requirement> requirements) {
unsigned result = 0;
for (const auto &req : requirements) {
if (req.getKind() != RequirementKind::Conformance)
continue;

if (req.getFirstType()->isEqual(entry.first) &&
req.getSecondType()->castTo<ProtocolType>()->getDecl() == entry.second)
return result;

++result;
}

llvm_unreachable("Conformance access path step is missing from requirements");
}

void ASTMangler::appendDependentProtocolConformance(
const ConformanceAccessPath &path) {
ProtocolDecl *currentProtocol = nullptr;
for (const auto &entry : path) {
// After each step, update the current protocol to refer to where we
// are.
SWIFT_DEFER {
currentProtocol = entry.second;
};

// The first entry is the "root". Find this requirement in the generic
// signature.
if (!currentProtocol) {
appendType(entry.first);
appendProtocolName(entry.second);
auto index =
conformanceRequirementIndex(entry,
CurGenericSignature->getRequirements());
appendOperator("HD", index + 1);
continue;
}

// Conformances are relative to the current protocol's requirement
// signature.
auto index =
conformanceRequirementIndex(entry,
currentProtocol->getRequirementSignature());

// Inherited conformance.
bool isInheritedConformance =
entry.first->isEqual(currentProtocol->getProtocolSelfType());
if (isInheritedConformance) {
appendProtocolName(entry.second);
appendOperator("HI", index + 1);
continue;
}

// Associated conformance.
// FIXME: Symbolic reference.
appendType(entry.first);
appendProtocolName(entry.second);

// For non-resilient protocols, encode the index.
bool isResilient =
currentProtocol->isResilient(Mod, ResilienceExpansion::Maximal);
appendOperator("HA", isResilient ? 0 : index + 1);
}
}

void ASTMangler::appendConcreteProtocolConformance(
const ProtocolConformance *conformance) {
auto module = conformance->getDeclContext()->getParentModule();

// Conforming type.
Type conformingType = conformance->getType();
if (conformingType->hasArchetype())
conformingType = conformingType->mapTypeOutOfContext();
appendType(conformingType->getCanonicalType());

// Protocol conformance reference.
appendProtocolConformanceRef(conformance->getRootNormalConformance());

// Conditional conformance requirements.
bool firstRequirement = true;
for (const auto &conditionalReq : conformance->getConditionalRequirements()) {
switch (conditionalReq.getKind()) {
case RequirementKind::Layout:
case RequirementKind::SameType:
case RequirementKind::Superclass:
continue;

case RequirementKind::Conformance: {
auto type = conditionalReq.getFirstType();
if (type->hasArchetype())
type = type->mapTypeOutOfContext();
CanType canType = type->getCanonicalType(CurGenericSignature);
auto proto =
conditionalReq.getSecondType()->castTo<ProtocolType>()->getDecl();
if (canType->isTypeParameter()) {
assert(CurGenericSignature &&
"Need a generic signature to resolve conformance");
auto conformanceAccessPath =
CurGenericSignature->getConformanceAccessPath(type, proto);
appendDependentProtocolConformance(conformanceAccessPath);
} else {
auto conditionalConf = module->lookupConformance(canType, proto);
appendConcreteProtocolConformance(conditionalConf->getConcrete());
}
appendListSeparator(firstRequirement);
break;
}
}
}
if (firstRequirement)
appendOperator("y");

appendOperator("HC");
}

void ASTMangler::appendOpParamForLayoutConstraint(LayoutConstraint layout) {
assert(layout);
switch (layout->getKind()) {
Expand Down
14 changes: 13 additions & 1 deletion lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,19 @@ bool NormalProtocolConformance::isRetroactive() const {
// If the conformance occurs in the same module as the conforming type
// definition, this is not a retroactive conformance.
if (auto nominal = getType()->getAnyNominal()) {
if (module == nominal->getParentModule())
auto nominalModule = nominal->getParentModule();

// Consider the overlay module to be the "home" of a nominal type
// defined in a Clang module.
if (auto nominalClangModule =
dyn_cast<ClangModuleUnit>(nominal->getModuleScopeContext())) {
if (auto clangLoader = nominal->getASTContext().getClangModuleLoader()) {
if (auto overlayModule = nominalClangModule->getAdapterModule())
nominalModule = overlayModule;
}
}

if (module == nominalModule)
return false;
}

Expand Down
Loading