Skip to content

[ABI] [Mangling] Only look for retroactive conformances in conditional reqs #21450

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 4 commits into from
Dec 21, 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
15 changes: 10 additions & 5 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,9 @@ Property behaviors are implemented using private protocol conformances.
::

concrete-protocol-conformance ::= type protocol-conformance-ref any-protocol-conformance-list 'HC'
protocol-conformance-ref ::= protocol module? 'HP'
protocol-conformance-ref ::= protocol 'HP' // same module as conforming type
protocol-conformance-ref ::= protocol 'Hp' // same module as protocol
protocol-conformance-ref ::= protocol module // "retroactive"

any-protocol-conformance ::= concrete-protocol-conformance
any-protocol-conformance ::= dependent-protocol-conformance
Expand All @@ -651,10 +653,13 @@ Property behaviors are implemented using private protocol conformances.
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. The concrete protocol conformances that
follow are for the conditional conformance requirements.
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. For a non-retroactive conformance
where both the type *and* the protocol are in the same module, or for
synthesized conformances that have no owning module, the "HP" operator is
preferred. 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
Expand Down
4 changes: 3 additions & 1 deletion include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ NODE(PropertyDescriptor)
CONTEXT_NODE(Protocol)
CONTEXT_NODE(ProtocolSymbolicReference)
NODE(ProtocolConformance)
NODE(ProtocolConformanceRef)
NODE(ProtocolConformanceRefInTypeModule)
NODE(ProtocolConformanceRefInProtocolModule)
NODE(ProtocolConformanceRefInOtherModule)
NODE(ProtocolDescriptor)
NODE(ProtocolConformanceDescriptor)
NODE(ProtocolList)
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Demangling/Demangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ class Demangler : public NodeFactory {
NodePointer getDependentGenericParamType(int depth, int index);
NodePointer demangleGenericParamIndex();
NodePointer popProtocolConformance();
NodePointer demangleProtocolConformanceRef();
NodePointer demangleRetroactiveProtocolConformanceRef();
NodePointer popAnyProtocolConformance();
NodePointer demangleConcreteProtocolConformance();
NodePointer popDependentProtocolConformance();
Expand Down
74 changes: 51 additions & 23 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1105,48 +1105,69 @@ 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 RootProtocolConformance *root) {
static bool conformanceHasIdentity(const RootProtocolConformance *root) {
auto conformance = dyn_cast<NormalProtocolConformance>(root);
if (!conformance) {
assert(isa<SelfProtocolConformance>(root));
return false; // self-conformances are never retroactive.
return true;
}

/// Non-retroactive conformances are... never retroactive.
if (!conformance->isRetroactive())
return false;

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

/// Objective-C protocol conformances don't have identity.
// Objective-C protocol conformances are checked by the ObjC runtime.
if (conformance->getProtocol()->isObjC())
return false;

return true;
}

/// 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 RootProtocolConformance *root) {
auto conformance = dyn_cast<NormalProtocolConformance>(root);
if (!conformance) {
assert(isa<SelfProtocolConformance>(root));
return false; // self-conformances are never retroactive.
}

return conformance->isRetroactive();
}

/// 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->getRootConformance()))
const RootProtocolConformance *rootConformance =
conformance->getRootConformance();
if (isRetroactiveConformance(rootConformance) &&
conformanceHasIdentity(rootConformance))
return true;

