Skip to content

Commit 8c448a5

Browse files
authored
Merge pull request #71043 from DougGregor/distributed-actor-to-actor
Introduce a builtin and API for getting the local actor from a distributed one
2 parents 3659ea7 + a25911e commit 8c448a5

26 files changed

+503
-33
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ class ConformanceFlags {
673673

674674
HasResilientWitnessesMask = 0x01u << 16,
675675
HasGenericWitnessTableMask = 0x01u << 17,
676+
IsConformanceOfProtocolMask = 0x01u << 18,
676677

677678
NumConditionalPackDescriptorsMask = 0xFFu << 24,
678679
NumConditionalPackDescriptorsShift = 24
@@ -724,6 +725,14 @@ class ConformanceFlags {
724725
: 0));
725726
}
726727

728+
ConformanceFlags withIsConformanceOfProtocol(
729+
bool isConformanceOfProtocol) const {
730+
return ConformanceFlags((Value & ~IsConformanceOfProtocolMask)
731+
| (isConformanceOfProtocol
732+
? IsConformanceOfProtocolMask
733+
: 0));
734+
}
735+
727736
/// Retrieve the type reference kind kind.
728737
TypeReferenceKind getTypeReferenceKind() const {
729738
return TypeReferenceKind(
@@ -749,6 +758,20 @@ class ConformanceFlags {
749758
return Value & IsSynthesizedNonUniqueMask;
750759
}
751760

761+
/// Is this a conformance of a protocol to another protocol?
762+
///
763+
/// The Swift compiler can synthesize a conformance of one protocol to
764+
/// another, meaning that every type that conforms to the first protocol
765+
/// can also produce a witness table conforming to the second. Such
766+
/// conformances cannot generally be written in the surface language, but
767+
/// can be made available for specific tasks. The only such instance at the
768+
/// time of this writing is that a (local) distributed actor can conform to
769+
/// a local actor, but the witness table can only be used via a specific
770+
/// builtin to form an existential.
771+
bool isConformanceOfProtocol() const {
772+
return Value & IsConformanceOfProtocolMask;
773+
}
774+
752775
/// Retrieve the # of conditional requirements.
753776
unsigned getNumConditionalRequirements() const {
754777
return (Value & NumConditionalRequirementsMask)

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ SIMPLE_DECL_ATTR(GKInspectable, GKInspectable,
235235
OnVar | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
236236
66)
237237
DECL_ATTR(_implements, Implements,
238-
OnFunc | OnAccessor | OnVar | OnSubscript | OnTypeAlias | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
238+
OnFunc | OnAccessor | OnVar | OnSubscript | OnTypeAlias | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
239239
67)
240240
DECL_ATTR(_objcRuntimeName, ObjCRuntimeName,
241241
OnClass | UserInaccessible | NotSerialized | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,

include/swift/AST/Builtins.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,12 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(GetEnumTag, "getEnumTag", "", Special)
10831083
/// tag's payload is already initialized with the given source.
10841084
BUILTIN_MISC_OPERATION_WITH_SILGEN(InjectEnumTag, "injectEnumTag", "", Special)
10851085

1086+
/// distributedActorAsAnyActor: <DA: DistributedActor>(_: DA) -> any Actor
1087+
///
1088+
/// For a given distributed actor that is known to be local, extract an
1089+
/// `any Actor` existential that refers to the local actor.
1090+
BUILTIN_MISC_OPERATION_WITH_SILGEN(DistributedActorAsAnyActor, "distributedActorAsAnyActor", "n", Special)
1091+
10861092
#undef BUILTIN_MISC_OPERATION_WITH_SILGEN
10871093

10881094
#undef BUILTIN_MISC_OPERATION

include/swift/AST/ProtocolConformance.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ class RootProtocolConformance : public ProtocolConformance {
468468
/// Get the conformance substitution map.
469469
SubstitutionMap getSubstitutionMap() const;
470470

471+
/// Whether this conformance was synthesized automatically and can have
472+
/// multiple copies in a single program.
473+
bool isSynthesized() const;
474+
471475
/// Apply the given function object to each value witness within this
472476
/// protocol conformance.
473477
///
@@ -679,6 +683,16 @@ class NormalProtocolConformance : public RootProtocolConformance,
679683
/// modules, but in a manner that ensures that all copies are equivalent.
680684
bool isSynthesizedNonUnique() const;
681685

686+
/// Whether this conformance represents the conformance of one protocol's
687+
/// conforming types to another protocol.
688+
///
689+
/// Such conformances cannot generally be written in the surface language, but
690+
/// can be made available for specific tasks. The only such instance at the
691+
/// time of this writing is that a (local) distributed actor can conform to
692+
/// a local actor, but the witness table can only be used via a specific
693+
/// builtin to form an existential.
694+
bool isConformanceOfProtocol() const;
695+
682696
/// Whether clients from outside the module can rely on the value witnesses
683697
/// being consistent across versions of the framework.
684698
bool isResilient() const;

lib/AST/ASTMangler.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,8 +1787,9 @@ static bool conformanceHasIdentity(const RootProtocolConformance *root) {
17871787
return true;
17881788
}
17891789

1790-
// Synthesized non-unique conformances all get collapsed together at run time.
1791-
if (conformance->isSynthesizedNonUnique())
1790+
// Synthesized conformances can have multiple copies, so they don't
1791+
// provide identity.
1792+
if (conformance->isSynthesized())
17921793
return false;
17931794

17941795
// Objective-C protocol conformances are checked by the ObjC runtime.

lib/AST/Builtins.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,19 @@ static ValueDecl *getHopToActor(ASTContext &ctx, Identifier id) {
19751975
return builder.build(id);
19761976
}
19771977

1978+
static ValueDecl *getDistributedActorAsAnyActor(ASTContext &ctx, Identifier id) {
1979+
BuiltinFunctionBuilder builder(ctx);
1980+
auto *distributedActorProto = ctx.getProtocol(KnownProtocolKind::DistributedActor);
1981+
auto *actorProto = ctx.getProtocol(KnownProtocolKind::Actor);
1982+
1983+
// Create type parameters and add conformance constraints.
1984+
auto actorParam = makeGenericParam();
1985+
builder.addParameter(actorParam);
1986+
builder.addConformanceRequirement(actorParam, distributedActorProto);
1987+
builder.setResult(makeConcrete(actorProto->getDeclaredExistentialType()));
1988+
return builder.build(id);
1989+
}
1990+
19781991
static ValueDecl *getPackLength(ASTContext &ctx, Identifier id) {
19791992
BuiltinFunctionBuilder builder(ctx, /* genericParamCount */ 1,
19801993
/* anyObject */ false,
@@ -3055,6 +3068,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
30553068

30563069
case BuiltinValueKind::InjectEnumTag:
30573070
return getInjectEnumTag(Context, Id);
3071+
3072+
case BuiltinValueKind::DistributedActorAsAnyActor:
3073+
return getDistributedActorAsAnyActor(Context, Id);
30583074
}
30593075

30603076
llvm_unreachable("bad builtin value!");

lib/AST/ProtocolConformance.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ bool RootProtocolConformance::hasWitness(ValueDecl *requirement) const {
290290
ROOT_CONFORMANCE_SUBCLASS_DISPATCH(hasWitness, (requirement))
291291
}
292292

293+
bool RootProtocolConformance::isSynthesized() const {
294+
if (auto normal = dyn_cast<NormalProtocolConformance>(this))
295+
return normal->isSynthesizedNonUnique() || normal->isConformanceOfProtocol();
296+
297+
return false;
298+
}
299+
293300
bool NormalProtocolConformance::isRetroactive() const {
294301
auto module = getDeclContext()->getParentModule();
295302

@@ -321,11 +328,17 @@ bool NormalProtocolConformance::isRetroactive() const {
321328
}
322329

323330
bool NormalProtocolConformance::isSynthesizedNonUnique() const {
331+
// Check if the conformance was synthesized by the ClangImporter.
324332
if (auto *file = dyn_cast<FileUnit>(getDeclContext()->getModuleScopeContext()))
325333
return file->getKind() == FileUnitKind::ClangModule;
334+
326335
return false;
327336
}
328337

338+
bool NormalProtocolConformance::isConformanceOfProtocol() const {
339+
return getDeclContext()->getSelfProtocolDecl() != nullptr;
340+
}
341+
329342
bool NormalProtocolConformance::isResilient() const {
330343
// If the type is non-resilient or the module we're in is non-resilient, the
331344
// conformance is non-resilient.

lib/IRGen/GenDecl.cpp

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,7 @@ void IRGenerator::emitLazyDefinitions() {
14231423
assert(LazySpecializedTypeMetadataRecords.empty());
14241424
assert(LazyTypeContextDescriptors.empty());
14251425
assert(LazyOpaqueTypeDescriptors.empty());
1426+
assert(LazyExtensionDescriptors.empty());
14261427
assert(LazyFieldDescriptors.empty());
14271428
// LazyFunctionDefinitions are allowed, but they must not be generic
14281429
for (SILFunction *f : LazyFunctionDefinitions) {
@@ -1438,7 +1439,9 @@ void IRGenerator::emitLazyDefinitions() {
14381439
while (!LazyTypeMetadata.empty() ||
14391440
!LazySpecializedTypeMetadataRecords.empty() ||
14401441
!LazyTypeContextDescriptors.empty() ||
1441-
!LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() ||
1442+
!LazyOpaqueTypeDescriptors.empty() ||
1443+
!LazyExtensionDescriptors.empty() ||
1444+
!LazyFieldDescriptors.empty() ||
14421445
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
14431446
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
14441447
!LazyMetadataAccessors.empty() ||
@@ -1494,6 +1497,15 @@ void IRGenerator::emitLazyDefinitions() {
14941497
CurrentIGMPtr IGM = getGenModule(type->getDeclContext());
14951498
IGM->emitOpaqueTypeDecl(type);
14961499
}
1500+
while (!LazyExtensionDescriptors.empty()) {
1501+
ExtensionDecl *ext = LazyExtensionDescriptors.back();
1502+
LazyExtensionDescriptors.pop_back();
1503+
auto &entry = LazyExtensions.find(ext)->second;
1504+
assert(entry.IsDescriptorUsed && !entry.IsDescriptorEmitted);
1505+
entry.IsDescriptorEmitted = true;
1506+
CurrentIGMPtr IGM = getGenModule(ext->getDeclContext());
1507+
IGM->getAddrOfExtensionContextDescriptor(ext);
1508+
}
14971509
while (!LazyFieldDescriptors.empty()) {
14981510
NominalTypeDecl *type = LazyFieldDescriptors.pop_back_val();
14991511
CurrentIGMPtr IGM = getGenModule(type->getDeclContext());
@@ -1804,6 +1816,18 @@ void IRGenerator::noteUseOfOpaqueTypeDescriptor(OpaqueTypeDecl *opaque) {
18041816
}
18051817
}
18061818

1819+
void IRGenerator::noteUseOfExtensionDescriptor(ExtensionDecl *ext) {
1820+
auto insertResult = LazyExtensions.try_emplace(ext);
1821+
auto &entry = insertResult.first->second;
1822+
1823+
bool isNovelUseOfDescriptor = !entry.IsDescriptorUsed;
1824+
entry.IsDescriptorUsed = true;
1825+
1826+
if (isNovelUseOfDescriptor) {
1827+
LazyExtensionDescriptors.push_back(ext);
1828+
}
1829+
}
1830+
18071831
static std::string getDynamicReplacementSection(IRGenModule &IGM) {
18081832
std::string sectionName;
18091833
switch (IGM.TargetInfo.OutputObjectFormat) {
@@ -4065,11 +4089,11 @@ IRGenModule::getAddrOfLLVMVariableOrGOTEquivalent(LinkEntity entity) {
40654089
return indirect();
40664090
}
40674091

4068-
static TypeEntityReference
4069-
getContextDescriptorEntityReference(IRGenModule &IGM, const LinkEntity &entity){
4092+
TypeEntityReference
4093+
IRGenModule::getContextDescriptorEntityReference(const LinkEntity &entity) {
40704094
// TODO: consider using a symbolic reference (i.e. a symbol string
40714095
// to be looked up dynamically) for types defined outside the module.
4072-
auto ref = IGM.getAddrOfLLVMVariableOrGOTEquivalent(entity);
4096+
auto ref = getAddrOfLLVMVariableOrGOTEquivalent(entity);
40734097
auto kind = ref.isIndirect()
40744098
? TypeReferenceKind::IndirectTypeDescriptor
40754099
: TypeReferenceKind::DirectTypeDescriptor;
@@ -4081,15 +4105,15 @@ getTypeContextDescriptorEntityReference(IRGenModule &IGM,
40814105
NominalTypeDecl *decl) {
40824106
auto entity = LinkEntity::forNominalTypeDescriptor(decl);
40834107
IGM.IRGen.noteUseOfTypeContextDescriptor(decl, DontRequireMetadata);
4084-
return getContextDescriptorEntityReference(IGM, entity);
4108+
return IGM.getContextDescriptorEntityReference(entity);
40854109
}
40864110

40874111
static TypeEntityReference
40884112
getProtocolDescriptorEntityReference(IRGenModule &IGM, ProtocolDecl *protocol) {
40894113
assert(!protocol->hasClangNode() &&
40904114
"objc protocols don't have swift protocol descriptors");
40914115
auto entity = LinkEntity::forProtocolDescriptor(protocol);
4092-
return getContextDescriptorEntityReference(IGM, entity);
4116+
return IGM.getContextDescriptorEntityReference(entity);
40934117
}
40944118

40954119
static TypeEntityReference
@@ -4113,7 +4137,7 @@ IRGenModule::getTypeEntityReference(GenericTypeDecl *decl) {
41134137
if (auto opaque = dyn_cast<OpaqueTypeDecl>(decl)) {
41144138
auto entity = LinkEntity::forOpaqueTypeDescriptor(opaque);
41154139
IRGen.noteUseOfOpaqueTypeDescriptor(opaque);
4116-
return getContextDescriptorEntityReference(*this, entity);
4140+
return getContextDescriptorEntityReference(entity);
41174141
}
41184142

41194143
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
@@ -4360,12 +4384,28 @@ llvm::Constant *IRGenModule::emitSwiftProtocols(bool asContiguousArray) {
43604384
return nullptr;
43614385
}
43624386

4387+
/// Determine whether the given protocol conformance can be found via
4388+
/// metadata for dynamic lookup.
4389+
static bool conformanceIsVisibleViaMetadata(
4390+
RootProtocolConformance *conformance) {
4391+
auto normal = dyn_cast<NormalProtocolConformance>(conformance);
4392+
if (!normal)
4393+
return true;
4394+
4395+
// Conformances of a protocol to another protocol cannot be looked up
4396+
// dynamically.
4397+
return !normal->isConformanceOfProtocol();
4398+
}
4399+
4400+
43634401
void IRGenModule::addProtocolConformance(ConformanceDescription &&record) {
43644402

43654403
emitProtocolConformance(record);
43664404

4367-
// Add this conformance to the conformance list.
4368-
ProtocolConformances.push_back(std::move(record));
4405+
if (conformanceIsVisibleViaMetadata(record.conformance)) {
4406+
// Add this conformance to the conformance list.
4407+
ProtocolConformances.push_back(std::move(record));
4408+
}
43694409
}
43704410

43714411
void IRGenModule::addAccessibleFunction(SILFunction *func) {

lib/IRGen/GenMeta.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,8 +2719,11 @@ IRGenModule::getAddrOfSharedContextDescriptor(LinkEntity entity,
27192719
// at runtime.
27202720
auto mangledName = entity.mangleAsString();
27212721
if (auto otherDefinition = Module.getGlobalVariable(mangledName)) {
2722-
GlobalVars.insert({entity, otherDefinition});
2723-
return otherDefinition;
2722+
if (!otherDefinition->isDeclaration() ||
2723+
!entity.isAlwaysSharedLinkage()) {
2724+
GlobalVars.insert({entity, otherDefinition});
2725+
return otherDefinition;
2726+
}
27242727
}
27252728

27262729
// Otherwise, emit the descriptor.

lib/IRGen/GenProto.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,24 @@ llvm::Function *FragileWitnessTableBuilder::buildInstantiationFunction() {
19331933
return fn;
19341934
}
19351935

1936+
/// Produce a reference for the conforming entity of a protocol conformance
1937+
/// descriptor, which is usually the conforming concrete type.
1938+
static TypeEntityReference getConformingEntityReference(
1939+
IRGenModule &IGM, const RootProtocolConformance *conformance) {
1940+
// This is a conformance of a protocol to another protocol. Module it as
1941+
// an extension.
1942+
if (isa<NormalProtocolConformance>(conformance) &&
1943+
cast<NormalProtocolConformance>(conformance)->isConformanceOfProtocol()) {
1944+
auto ext = cast<ExtensionDecl>(conformance->getDeclContext());
1945+
auto linkEntity = LinkEntity::forExtensionDescriptor(ext);
1946+
IGM.IRGen.noteUseOfExtensionDescriptor(ext);
1947+
return IGM.getContextDescriptorEntityReference(linkEntity);
1948+
}
1949+
1950+
return IGM.getTypeEntityReference(
1951+
conformance->getDeclContext()->getSelfNominalTypeDecl());
1952+
}
1953+
19361954
namespace {
19371955
/// Builds a protocol conformance descriptor.
19381956
class ProtocolConformanceDescriptorBuilder {
@@ -1984,8 +2002,7 @@ namespace {
19842002
void addConformingType() {
19852003
// Add a relative reference to the type, with the type reference
19862004
// kind stored in the flags.
1987-
auto ref = IGM.getTypeEntityReference(
1988-
Conformance->getDeclContext()->getSelfNominalTypeDecl());
2005+
auto ref = getConformingEntityReference(IGM, Conformance);
19892006
B.addRelativeAddress(ref.getValue());
19902007
Flags = Flags.withTypeReferenceKind(ref.getKind());
19912008
}
@@ -2000,6 +2017,7 @@ namespace {
20002017
if (auto conf = dyn_cast<NormalProtocolConformance>(Conformance)) {
20012018
Flags = Flags.withIsRetroactive(conf->isRetroactive());
20022019
Flags = Flags.withIsSynthesizedNonUnique(conf->isSynthesizedNonUnique());
2020+
Flags = Flags.withIsConformanceOfProtocol(conf->isConformanceOfProtocol());
20032021
} else {
20042022
Flags = Flags.withIsRetroactive(false)
20052023
.withIsSynthesizedNonUnique(false);
@@ -2027,9 +2045,22 @@ namespace {
20272045
if (!normal)
20282046
return;
20292047

2048+
llvm::Optional<Requirement> scratchRequirement;
20302049
auto condReqs = normal->getConditionalRequirements();
2031-
if (condReqs.empty())
2032-
return;
2050+
if (condReqs.empty()) {
2051+
// For a protocol P that conforms to another protocol, introduce a
2052+
// conditional requirement for that P's Self: P. This aligns with
2053+
// SILWitnessTable::enumerateWitnessTableConditionalConformances().
2054+
if (auto selfProto = normal->getDeclContext()->getSelfProtocolDecl()) {
2055+
auto selfType = selfProto->getSelfInterfaceType()->getCanonicalType();
2056+
scratchRequirement.emplace(RequirementKind::Conformance, selfType,
2057+
selfProto->getDeclaredInterfaceType());
2058+
condReqs = *scratchRequirement;
2059+
}
2060+
2061+
if (condReqs.empty())
2062+
return;
2063+
}
20332064

20342065
Flags = Flags.withNumConditionalRequirements(condReqs.size());
20352066

0 commit comments

Comments
 (0)