Skip to content

Commit 8112f68

Browse files
authored
Merge pull request swiftlang#20629 from rjmccall/error-self-conformance
Allow Error to conform to itself
2 parents ccfd405 + 49ba9c5 commit 8112f68

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
@@ -3985,6 +3985,9 @@ class ProtocolDecl final : public NominalTypeDecl {
39853985
->existentialConformsToSelfSlow();
39863986
}
39873987

3988+
/// Does this protocol require a self-conformance witness table?
3989+
bool requiresSelfConformanceWitnessTable() const;
3990+
39883991
/// Find direct Self references within the given requirement.
39893992
///
39903993
/// \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
@@ -3910,15 +3910,22 @@ bool ProtocolDecl::requiresClassSlow() {
39103910
return Bits.ProtocolDecl.RequiresClass;
39113911
}
39123912

3913+
bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {
3914+
return isSpecificProtocol(KnownProtocolKind::Error);
3915+
}
3916+
39133917
bool ProtocolDecl::existentialConformsToSelfSlow() {
39143918
// Assume for now that the existential conforms to itself; this
39153919
// prevents circularity issues.
39163920
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
39173921
Bits.ProtocolDecl.ExistentialConformsToSelf = true;
39183922

3923+
// If it's not @objc, it conforms to itself only if it has a
3924+
// self-conformance witness table.
39193925
if (!isObjC()) {
3920-
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
3921-
return false;
3926+
bool hasSelfConformance = requiresSelfConformanceWitnessTable();
3927+
Bits.ProtocolDecl.ExistentialConformsToSelf = hasSelfConformance;
3928+
return hasSelfConformance;
39223929
}
39233930

39243931
// 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
@@ -1432,9 +1432,12 @@ DeclContext::getLocalConformances(
14321432
if (!nominal)
14331433
return result;
14341434

1435-
// Protocols don't have conformances.
1436-
if (isa<ProtocolDecl>(nominal))
1435+
// Protocols only have self-conformances.
1436+
if (auto protocol = dyn_cast<ProtocolDecl>(nominal)) {
1437+
if (protocol->requiresSelfConformanceWitnessTable())
1438+
return { protocol->getASTContext().getSelfConformance(protocol) };
14371439
return { };
1440+
}
14381441

14391442
// Update to record all potential conformances.
14401443
nominal->prepareConformanceTable();

lib/IRGen/GenProto.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,16 +2876,23 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
28762876
// If we don't have concrete conformance information, the type must be
28772877
// an archetype and the conformance must be via one of the protocol
28782878
// requirements of the archetype. Look at what's locally bound.
2879+
ProtocolConformance *concreteConformance;
28792880
if (conformance.isAbstract()) {
2880-
auto archetype = cast<ArchetypeType>(srcType);
2881-
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
2882-
}
2881+
if (auto archetype = dyn_cast<ArchetypeType>(srcType))
2882+
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
2883+
2884+
// Otherwise, this must be a self-conformance.
2885+
assert(proto->requiresSelfConformanceWitnessTable());
2886+
assert(cast<ProtocolType>(srcType)->getDecl() == proto);
2887+
concreteConformance = IGF.IGM.Context.getSelfConformance(proto);
28832888

28842889
// All other source types should be concrete enough that we have
28852890
// conformance info for them. However, that conformance info might be
28862891
// more concrete than we're expecting.
28872892
// TODO: make a best effort to devirtualize, maybe?
2888-
auto concreteConformance = conformance.getConcrete();
2893+
} else {
2894+
concreteConformance = conformance.getConcrete();
2895+
}
28892896
assert(concreteConformance->getProtocol() == proto);
28902897

28912898
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
@@ -3574,15 +3574,23 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived,
35743574
enum class WitnessDispatchKind {
35753575
Static,
35763576
Dynamic,
3577-
Class
3577+
Class,
3578+
Witness
35783579
};
35793580

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

3585+
if (isSelfConformance) {
3586+
assert(isa<ProtocolDecl>(decl->getDeclContext()));
3587+
return WitnessDispatchKind::Witness;
3588+
}
3589+
35833590
ClassDecl *C = decl->getDeclContext()->getSelfClassDecl();
3584-
if (!C)
3591+
if (!C) {
35853592
return WitnessDispatchKind::Static;
3593+
}
35863594

35873595
// If the witness is dynamic, go through dynamic dispatch.
35883596
if (decl->isObjCDynamic()) {
@@ -3629,6 +3637,7 @@ getWitnessFunctionType(SILGenModule &SGM,
36293637
switch (witnessKind) {
36303638
case WitnessDispatchKind::Static:
36313639
case WitnessDispatchKind::Dynamic:
3640+
case WitnessDispatchKind::Witness:
36323641
return SGM.Types.getConstantInfo(witness).SILFnType;
36333642
case WitnessDispatchKind::Class:
36343643
return SGM.Types.getConstantOverrideType(witness);
@@ -3637,18 +3646,36 @@ getWitnessFunctionType(SILGenModule &SGM,
36373646
llvm_unreachable("Unhandled WitnessDispatchKind in switch.");
36383647
}
36393648

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

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

3681-
auto witnessKind = getWitnessDispatchKind(witness);
3727+
auto witnessKind = getWitnessDispatchKind(witness, isSelfConformance);
36823728
auto thunkTy = F.getLoweredFunctionType();
36833729

36843730
SmallVector<ManagedValue, 8> origParams;
@@ -3703,9 +3749,24 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
37033749
->getCanonicalType());
37043750
}
37053751

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

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

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

37333789
SILValue witnessFnRef = getWitnessFunctionRef(*this, witness,
37343790
origWitnessFTy,
3735-
witnessKind,
3791+
witnessKind, witnessSubs,
37363792
witnessParams, loc);
37373793

37383794
auto coroutineKind =

0 commit comments

Comments
 (0)