Skip to content

Commit 7fea4b8

Browse files
committed
Allow Error to conform to itself.
Most of the foundation for this was laid in earlier patches.
1 parent 94b2dd8 commit 7fea4b8

File tree

13 files changed

+469
-41
lines changed

13 files changed

+469
-41
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

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: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,15 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
585585
// existential to an archetype parameter, so for now we restrict this to
586586
// @objc protocols.
587587
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+
588597
return None;
589598
}
590599

@@ -676,7 +685,7 @@ ModuleDecl::lookupConformance(Type type, ProtocolDecl *protocol) {
676685
auto nominal = type->getAnyNominal();
677686

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

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

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,9 +1409,12 @@ DeclContext::getLocalConformances(
14091409
if (!nominal)
14101410
return result;
14111411

1412-
// Protocols don't have conformances.
1413-
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) };
14141416
return { };
1417+
}
14151418

14161419
// Update to record all potential conformances.
14171420
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,

lib/SILGen/SILGenPoly.cpp

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3575,15 +3575,23 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived,
35753575
enum class WitnessDispatchKind {
35763576
Static,
35773577
Dynamic,
3578-
Class
3578+
Class,
3579+
Witness
35793580
};
35803581

3581-
static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness) {
3582+
static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness,
3583+
bool isSelfConformance) {
35823584
auto *decl = witness.getDecl();
35833585

3586+
if (isSelfConformance) {
3587+
assert(isa<ProtocolDecl>(decl->getDeclContext()));
3588+
return WitnessDispatchKind::Witness;
3589+
}
3590+
35843591
ClassDecl *C = decl->getDeclContext()->getSelfClassDecl();
3585-
if (!C)
3592+
if (!C) {
35863593
return WitnessDispatchKind::Static;
3594+
}
35873595

35883596
// If the witness is dynamic, go through dynamic dispatch.
35893597
if (decl->isObjCDynamic()) {
@@ -3630,6 +3638,7 @@ getWitnessFunctionType(SILGenModule &SGM,
36303638
switch (witnessKind) {
36313639
case WitnessDispatchKind::Static:
36323640
case WitnessDispatchKind::Dynamic:
3641+
case WitnessDispatchKind::Witness:
36333642
return SGM.Types.getConstantInfo(witness).SILFnType;
36343643
case WitnessDispatchKind::Class:
36353644
return SGM.Types.getConstantOverrideType(witness);
@@ -3638,18 +3647,36 @@ getWitnessFunctionType(SILGenModule &SGM,
36383647
llvm_unreachable("Unhandled WitnessDispatchKind in switch.");
36393648
}
36403649

3650+
static std::pair<CanType, ProtocolConformanceRef>
3651+
getSelfTypeAndConformanceForWitness(SILDeclRef witness, SubstitutionMap subs) {
3652+
auto protocol = cast<ProtocolDecl>(witness.getDecl()->getDeclContext());
3653+
auto selfParam = protocol->getProtocolSelfType()->getCanonicalType();
3654+
auto type = subs.getReplacementTypes()[0];
3655+
auto conf = *subs.lookupConformance(selfParam, protocol);
3656+
return {type->getCanonicalType(), conf};
3657+
}
3658+
36413659
static SILValue
36423660
getWitnessFunctionRef(SILGenFunction &SGF,
36433661
SILDeclRef witness,
36443662
CanSILFunctionType witnessFTy,
36453663
WitnessDispatchKind witnessKind,
3664+
SubstitutionMap witnessSubs,
36463665
SmallVectorImpl<ManagedValue> &witnessParams,
36473666
SILLocation loc) {
36483667
switch (witnessKind) {
36493668
case WitnessDispatchKind::Static:
36503669
return SGF.emitGlobalFunctionRef(loc, witness);
36513670
case WitnessDispatchKind::Dynamic:
36523671
return SGF.emitDynamicMethodRef(loc, witness, witnessFTy).getValue();
3672+
case WitnessDispatchKind::Witness: {
3673+
auto typeAndConf =
3674+
getSelfTypeAndConformanceForWitness(witness, witnessSubs);
3675+
return SGF.B.createWitnessMethod(loc, typeAndConf.first,
3676+
typeAndConf.second,
3677+
witness,
3678+
SILType::getPrimitiveObjectType(witnessFTy));
3679+
}
36533680
case WitnessDispatchKind::Class: {
36543681
SILValue selfPtr = witnessParams.back().getValue();
36553682
return SGF.emitClassMethodRef(loc, selfPtr, witness, witnessFTy);
@@ -3659,13 +3686,32 @@ getWitnessFunctionRef(SILGenFunction &SGF,
36593686
llvm_unreachable("Unhandled WitnessDispatchKind in switch.");
36603687
}
36613688

3689+
static ManagedValue
3690+
emitOpenExistentialInSelfConformance(SILGenFunction &SGF, SILLocation loc,
3691+
SILDeclRef witness,
3692+
SubstitutionMap subs, ManagedValue value,
3693+
SILParameterInfo destParameter) {
3694+
auto typeAndConf = getSelfTypeAndConformanceForWitness(witness, subs);
3695+
auto archetype = typeAndConf.first->castTo<ArchetypeType>();
3696+
assert(archetype->isOpenedExistential());
3697+
3698+
auto openedTy = destParameter.getSILStorageType();
3699+
auto state = SGF.emitOpenExistential(loc, value, archetype, openedTy,
3700+
destParameter.isIndirectMutating()
3701+
? AccessKind::ReadWrite
3702+
: AccessKind::Read);
3703+
3704+
return state.Value;
3705+
}
3706+
36623707
void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
36633708
CanAnyFunctionType reqtSubstTy,
36643709
SILDeclRef requirement,
36653710
SubstitutionMap reqtSubs,
36663711
SILDeclRef witness,
36673712
SubstitutionMap witnessSubs,
3668-
IsFreeFunctionWitness_t isFree) {
3713+
IsFreeFunctionWitness_t isFree,
3714+
bool isSelfConformance) {
36693715
// FIXME: Disable checks that the protocol witness carries debug info.
36703716
// Should we carry debug info for witnesses?
36713717
F.setBare(IsBare);
@@ -3679,7 +3725,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
36793725
FullExpr scope(Cleanups, cleanupLoc);
36803726
FormalEvaluationScope formalEvalScope(*this);
36813727

3682-
auto witnessKind = getWitnessDispatchKind(witness);
3728+
auto witnessKind = getWitnessDispatchKind(witness, isSelfConformance);
36833729
auto thunkTy = F.getLoweredFunctionType();
36843730

36853731
SmallVector<ManagedValue, 8> origParams;
@@ -3704,9 +3750,24 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
37043750
->getCanonicalType());
37053751
}
37063752

3753+
// Get the lowered type of the witness.
3754+
auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind);
3755+
auto witnessFTy = origWitnessFTy;
3756+
if (!witnessSubs.empty())
3757+
witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs);
3758+
37073759
auto reqtSubstParams = reqtSubstTy.getParams();
37083760
auto witnessSubstParams = witnessSubstTy.getParams();
37093761

3762+
// For a self-conformance, open the self parameter.
3763+
if (isSelfConformance) {
3764+
assert(!isFree && "shouldn't have a free witness for a self-conformance");
3765+
origParams.back() =
3766+
emitOpenExistentialInSelfConformance(*this, loc, witness, witnessSubs,
3767+
origParams.back(),
3768+
witnessFTy->getSelfParameter());
3769+
}
3770+
37103771
// For a free function witness, discard the 'self' parameter of the
37113772
// requirement.
37123773
if (isFree) {
@@ -3716,11 +3777,6 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
37163777

37173778
// Translate the argument values from the requirement abstraction level to
37183779
// the substituted signature of the witness.
3719-
auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind);
3720-
auto witnessFTy = origWitnessFTy;
3721-
if (!witnessSubs.empty())
3722-
witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs);
3723-
37243780
SmallVector<ManagedValue, 8> witnessParams;
37253781
AbstractionPattern witnessOrigTy(witnessInfo.LoweredType);
37263782
TranslateArguments(*this, loc,
@@ -3733,7 +3789,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
37333789

37343790
SILValue witnessFnRef = getWitnessFunctionRef(*this, witness,
37353791
origWitnessFTy,
3736-
witnessKind,
3792+
witnessKind, witnessSubs,
37373793
witnessParams, loc);
37383794

37393795
auto coroutineKind =

0 commit comments

Comments
 (0)