Skip to content

Commit 1a3637c

Browse files
authored
Merge pull request #21073 from rjmccall/error-self-conformance-5.0
[5.0] Allow Error to conform to itself
2 parents 21f3a50 + 7fea4b8 commit 1a3637c

File tree

14 files changed

+535
-93
lines changed

14 files changed

+535
-93
lines changed

include/swift/AST/Decl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3975,6 +3975,9 @@ class ProtocolDecl final : public NominalTypeDecl {
39753975
->existentialConformsToSelfSlow();
39763976
}
39773977

3978+
/// Does this protocol require a self-conformance witness table?
3979+
bool requiresSelfConformanceWitnessTable() const;
3980+
39783981
/// Find direct Self references within the given requirement.
39793982
///
39803983
/// \param allowCovariantParameters If true, 'Self' is assumed to be

include/swift/AST/Module.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ class ModuleDecl : public DeclContext, public TypeDecl {
385385
Optional<ProtocolConformanceRef>
386386
lookupConformance(Type type, ProtocolDecl *protocol);
387387

388+
/// Look for the conformance of the given existential type to the given
389+
/// protocol.
390+
Optional<ProtocolConformanceRef>
391+
lookupExistentialConformance(Type type, ProtocolDecl *protocol);
392+
388393
/// Find a member named \p name in \p container that was declared in this
389394
/// module.
390395
///

lib/AST/Decl.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3863,15 +3863,22 @@ bool ProtocolDecl::requiresClassSlow() {
38633863
return Bits.ProtocolDecl.RequiresClass;
38643864
}
38653865

3866+
bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {
3867+
return isSpecificProtocol(KnownProtocolKind::Error);
3868+
}
3869+
38663870
bool ProtocolDecl::existentialConformsToSelfSlow() {
38673871
// Assume for now that the existential conforms to itself; this
38683872
// prevents circularity issues.
38693873
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
38703874
Bits.ProtocolDecl.ExistentialConformsToSelf = true;
38713875

3876+
// If it's not @objc, it conforms to itself only if it has a
3877+
// self-conformance witness table.
38723878
if (!isObjC()) {
3873-
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
3874-
return false;
3879+
bool hasSelfConformance = requiresSelfConformanceWitnessTable();
3880+
Bits.ProtocolDecl.ExistentialConformsToSelf = hasSelfConformance;
3881+
return hasSelfConformance;
38753882
}
38763883

38773884
// Check whether this protocol conforms to itself.

lib/AST/Module.cpp

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,66 @@ void ModuleDecl::getDisplayDecls(SmallVectorImpl<Decl*> &Results) const {
570570
FORWARD(getDisplayDecls, (Results));
571571
}
572572

573+
Optional<ProtocolConformanceRef>
574+
ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
575+
assert(type->isExistentialType());
576+
577+
// If the existential type cannot be represented or the protocol does not
578+
// conform to itself, there's no point in looking further.
579+
if (!protocol->existentialConformsToSelf())
580+
return None;
581+
582+
auto layout = type->getExistentialLayout();
583+
584+
// Due to an IRGen limitation, witness tables cannot be passed from an
585+
// existential to an archetype parameter, so for now we restrict this to
586+
// @objc protocols.
587+
if (!layout.isObjC()) {
588+
// There's a specific exception for protocols with self-conforming
589+
// witness tables, but the existential has to be *exactly* that type.
590+
// TODO: synthesize witness tables on-demand for protocol compositions
591+
// that can satisfy the requirement.
592+
if (protocol->requiresSelfConformanceWitnessTable() &&
593+
type->is<ProtocolType>() &&
594+
type->castTo<ProtocolType>()->getDecl() == protocol)
595+
return ProtocolConformanceRef(protocol);
596+
597+
return None;
598+
}
599+
600+
// If the existential is class-constrained, the class might conform
601+
// concretely.
602+
if (auto superclass = layout.explicitSuperclass) {
603+
if (auto result = lookupConformance(superclass, protocol))
604+
return result;
605+
}
606+
607+
// Otherwise, the existential might conform abstractly.
608+
for (auto proto : layout.getProtocols()) {
609+
auto *protoDecl = proto->getDecl();
610+
611+
// If we found the protocol we're looking for, return an abstract
612+
// conformance to it.
613+
if (protoDecl == protocol)
614+
return ProtocolConformanceRef(protocol);
615+
616+
// If the protocol has a superclass constraint, we might conform
617+
// concretely.
618+
if (auto superclass = protoDecl->getSuperclass()) {
619+
if (auto result = lookupConformance(superclass, protocol))
620+
return result;
621+
}
622+
623+
// Now check refined protocols.
624+
if (protoDecl->inheritsFrom(protocol))
625+
return ProtocolConformanceRef(protocol);
626+
}
627+
628+
// We didn't find our protocol in the existential's list; it doesn't
629+
// conform.
630+
return None;
631+
}
632+
573633
Optional<ProtocolConformanceRef>
574634
ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
575635
ASTContext &ctx = getASTContext();
@@ -609,52 +669,8 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
609669
// An existential conforms to a protocol if the protocol is listed in the
610670
// existential's list of conformances and the existential conforms to
611671
// itself.
612-
if (type->isExistentialType()) {
613-
// If the existential type cannot be represented or the protocol does not
614-
// conform to itself, there's no point in looking further.
615-
if (!protocol->existentialConformsToSelf())
616-
return None;
617-
618-
auto layout = type->getExistentialLayout();
619-
620-
// Due to an IRGen limitation, witness tables cannot be passed from an
621-
// existential to an archetype parameter, so for now we restrict this to
622-
// @objc protocols.
623-
if (!layout.isObjC())
624-
return None;
625-
626-
// If the existential is class-constrained, the class might conform
627-
// concretely.
628-
if (auto superclass = layout.explicitSuperclass) {
629-
if (auto result = lookupConformance(superclass, protocol))
630-
return result;
631-
}
632-
633-
// Otherwise, the existential might conform abstractly.
634-
for (auto proto : layout.getProtocols()) {
635-
auto *protoDecl = proto->getDecl();
636-
637-
// If we found the protocol we're looking for, return an abstract
638-
// conformance to it.
639-
if (protoDecl == protocol)
640-
return ProtocolConformanceRef(protocol);
641-
642-
// If the protocol has a superclass constraint, we might conform
643-
// concretely.
644-
if (auto superclass = protoDecl->getSuperclass()) {
645-
if (auto result = lookupConformance(superclass, protocol))
646-
return result;
647-
}
648-
649-
// Now check refined protocols.
650-
if (protoDecl->inheritsFrom(protocol))
651-
return ProtocolConformanceRef(protocol);
652-
}
653-
654-
// We didn't find our protocol in the existential's list; it doesn't
655-
// conform.
656-
return None;
657-
}
672+
if (type->isExistentialType())
673+
return lookupExistentialConformance(type, protocol);
658674

659675
// Type variables have trivial conformances.
660676
if (type->isTypeVariableOrMember())
@@ -669,7 +685,7 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
669685
auto nominal = type->getAnyNominal();
670686

671687
// If we don't have a nominal type, there are no conformances.
672-
if (!nominal) return None;
688+
if (!nominal || isa<ProtocolDecl>(nominal)) return None;
673689

674690
// Find the (unspecialized) conformance.
675691
SmallVector<ProtocolConformance *, 2> conformances;

lib/AST/ProtocolConformance.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,16 @@ ProtocolConformanceRef::subst(Type origType,
112112
if (substType->isOpenedExistential())
113113
return *this;
114114

115-
// If the substituted type is an existential, we have a self-conforming
116-
// existential being substituted in place of itself. There's no
117-
// conformance information in this case, so just return.
118-
if (substType->isObjCExistentialType())
119-
return *this;
120-
121115
auto *proto = getRequirement();
122116

117+
// If the type is an existential, it must be self-conforming.
118+
if (substType->isExistentialType()) {
119+
auto optConformance =
120+
proto->getModuleContext()->lookupExistentialConformance(substType, proto);
121+
assert(optConformance && "existential type didn't self-conform");
122+
return *optConformance;
123+
}
124+
123125
// Check the conformance map.
124126
if (auto result = conformances(origType->getCanonicalType(),
125127
substType, proto)) {
@@ -1407,9 +1409,12 @@ DeclContext::getLocalConformances(
14071409
if (!nominal)
14081410
return result;
14091411

1410-
// Protocols don't have conformances.
1411-
if (isa<ProtocolDecl>(nominal))
1412+
// Protocols only have self-conformances.
1413+
if (auto protocol = dyn_cast<ProtocolDecl>(nominal)) {
1414+
if (protocol->requiresSelfConformanceWitnessTable())
1415+
return { protocol->getASTContext().getSelfConformance(protocol) };
14121416
return { };
1417+
}
14131418

14141419
// Update to record all potential conformances.
14151420
nominal->prepareConformanceTable();

lib/IRGen/GenProto.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,16 +2847,23 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
28472847
// If we don't have concrete conformance information, the type must be
28482848
// an archetype and the conformance must be via one of the protocol
28492849
// requirements of the archetype. Look at what's locally bound.
2850+
ProtocolConformance *concreteConformance;
28502851
if (conformance.isAbstract()) {
2851-
auto archetype = cast<ArchetypeType>(srcType);
2852-
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
2853-
}
2852+
if (auto archetype = dyn_cast<ArchetypeType>(srcType))
2853+
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
2854+
2855+
// Otherwise, this must be a self-conformance.
2856+
assert(proto->requiresSelfConformanceWitnessTable());
2857+
assert(cast<ProtocolType>(srcType)->getDecl() == proto);
2858+
concreteConformance = IGF.IGM.Context.getSelfConformance(proto);
28542859

28552860
// All other source types should be concrete enough that we have
28562861
// conformance info for them. However, that conformance info might be
28572862
// more concrete than we're expecting.
28582863
// TODO: make a best effort to devirtualize, maybe?
2859-
auto concreteConformance = conformance.getConcrete();
2864+
} else {
2865+
concreteConformance = conformance.getConcrete();
2866+
}
28602867
assert(concreteConformance->getProtocol() == proto);
28612868

28622869
auto cacheKind =

lib/SILGen/SILGen.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
307307
/// Emit the default witness table for a resilient protocol.
308308
void emitDefaultWitnessTable(ProtocolDecl *protocol);
309309

310+
/// Emit the self-conformance witness table for a protocol.
311+
void emitSelfConformanceWitnessTable(ProtocolDecl *protocol);
312+
310313
/// Emit the lazy initializer function for a global pattern binding
311314
/// declaration.
312315
SILFunction *emitLazyGlobalInitializer(StringRef funcName,

lib/SILGen/SILGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
651651
SubstitutionMap reqtSubs,
652652
SILDeclRef witness,
653653
SubstitutionMap witnessSubs,
654-
IsFreeFunctionWitness_t isFree);
654+
IsFreeFunctionWitness_t isFree,
655+
bool isSelfConformance);
655656

656657
/// Convert a block to a native function with a thunk.
657658
ManagedValue emitBlockToFunc(SILLocation loc,

0 commit comments

Comments
 (0)