Skip to content
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
3 changes: 3 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3964,6 +3964,9 @@ class ProtocolDecl final : public NominalTypeDecl {
->existentialConformsToSelfSlow();
}

/// Does this protocol require a self-conformance witness table?
bool requiresSelfConformanceWitnessTable() const;

/// Find direct Self references within the given requirement.
///
/// \param allowCovariantParameters If true, 'Self' is assumed to be
Expand Down
11 changes: 9 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3833,15 +3833,22 @@ bool ProtocolDecl::requiresClassSlow() {
return Bits.ProtocolDecl.RequiresClass;
}

bool ProtocolDecl::requiresSelfConformanceWitnessTable() const {
return isSpecificProtocol(KnownProtocolKind::Error);
}

bool ProtocolDecl::existentialConformsToSelfSlow() {
// Assume for now that the existential conforms to itself; this
// prevents circularity issues.
Bits.ProtocolDecl.ExistentialConformsToSelfValid = true;
Bits.ProtocolDecl.ExistentialConformsToSelf = true;

// If it's not @objc, it conforms to itself only if it has a
// self-conformance witness table.
if (!isObjC()) {
Bits.ProtocolDecl.ExistentialConformsToSelf = false;
return false;
bool hasSelfConformance = requiresSelfConformanceWitnessTable();
Bits.ProtocolDecl.ExistentialConformsToSelf = hasSelfConformance;
return hasSelfConformance;
}

// Check whether this protocol conforms to itself.
Expand Down
11 changes: 10 additions & 1 deletion lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,15 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
// existential to an archetype parameter, so for now we restrict this to
// @objc protocols.
if (!layout.isObjC()) {
// There's a specific exception for protocols with self-conforming
// witness tables, but the existential has to be *exactly* that type.
// TODO: synthesize witness tables on-demand for protocol compositions
// that can satisfy the requirement.
if (protocol->requiresSelfConformanceWitnessTable() &&
type->is<ProtocolType>() &&
type->castTo<ProtocolType>()->getDecl() == protocol)
return ProtocolConformanceRef(protocol);

return None;
}

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

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

// Find the (unspecialized) conformance.
SmallVector<ProtocolConformance *, 2> conformances;
Expand Down
23 changes: 14 additions & 9 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,21 @@ ProtocolConformanceRef::subst(Type origType,

auto *proto = getRequirement();

// If the type is an existential, it must be self-conforming.
if (substType->isExistentialType()) {
auto optConformance =
proto->getModuleContext()->lookupExistentialConformance(substType, proto);
assert(optConformance && "existential type didn't self-conform");
return *optConformance;
}

// Check the conformance map.
if (auto result = conformances(origType->getCanonicalType(),
substType, proto)) {
return *result;
}

// The only remaining case is that the type is an existential that
// self-conforms.
assert(substType->isExistentialType());
auto optConformance =
proto->getModuleContext()->lookupExistentialConformance(substType, proto);
assert(optConformance && "existential type didn't self-conform");
return *optConformance;
llvm_unreachable("Invalid conformance substitution");
}

Type
Expand Down Expand Up @@ -1407,9 +1409,12 @@ DeclContext::getLocalConformances(
if (!nominal)
return result;

// Protocols don't have conformances.
if (isa<ProtocolDecl>(nominal))
// Protocols only have self-conformances.
if (auto protocol = dyn_cast<ProtocolDecl>(nominal)) {
if (protocol->requiresSelfConformanceWitnessTable())
return { protocol->getASTContext().getSelfConformance(protocol) };
return { };
}

// Update to record all potential conformances.
nominal->prepareConformanceTable();
Expand Down
15 changes: 11 additions & 4 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2846,16 +2846,23 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
// If we don't have concrete conformance information, the type must be
// an archetype and the conformance must be via one of the protocol
// requirements of the archetype. Look at what's locally bound.
ProtocolConformance *concreteConformance;
if (conformance.isAbstract()) {
auto archetype = cast<ArchetypeType>(srcType);
return emitArchetypeWitnessTableRef(IGF, archetype, proto);
}
if (auto archetype = dyn_cast<ArchetypeType>(srcType))
return emitArchetypeWitnessTableRef(IGF, archetype, proto);

// Otherwise, this must be a self-conformance.
assert(proto->requiresSelfConformanceWitnessTable());
assert(cast<ProtocolType>(srcType)->getDecl() == proto);
concreteConformance = IGF.IGM.Context.getSelfConformance(proto);

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

auto cacheKind =
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
/// Emit the default witness table for a resilient protocol.
void emitDefaultWitnessTable(ProtocolDecl *protocol);

/// Emit the self-conformance witness table for a protocol.
void emitSelfConformanceWitnessTable(ProtocolDecl *protocol);

/// Emit the lazy initializer function for a global pattern binding
/// declaration.
SILFunction *emitLazyGlobalInitializer(StringRef funcName,
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
SubstitutionMap reqtSubs,
SILDeclRef witness,
SubstitutionMap witnessSubs,
IsFreeFunctionWitness_t isFree);
IsFreeFunctionWitness_t isFree,
bool isSelfConformance);

/// Convert a block to a native function with a thunk.
ManagedValue emitBlockToFunc(SILLocation loc,
Expand Down
78 changes: 67 additions & 11 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3575,15 +3575,23 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived,
enum class WitnessDispatchKind {
Static,
Dynamic,
Class
Class,
Witness
};

static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness) {
static WitnessDispatchKind getWitnessDispatchKind(SILDeclRef witness,
bool isSelfConformance) {
auto *decl = witness.getDecl();

if (isSelfConformance) {
assert(isa<ProtocolDecl>(decl->getDeclContext()));
return WitnessDispatchKind::Witness;
}

ClassDecl *C = decl->getDeclContext()->getSelfClassDecl();
if (!C)
if (!C) {
return WitnessDispatchKind::Static;
}

// If the witness is dynamic, go through dynamic dispatch.
if (decl->isObjCDynamic()) {
Expand Down Expand Up @@ -3630,6 +3638,7 @@ getWitnessFunctionType(SILGenModule &SGM,
switch (witnessKind) {
case WitnessDispatchKind::Static:
case WitnessDispatchKind::Dynamic:
case WitnessDispatchKind::Witness:
return SGM.Types.getConstantInfo(witness).SILFnType;
case WitnessDispatchKind::Class:
return SGM.Types.getConstantOverrideType(witness);
Expand All @@ -3638,18 +3647,36 @@ getWitnessFunctionType(SILGenModule &SGM,
llvm_unreachable("Unhandled WitnessDispatchKind in switch.");
}

static std::pair<CanType, ProtocolConformanceRef>
getSelfTypeAndConformanceForWitness(SILDeclRef witness, SubstitutionMap subs) {
auto protocol = cast<ProtocolDecl>(witness.getDecl()->getDeclContext());
auto selfParam = protocol->getProtocolSelfType()->getCanonicalType();
auto type = subs.getReplacementTypes()[0];
auto conf = *subs.lookupConformance(selfParam, protocol);
return {type->getCanonicalType(), conf};
}

static SILValue
getWitnessFunctionRef(SILGenFunction &SGF,
SILDeclRef witness,
CanSILFunctionType witnessFTy,
WitnessDispatchKind witnessKind,
SubstitutionMap witnessSubs,
SmallVectorImpl<ManagedValue> &witnessParams,
SILLocation loc) {
switch (witnessKind) {
case WitnessDispatchKind::Static:
return SGF.emitGlobalFunctionRef(loc, witness);
case WitnessDispatchKind::Dynamic:
return SGF.emitDynamicMethodRef(loc, witness, witnessFTy).getValue();
case WitnessDispatchKind::Witness: {
auto typeAndConf =
getSelfTypeAndConformanceForWitness(witness, witnessSubs);
return SGF.B.createWitnessMethod(loc, typeAndConf.first,
typeAndConf.second,
witness,
SILType::getPrimitiveObjectType(witnessFTy));
}
case WitnessDispatchKind::Class: {
SILValue selfPtr = witnessParams.back().getValue();
return SGF.emitClassMethodRef(loc, selfPtr, witness, witnessFTy);
Expand All @@ -3659,13 +3686,32 @@ getWitnessFunctionRef(SILGenFunction &SGF,
llvm_unreachable("Unhandled WitnessDispatchKind in switch.");
}

static ManagedValue
emitOpenExistentialInSelfConformance(SILGenFunction &SGF, SILLocation loc,
SILDeclRef witness,
SubstitutionMap subs, ManagedValue value,
SILParameterInfo destParameter) {
auto typeAndConf = getSelfTypeAndConformanceForWitness(witness, subs);
auto archetype = typeAndConf.first->castTo<ArchetypeType>();
assert(archetype->isOpenedExistential());

auto openedTy = destParameter.getSILStorageType();
auto state = SGF.emitOpenExistential(loc, value, archetype, openedTy,
destParameter.isIndirectMutating()
? AccessKind::ReadWrite
: AccessKind::Read);

return state.Value;
}

void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
CanAnyFunctionType reqtSubstTy,
SILDeclRef requirement,
SubstitutionMap reqtSubs,
SILDeclRef witness,
SubstitutionMap witnessSubs,
IsFreeFunctionWitness_t isFree) {
IsFreeFunctionWitness_t isFree,
bool isSelfConformance) {
// FIXME: Disable checks that the protocol witness carries debug info.
// Should we carry debug info for witnesses?
F.setBare(IsBare);
Expand All @@ -3679,7 +3725,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,
FullExpr scope(Cleanups, cleanupLoc);
FormalEvaluationScope formalEvalScope(*this);

auto witnessKind = getWitnessDispatchKind(witness);
auto witnessKind = getWitnessDispatchKind(witness, isSelfConformance);
auto thunkTy = F.getLoweredFunctionType();

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

// Get the lowered type of the witness.
auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind);
auto witnessFTy = origWitnessFTy;
if (!witnessSubs.empty())
witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs);

auto reqtSubstParams = reqtSubstTy.getParams();
auto witnessSubstParams = witnessSubstTy.getParams();

// For a self-conformance, open the self parameter.
if (isSelfConformance) {
assert(!isFree && "shouldn't have a free witness for a self-conformance");
origParams.back() =
emitOpenExistentialInSelfConformance(*this, loc, witness, witnessSubs,
origParams.back(),
witnessFTy->getSelfParameter());
}

// For a free function witness, discard the 'self' parameter of the
// requirement.
if (isFree) {
Expand All @@ -3716,11 +3777,6 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,

// Translate the argument values from the requirement abstraction level to
// the substituted signature of the witness.
auto origWitnessFTy = getWitnessFunctionType(SGM, witness, witnessKind);
auto witnessFTy = origWitnessFTy;
if (!witnessSubs.empty())
witnessFTy = origWitnessFTy->substGenericArgs(SGM.M, witnessSubs);

SmallVector<ManagedValue, 8> witnessParams;
AbstractionPattern witnessOrigTy(witnessInfo.LoweredType);
TranslateArguments(*this, loc,
Expand All @@ -3733,7 +3789,7 @@ void SILGenFunction::emitProtocolWitness(AbstractionPattern reqtOrigTy,

SILValue witnessFnRef = getWitnessFunctionRef(*this, witness,
origWitnessFTy,
witnessKind,
witnessKind, witnessSubs,
witnessParams, loc);

auto coroutineKind =
Expand Down
Loading