// If any of the substitutions used to form this conformance are retroactive,
// it's retroactive.
// If the conformance is conditional and any of the substitutions used to
// satisfy the conditions are retroactive, it's retroactive.
auto subMap = conformance->getSubstitutions(module);
for (auto conformance : subMap.getConformances()) {
if (conformance.isConcrete() &&
containsRetroactiveConformance(conformance.getConcrete(), module))
for (auto requirement : rootConformance->getConditionalRequirements()) {
if (requirement.getKind() != RequirementKind::Conformance)
continue;
ProtocolDecl *proto =
requirement.getSecondType()->castTo<ProtocolType>()->getDecl();
Optional<ProtocolConformanceRef> conformance =
subMap.lookupConformance(requirement.getFirstType()->getCanonicalType(),
proto);
if (!conformance) {
// This should only happen when mangling invalid ASTs, but that happens
// for indexing purposes.
continue;
}
if (conformance->isConcrete() &&
containsRetroactiveConformance(conformance->getConcrete(), module)) {
return true;
}
}

return false;
Expand Down Expand Up @@ -2278,12 +2299,19 @@ void ASTMangler::appendProtocolConformanceRef(
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 (isRetroactiveConformance(conformance))
// conformance resides. Otherwise, use an operator to indicate which known
// module it's associated with.
if (!conformanceHasIdentity(conformance)) {
// Same as "conformance module matches type", below.
appendOperator("HP");
} else if (isRetroactiveConformance(conformance)) {
appendModule(conformance->getDeclContext()->getParentModule());

appendOperator("HP");
} else if (conformance->getDeclContext()->getParentModule() ==
conformance->getType()->getAnyNominal()->getParentModule()) {
appendOperator("HP");
} else {
appendOperator("Hp");
}
}

/// Retrieve the index of the conformance requirement indicated by the
Expand Down
28 changes: 19 additions & 9 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,12 @@ NodePointer Demangler::demangleOperator() {
case 'C': return demangleConcreteProtocolConformance();
case 'D': return demangleDependentProtocolConformanceRoot();
case 'I': return demangleDependentProtocolConformanceInherited();
case 'P': return demangleProtocolConformanceRef();
case 'P':
return createWithChild(
Node::Kind::ProtocolConformanceRefInTypeModule, popProtocol());
case 'p':
return createWithChild(
Node::Kind::ProtocolConformanceRefInProtocolModule, popProtocol());
default:
pushBack();
pushBack();
Expand Down Expand Up @@ -1295,22 +1300,27 @@ NodePointer Demangler::popAnyProtocolConformance() {
});
}

NodePointer Demangler::demangleProtocolConformanceRef() {
NodePointer Demangler::demangleRetroactiveProtocolConformanceRef() {
NodePointer module = popModule();
NodePointer proto = popProtocol();
auto protocolConformanceRef =
createWithChild(Node::Kind::ProtocolConformanceRef, proto);

// The module is optional, present only for retroactive conformances. Add it
// as the second child.
if (protocolConformanceRef && module)
protocolConformanceRef->addChild(module, *this);
createWithChildren(Node::Kind::ProtocolConformanceRefInOtherModule,
proto, module);
return protocolConformanceRef;
}

NodePointer Demangler::demangleConcreteProtocolConformance() {
NodePointer conditionalConformanceList = popAnyProtocolConformanceList();
NodePointer conformanceRef = popNode(Node::Kind::ProtocolConformanceRef);

NodePointer conformanceRef =
popNode(Node::Kind::ProtocolConformanceRefInTypeModule);
if (!conformanceRef) {
conformanceRef =
popNode(Node::Kind::ProtocolConformanceRefInProtocolModule);
}
if (!conformanceRef)
conformanceRef = demangleRetroactiveProtocolConformanceRef();

NodePointer type = popNode(Node::Kind::Type);
return createWithChildren(Node::Kind::ConcreteProtocolConformance,
type, conformanceRef, conditionalConformanceList);
Expand Down
15 changes: 13 additions & 2 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,9 @@ class NodePrinter {
case Node::Kind::DependentProtocolConformanceAssociated:
case Node::Kind::DependentProtocolConformanceInherited:
case Node::Kind::DependentProtocolConformanceRoot:
case Node::Kind::ProtocolConformanceRef:
case Node::Kind::ProtocolConformanceRefInTypeModule:
case Node::Kind::ProtocolConformanceRefInProtocolModule:
case Node::Kind::ProtocolConformanceRefInOtherModule:
case Node::Kind::DynamicallyReplaceableFunctionKey:
case Node::Kind::DynamicallyReplaceableFunctionImpl:
case Node::Kind::DynamicallyReplaceableFunctionVar:
Expand Down Expand Up @@ -2172,7 +2174,16 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
<< " ";
printChildren(Node);
return nullptr;
case Node::Kind::ProtocolConformanceRef:
case Node::Kind::ProtocolConformanceRefInTypeModule:
Printer << "protocol conformance ref (type's module) ";
printChildren(Node);
return nullptr;
case Node::Kind::ProtocolConformanceRefInProtocolModule:
Printer << "protocol conformance ref (protocol's module) ";
printChildren(Node);
return nullptr;
case Node::Kind::ProtocolConformanceRefInOtherModule:
Printer << "protocol conformance ref (retroactive) ";
printChildren(Node);
return nullptr;
}
Expand Down
10 changes: 9 additions & 1 deletion lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,15 @@ void Remangler::mangleRetroactiveConformance(Node *node) {
unreachable("Retroactive conformances aren't in the old mangling");
}

void Remangler::mangleProtocolConformanceRef(Node *node) {
void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node) {
unreachable("Protocol conformance references aren't in the old mangling");
}

void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node) {
unreachable("Protocol conformance references aren't in the old mangling");
}

void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node) {
unreachable("Protocol conformance references aren't in the old mangling");
}

