Skip to content

Commit 85fcd69

Browse files
authored
[Concurrency] Implement detecting isIsolatingCurrentContext user impls (#79946)
* [Concurrency] Detect non-default impls of isIsolatingCurrentContext * [Concurrency] No need for trailing info about isIsolating... in conformance * Apply changes from review
1 parent 1c78d0c commit 85fcd69

13 files changed

+396
-153
lines changed

include/swift/ABI/Metadata.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,6 +2930,14 @@ struct TargetProtocolConformanceDescriptor final
29302930
return Demangle::makeSymbolicMangledNameStringRef(this->template getTrailingObjects<TargetGlobalActorReference<Runtime>>()->type);
29312931
}
29322932

2933+
/// True if this is a conformance to 'SerialExecutor' which has a non-default
2934+
/// (i.e. not the stdlib's default implementation) witness. This means that
2935+
/// the developer has implemented this method explicitly and we should prefer
2936+
/// calling it.
2937+
bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const {
2938+
return Flags.hasNonDefaultSerialExecutorIsIsolatingCurrentContext();
2939+
}
2940+
29332941
/// Retrieve the protocol conformance of the global actor type to the
29342942
/// GlobalActor protocol.
29352943
const TargetProtocolConformanceDescriptor<Runtime> *

include/swift/ABI/MetadataValues.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,14 @@ class ConformanceFlags {
748748
IsConformanceOfProtocolMask = 0x01u << 18,
749749
HasGlobalActorIsolation = 0x01u << 19,
750750

751+
// Used to detect if this is a conformance to SerialExecutor that has
752+
// an user defined implementation of 'isIsolatingCurrentContext'. This
753+
// requirement is special in the sense that if a non-default impl is present
754+
// we will avoid calling the `checkIsolated` method which would lead to a
755+
// crash. In other words, this API "soft replaces" 'checkIsolated' so we
756+
// must at runtime the presence of a non-default implementation.
757+
HasNonDefaultSerialExecutorIsIsolatingCurrentContext = 0x01u << 20,
758+
751759
NumConditionalPackDescriptorsMask = 0xFFu << 24,
752760
NumConditionalPackDescriptorsShift = 24
753761
};
@@ -813,7 +821,15 @@ class ConformanceFlags {
813821
? HasGlobalActorIsolation
814822
: 0));
815823
}
816-
824+
825+
ConformanceFlags withHasNonDefaultSerialExecutorIsIsolatingCurrentContext(
826+
bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext) const {
827+
return ConformanceFlags((Value & ~HasNonDefaultSerialExecutorIsIsolatingCurrentContext)
828+
| (hasNonDefaultSerialExecutorIsIsolatingCurrentContext
829+
? HasNonDefaultSerialExecutorIsIsolatingCurrentContext
830+
: 0));
831+
}
832+
817833
/// Retrieve the type reference kind kind.
818834
TypeReferenceKind getTypeReferenceKind() const {
819835
return TypeReferenceKind(
@@ -858,6 +874,10 @@ class ConformanceFlags {
858874
return Value & HasGlobalActorIsolation;
859875
}
860876

877+
bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const {
878+
return Value & HasNonDefaultSerialExecutorIsIsolatingCurrentContext;
879+
}
880+
861881
/// Retrieve the # of conditional requirements.
862882
unsigned getNumConditionalRequirements() const {
863883
return (Value & NumConditionalRequirementsMask)

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ IDENTIFIER(makeIterator)
118118
IDENTIFIER(makeAsyncIterator)
119119
IDENTIFIER(nestedContainer)
120120
IDENTIFIER(isEmpty)
121+
IDENTIFIER(isIsolatingCurrentContext)
121122
IDENTIFIER(isolation)
122123
IDENTIFIER(Iterator)
123124
IDENTIFIER(AsyncIterator)

include/swift/AST/Module.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,9 @@ class ModuleDecl
11581158
/// \returns true if this module is the "swift" standard library module.
11591159
bool isStdlibModule() const;
11601160

1161+
/// \returns true if this module is the "_Concurrency" standard library module.
1162+
bool isConcurrencyModule() const;
1163+
11611164
/// \returns true if this module has standard substitutions for mangling.
11621165
bool hasStandardSubstitutions() const;
11631166

include/swift/Runtime/Bincompat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ bool useLegacySwiftObjCHashing();
8080
/// - if not available, it will invoke the the *crashing* 'checkIsolated'
8181
///
8282
/// This can be overridden by using `SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=1`
83-
/// or `SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=crash|nocrash|swift6|swift6.2`
83+
/// or `SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=crash|nocrash|swift6|isIsolatingCurrentContext`
8484
SWIFT_RUNTIME_STDLIB_SPI
8585
bool swift_bincompat_useLegacyNonCrashingExecutorChecks();
8686

include/swift/Runtime/Concurrency.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,7 @@ bool swift_task_isCurrentExecutor(SerialExecutorRef executor);
10231023
/// this could be a pointer to a different enum instance if we need it to be.
10241024
enum swift_task_is_current_executor_flag : uint64_t {
10251025
/// We aren't passing any flags.
1026+
/// Effectively this is a backwards compatible mode.
10261027
None = 0x0,
10271028

10281029
/// This is not used today, but is just future ABI reservation.
@@ -1047,9 +1048,15 @@ enum swift_task_is_current_executor_flag : uint64_t {
10471048
/// The routine should assert on failure.
10481049
Assert = 0x8,
10491050

1051+
/// The routine MUST NOT assert on failure.
1052+
/// Even at the cost of not calling 'checkIsolated' if it is available.
1053+
MustNotAssert = 0x10,
1054+
10501055
/// The routine should use 'isIsolatingCurrentContext' function on the
10511056
/// 'expected' executor instead of `checkIsolated`.
1052-
HasIsIsolatingCurrentContext = 0x10,
1057+
///
1058+
/// This is a variant of `MustNotAssert`
1059+
UseIsIsolatingCurrentContext = 0x20,
10531060
};
10541061

10551062
SWIFT_EXPORT_FROM(swift_Concurrency)

lib/AST/Module.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,10 @@ bool ModuleDecl::isStdlibModule() const {
20312031
return !getParent() && getName() == getASTContext().StdlibModuleName;
20322032
}
20332033

2034+
bool ModuleDecl::isConcurrencyModule() const {
2035+
return !getParent() && getName() == getASTContext().Id_Concurrency;
2036+
}
2037+
20342038
bool ModuleDecl::hasStandardSubstitutions() const {
20352039
return !getParent() &&
20362040
(getName() == getASTContext().StdlibModuleName ||
@@ -4285,11 +4289,6 @@ struct SwiftSettingsWalker : ASTWalker {
42854289

42864290
} // namespace
42874291

4288-
static bool isConcurrencyModule(DeclContext *dc) {
4289-
auto *m = dc->getParentModule();
4290-
return !m->getParent() && m->getName() == m->getASTContext().Id_Concurrency;
4291-
}
4292-
42934292
bool SwiftSettingsWalker::isSwiftSettingsMacroExpr(
42944293
MacroExpansionExpr *macroExpr) {
42954294
// First make sure we actually have a macro with the name SwiftSettings.
@@ -4406,7 +4405,8 @@ SwiftSettingsWalker::patternMatchDefaultIsolationMainActor(CallExpr *callExpr) {
44064405
if (!nomDecl)
44074406
return CanType();
44084407
auto *nomDeclDC = nomDecl->getDeclContext();
4409-
if (!nomDeclDC->isModuleScopeContext() || !isConcurrencyModule(nomDeclDC))
4408+
auto *nomDeclModule = nomDecl->getParentModule();
4409+
if (!nomDeclDC->isModuleScopeContext() || !nomDeclModule->isConcurrencyModule())
44104410
return CanType();
44114411

44124412
return nomDecl->getDeclaredType()->getCanonicalType();

lib/IRGen/GenProto.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,6 +2238,7 @@ namespace {
22382238
Flags = Flags.withIsSynthesizedNonUnique(conf->isSynthesizedNonUnique());
22392239
Flags = Flags.withIsConformanceOfProtocol(conf->isConformanceOfProtocol());
22402240
Flags = Flags.withHasGlobalActorIsolation(isolation.isGlobalActor());
2241+
Flags = withSerialExecutorCheckingModeFlags(Flags, conf);
22412242
} else {
22422243
Flags = Flags.withIsRetroactive(false)
22432244
.withIsSynthesizedNonUnique(false);
@@ -2459,6 +2460,32 @@ namespace {
24592460
rootGlobalActorConformance));
24602461
B.addRelativeAddress(globalActorConformanceDescriptor);
24612462
}
2463+
2464+
static ConformanceFlags
2465+
withSerialExecutorCheckingModeFlags(ConformanceFlags Flags, const NormalProtocolConformance *conf) {
2466+
ProtocolDecl *proto = conf->getProtocol();
2467+
auto &C = proto->getASTContext();
2468+
2469+
ConformanceFlags UpdatedFlags = Flags;
2470+
if (proto->isSpecificProtocol(swift::KnownProtocolKind::SerialExecutor)) {
2471+
conf->forEachValueWitness([&](const ValueDecl *req,
2472+
Witness witness) {
2473+
bool nameMatch = witness.getDecl()->getBaseIdentifier() == C.Id_isIsolatingCurrentContext;
2474+
if (nameMatch) {
2475+
if (DeclContext *NominalOrExtension = witness.getDecl()->getDeclContext()) {
2476+
// If the witness is NOT the default implementation in the _Concurrency library,
2477+
// we should record that this is an user provided implementation and we should call it.
2478+
bool hasNonDefaultIsIsolatingCurrentContext =
2479+
!NominalOrExtension->getParentModule()->isConcurrencyModule();
2480+
UpdatedFlags = UpdatedFlags.withHasNonDefaultSerialExecutorIsIsolatingCurrentContext(
2481+
hasNonDefaultIsIsolatingCurrentContext);
2482+
}
2483+
}
2484+
});
2485+
}
2486+
2487+
return UpdatedFlags;
2488+
}
24622489
};
24632490
}
24642491

0 commit comments

Comments
 (0)