Expand Down
16 changes: 12 additions & 4 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1655,16 +1655,24 @@ void Remangler::mangleProtocolConformance(Node *node) {
mangle(GenSig);
}

void Remangler::mangleProtocolConformanceRef(Node *node) {
void Remangler::mangleProtocolConformanceRefInTypeModule(Node *node) {
manglePureProtocol(node->getChild(0));
if (node->getNumChildren() > 1)
mangleChildNode(node, 1);
Buffer << "HP";
}

void Remangler::mangleProtocolConformanceRefInProtocolModule(Node *node) {
manglePureProtocol(node->getChild(0));
Buffer << "Hp";
}

void Remangler::mangleProtocolConformanceRefInOtherModule(Node *node) {
manglePureProtocol(node->getChild(0));
mangleChildNode(node, 1);
}

void Remangler::mangleConcreteProtocolConformance(Node *node) {
mangleType(node->getChild(0));
mangleProtocolConformanceRef(node->getChild(1));
mangle(node->getChild(1));
if (node->getNumChildren() > 2)
mangleAnyProtocolConformanceList(node->getChild(2));
else
Expand Down
4 changes: 3 additions & 1 deletion test/Demangle/Inputs/manglings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@ _T0So13GenericOptionas8HashableSCsACP9hashValueSivgTW ---> {T:} protocol witness
_T0So11CrappyColorVs16RawRepresentableSCMA ---> reflection metadata associated type descriptor __C.CrappyColor : Swift.RawRepresentable in __C_Synthesized
$S28protocol_conformance_records15NativeValueTypeVAA8RuncibleAAMc ---> protocol conformance descriptor for protocol_conformance_records.NativeValueType : protocol_conformance_records.Runcible in protocol_conformance_records
$SSC9SomeErrorLeVD ---> __C_Synthesized.related decl 'e' for SomeError
$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PAAHPyHCg_AiJ1QAAHPyHCg1_GF ---> mangling_retroactive.test0(mangling_retroactive.Z<RetroactiveB.X, Swift.Int, RetroactiveB.Y>) -> ()
$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PAAyHCg_AiJ1QAAyHCg1_GF ---> mangling_retroactive.test0(mangling_retroactive.Z<RetroactiveB.X, Swift.Int, RetroactiveB.Y>) -> ()
$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PHPyHCg_AiJ1QHPyHCg1_GF ---> mangling_retroactive.test0(mangling_retroactive.Z<RetroactiveB.X, Swift.Int, RetroactiveB.Y>) -> ()
$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PHpyHCg_AiJ1QHpyHCg1_GF ---> mangling_retroactive.test0(mangling_retroactive.Z<RetroactiveB.X, Swift.Int, RetroactiveB.Y>) -> ()
_T0LiteralAByxGxd_tcfC ---> _T0LiteralAByxGxd_tcfC
_T0XZ ---> _T0XZ
_TTSf0os___TFVs17_LegacyStringCore15_invariantCheckfT_T_ ---> function signature specialization <Arg[0] = Guaranteed To Owned and Exploded> of Swift._LegacyStringCore._invariantCheck() -> ()
Expand Down
53 changes: 50 additions & 3 deletions test/SILGen/mangling_retroactive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ struct Z<T: P, U: Hashable, V: Q> { }
extension X: P { } // retroactive
extension Y: Q { } // retroactive

// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PAAHPyHCg_AiJ1QAAHPyHCg1_GF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test0yyAA1ZVy12RetroactiveB1XVSiAE1YVAG0D1A1PAAyHCg_AiJ1QAAyHCg1_GF
func test0(_: Z<X, Int, Y>) { }

struct Z2<T: P> {
struct Inner<V: Q> { }
}

// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test1yyAA2Z2V5InnerVy12RetroactiveB1XV_AG1YVAI0F1A1PAAHPyHCg_AkL1QAAHPyHCg0_GF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test1yyAA2Z2V5InnerVy12RetroactiveB1XV_AG1YVAI0F1A1PAAyHCg_AkL1QAAyHCg0_GF
func test1(_: Z2<X>.Inner<Y>) { }

extension X: Hashable {
Expand All @@ -38,5 +38,52 @@ extension Z: Equatable where T: Hashable, V: Equatable {
struct RequiresEquatable<T: Equatable> { }

// Conditional requirement involves retroactive conformances.
// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test2yyAA17RequiresEquatableVyAA1ZVy12RetroactiveB1XVSiAG1YVAI0F1A1PAAHPyHCg_AkL1QAAHPyHCg1_GAOSQHPAISHAAHPyHC_AKSQAAHPyHCHCg_GF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive5test2yyAA17RequiresEquatableVyAA1ZVy12RetroactiveB1XVSiAG1YVAI0F1A1PAAyHCg_AkL1QAAyHCg1_GAOSQHPAISHAAyHC_AKSQAAyHCHCg_GF
func test2(_: RequiresEquatable<Z<X, Int, Y>>) { }

struct UnconditionallyP<T: Q>: P {}
struct RequiresP<T: P> {}

// RequiresP uses a non-retroactive conformance for its generic param
// UnconditionallyP, even though UnconditionallyP's generic param uses a
// retroactive conformance to conform to Q.
func rdar46735592(_: RequiresP<UnconditionallyP<Y>>) { }
// CHECK: sil hidden [ossa] @$s20mangling_retroactive12rdar46735592yyAA9RequiresPVyAA16UnconditionallyPVy12RetroactiveB1YVAI0F1A1QAAyHCg_GGF

struct QImpl: Q {}
struct ConditionallyP<T> {}
extension ConditionallyP: P where T: Q {}

func useConditionallyP(_: RequiresP<ConditionallyP<QImpl>>) {}
func useConditionallyP_retroactive(_: RequiresP<ConditionallyP<Y>>) {}
// CHECK: sil hidden [ossa] @$s20mangling_retroactive17useConditionallyPyyAA9RequiresPVyAA0D1PVyAA5QImplVGGF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive018useConditionallyP_B0yyAA9RequiresPVyAA0D1PVy12RetroactiveB1YVGAJ0F1A1PHPAiK1QAAyHC_HCg_GF

protocol Wrapper {
associatedtype Wrapped
}
struct WrapperImpl<Wrapped>: Wrapper {}

struct IndirectlyConditionallyP<T: Wrapper> {}
extension IndirectlyConditionallyP: P where T.Wrapped: Q {}

func useIndirectlyConditionallyP(_: RequiresP<IndirectlyConditionallyP<WrapperImpl<QImpl>>>) {}
func useIndirectlyConditionallyP_retroactive(_: RequiresP<IndirectlyConditionallyP<WrapperImpl<Y>>>) {}
// CHECK: sil hidden [ossa] @$s20mangling_retroactive27useIndirectlyConditionallyPyyAA9RequiresPVyAA0dE1PVyAA11WrapperImplVyAA5QImplVGGGF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive028useIndirectlyConditionallyP_B0yyAA9RequiresPVyAA0dE1PVyAA11WrapperImplVy12RetroactiveB1YVGGAM0I1A1PHPAkN1QAAyHC_HCg_GF

struct IndirectlyConditionallyP2<T> {}
extension IndirectlyConditionallyP2: P where T: Wrapper, T.Wrapped: Q {}

func useIndirectlyConditionallyP2(_: RequiresP<IndirectlyConditionallyP<WrapperImpl<QImpl>>>) {}
func useIndirectlyConditionallyP2_retroactive(_: RequiresP<IndirectlyConditionallyP<WrapperImpl<Y>>>) {}
// CHECK: sil hidden [ossa] @$s20mangling_retroactive28useIndirectlyConditionallyP2yyAA9RequiresPVyAA0dE1PVyAA11WrapperImplVyAA5QImplVGGGF
// CHECK: sil hidden [ossa] @$s20mangling_retroactive029useIndirectlyConditionallyP2_B0yyAA9RequiresPVyAA0dE1PVyAA11WrapperImplVy12RetroactiveB1YVGGAM0J1A1PHPAkN1QAAyHC_HCg_GF

protocol NonRetroactive {}
extension Y: NonRetroactive {}
struct ConditionallyP2<T> {}
extension ConditionallyP2: P where T: Q, T: NonRetroactive {}

func useConditionallyP2(_: RequiresP<ConditionallyP2<Y>>) {}
// CHECK: sil hidden [ossa] @$s20mangling_retroactive18useConditionallyP2yyAA9RequiresPVyAA0dE0Vy12RetroactiveB1YVGAJ0G1A1PHPAiK1QAAyHC_AiA03NonG0HpyHCHCg_GF